1.4.3 and fun with strptime

Matt Nourse, March 2 2012

1.4.3 is ready for download now. There's a few new features and a fix for the subtle bug in the code below. Can you see what the bug is?

struct tm tm_buf;
memset(&tm_buf, 0, sizeof(tm_buf));
char *result = strptime(time_ptr, format_ptr, &tm_buf);
.
[error checking here]
.
time_t epoch_time = mktime(&tm_buf));

(No, it's not a mktime vs mktime_r thing...r17 uses fork() not threads for parallelism).

While you're thinking, here's the change list for r17 1.4.3:

  • Added the time.format function. time.format is to strftime what time.parse is to strptime.
  • Clarified and improved rel.assert.empty behaviour. Previously rel.assert.empty passed through the stream headers, now it doesn't pass through anything.
  • rel.tsv is now more tolerant of unsupported escape characters, and translates header names into r17-safe names.
  • Increased the maximum script token length to 1K.
  • Fixed a bug in time.parse: previously the returned time was incorrect by one hour during daylight savings time periods.

Now can you see the bug?

The strptime Linux man page says:

In principle, this function does not initialize tm but only stores the values specified. This means that tm should be initialized before the call. Details differ a bit between different Unix systems. The glibc implementation does not touch those fields which are not explicitly specified, except that it recomputes the tm_wday and tm_yday field if any of the year, month, or day elements changed.

...so I dutifully memset the tm_buf structure. But, the mktime Linux man page says:

The value specified in the tm_isdst field informs mktime() whether or not daylight saving time (DST) is in effect for the time supplied in the tm structure: a positive value means DST is in effect; zero means that DST is not in effect; and a negative value means that mktime() should (use timezone information and system databases to) attempt to determine whether DST is in effect at the specified time.

So the net effect of these two behaviours is that the parsed time is skewed by one hour only during daylight savings time and only if the time string does not supply daylight savings information.

The fault is mine, strptime and mktime behave exactly as documented and these behaviours can't be altered now. But surely this "feature" is worthy of some more discussion in the man pages?

Even the example in the strptime Linux man page only uses memset to initialise the tm structure:

memset(&tm, 0, sizeof(struct tm));
strptime("2001-11-12 18:31:01", "%Y-%m-%d %H:%M:%S", &tm);
strftime(buf, sizeof(buf), "%d %b %Y %H:%M", &tm);
puts(buf);

My fix:
memset(&tm_buf, 0, sizeof(tm_buf));
// [whining and moaning]
tm_buf.tm_isdst = -1;
char *result = strptime(time_ptr, format_ptr, &tm_buf);

And just to keep us on our toes, mktime modifies tm_isdst "regardless of its initial value". So if you call strptime a second time after the call to mktime then you'll get a different result during some times of the year. Oh the joy...

blog comments powered by Disqus