10

When you call mktime(), Feb 1 seems to come before Jan 31. Why is this? Am I doing something wrong or is this a bug in glibc?

Here's the code:

struct tm tm;
time_t tt;

memset(&tm, 0, sizeof(tm));
tm.tm_year = 2011;
tm.tm_mon = 1;
tm.tm_mday = 31;
tm.tm_hour = 11;
tm.tm_min = 41;
tm.tm_sec = 28;
tm.tm_isdst = 0;
tt = mktime(&tm);

printf("Time now %d-%d-%d %d:%d:%d (%s) = %lu\n",
    tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone, tt);


memset(&tm, 0, sizeof(tm));
tm.tm_year = 2011;
tm.tm_mon = 2;
tm.tm_mday = 1;
tm.tm_hour = 1;
tm.tm_min = 1;
tm.tm_sec = 1;
tm.tm_isdst = 0;
tt = mktime(&tm);

printf("Time now %d-%d-%d %d:%d:%d (%s) = %lu\n",
    tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone, tt);

And here's the output:

Time now 2011-2-3 11:41:28 (PST) = 61257325288
Time now 2011-2-1 1:1:1 (PST) = 61257114061

Note that the original intention was to compare two time_t's. This issue causes the first date/time to appear to be later than the second, which is obviously a bit of a problem.

This is just compiled with "gcc test.c" and run with "./a.out" on Ubuntu 9.10, gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu8), libc-2.10.1-0ubuntu15

On a 32-bit system the results are as expected - i.e. completely different to the 64 bit result!

Would anyone care to confirm/refute this result and/or give some insight into what I may be doing wrong?

Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Paul A
  • 103
  • 4

2 Answers2

12

tm_mon is zero-based, so you attempted to set February 31st, which got normalized. Here's a link to the definition of mktime().

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • 2
    Simply speaking, February 31 (which is actually March 3 or March 2) is later than March 1. – Maxim Razin Feb 02 '11 at 20:31
  • Thanks Jim - bit of a duh moment ;) Not sure how I missed that after RTFM'ing multiple times! – Paul A Feb 02 '11 at 20:58
  • @Paul - We all have those moments -- I've had more than I care to remember :-) – Jim Garrison Feb 02 '11 at 23:19
  • This has nothing to do with 32 vs 64 bit. mktime normalizes out-of-range dates so if you set the month to February (1) and the day to 31 and then call mktime() the result will be March 3rd (in a non-leap year; March 2nd in a leap year). – Jim Garrison Feb 04 '11 at 17:48
3

Why does Feb 1 come before Jan 31?

Incorrect assignments to struct tm members.


.tm_mon is zero based

.tm_mon is zero based@Jim Garrison.

// tm.tm_mon = 1;
tm.tm_mon = 1 - 1; // For January

.tm.tm_year is 1900 based

// tm.tm_year = 2011;
tm.tm_year = 2011 - 1900;

time_t print specifier

"%ld" is not certainly the matching specifier for time_t, thus incurring undefined behavior (UB). time_t might not not even be an integer type. Recommend a cast to a wide signed type or this. Note that tt = mktime(&tm) returns a -1 value on error, so useful to see -1 and not an unsigned value.

// printf("%lu\n", tt);
printf("%lld\n", (long long) tt);

.tm_isdst

mktime() operates on local time. tm.tm_isdst = 0; asserts the time stamp is standard time (reasonable for PST about January). Had this code been run in a timezone with daylight time in January (e.g. Wellington), the reported tm.tm_hour may differ from expectations. Usually best to let mktime() deduce .tm_isdst. Otherwise finding the difference between timestamps (OP's higher goal) may shift an hour unexpectedly when spanning a DST change.

// tm.tm_isdst = 0;
tm.tm_isdst = -1;  // DST information is not available, mktime will adjust.
tt = mktime(&tm)
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256