* - strptime(3) will not parse the leading dash with that format
* but apart from that it works well.
*/
-static const char *const iso8601_fmt_d = "%4Y-%m-%d";
-static const char *const iso8601_fmt_t = "%T";
-static const char *const iso8601_fmt_tz = "%TZ%z";
-static const char *const iso8601_fmt_dt = "%4Y-%m-%dT%T";
-static const char *const iso8601_fmt_dtz = "%4Y-%m-%dT%TZ%z";
-
-static inline const char *
-pick_iso8601_fmt (const bool date, const bool time, const bool tz)
-{
- const char *format = NULL;
- if (date) {
- if (time) {
- if (tz) {
- format = iso8601_fmt_dtz;
+namespace iso8601 {
+
+ /*
+ * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
+ * That is assuming the year in broken down time is an int we
+ * need to reserve ten decimal places.
+ */
+// static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
+ static const size_t bufsize = 33;
+
+ enum kind {
+ d = 0,
+ t = 1,
+ tz = 2,
+ dt = 3,
+ dtz = 4,
+ ISO8601_SIZE = 5,
+ };
+
+ /*
+ * Unfortunately the glibc strptime(3) on the Intranator trips over
+ * the length specifier in field descriptors so we can’t reuse the
+ * formatters here. This problem is fixed in newer glibc. For the time
+ * being we keep two tables of formatters and choose the appropriate
+ * at runtime.
+ */
+
+ static const char *const formatter [ISO8601_SIZE] =
+ { /* [iso8601::d ] = */ "%4Y-%m-%d",
+ /* [iso8601::t ] = */ "%T",
+ /* [iso8601::tz ] = */ "%TZ%z",
+ /* [iso8601::dt ] = */ "%4Y-%m-%dT%T",
+ /* [iso8601::dtz] = */ "%4Y-%m-%dT%TZ%z",
+ };
+
+ static const char *const scanner [ISO8601_SIZE] =
+ { /* [iso8601::d ] = */ "%Y-%m-%d",
+ /* [iso8601::t ] = */ "%T",
+ /* [iso8601::tz ] = */ "%TZ%z",
+ /* [iso8601::dt ] = */ "%Y-%m-%dT%T",
+ /* [iso8601::dtz] = */ "%Y-%m-%dT%TZ%z",
+ };
+
+ static inline const char *
+ pick_fmt (const bool date, const bool time, const bool tz, const bool scan=false)
+ {
+ const char *const *table = scan ? iso8601::scanner : iso8601::formatter;
+ enum iso8601::kind format = iso8601::dtz;
+
+ if (date) {
+ if (time) {
+ if (tz) {
+ format = iso8601::dtz;
+ } else {
+ format = iso8601::dt;
+ }
} else {
- format = iso8601_fmt_dt;
+ format = iso8601::d;
}
+ } else if (time && tz) {
+ format = iso8601::tz;
} else {
- format = iso8601_fmt_d;
+ format = iso8601::t; /* default to %T */
}
- } else if (time && tz) {
- format = iso8601_fmt_tz;
- } else {
- format = iso8601_fmt_t; /* default to %T */
+
+ return table [format];
}
- return format;
-}
+} /* [iso8601] */
-namespace {
- /*
- * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
- * That is assuming the year in broken down time is an int we
- * need to reserve ten decimal places.
- */
-// static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
- static const size_t iso8601_bufsize = 33;
+namespace {
static inline int flip_tm_year (const int y)
{ return (y + 1900) * -1 - 1900; }
const bool time, const bool tz)
{
struct tm tmp;
- char buf [iso8601_bufsize] = { 0 };
+ char buf [iso8601::bufsize] = { 0 };
char *start = &buf [0];
- const char *format = pick_iso8601_fmt (date, time, tz);
+ const char *format = iso8601::pick_fmt (date, time, tz);
memcpy (&tmp, &tm, sizeof (tmp));
* The sign is *always* handled above so the formatted string her
* is always one character shorter.
* */
- const size_t n = strftime (start, iso8601_bufsize-1, format, &tmp);
+ const size_t n = strftime (start, iso8601::bufsize-1, format, &tmp);
buf [n+1] = '\0';
const bool date, const bool time, const bool tz) NOEXCEPT
{
struct tm tm;
- const char *format = pick_iso8601_fmt (date, time, tz);
+ const char *format = iso8601::pick_fmt (date, time, tz, true);
const char *start = s;
bool negyear = false;