I have been playing with mktime, and I noticed a weird, and inconsistent, behavior.
I provide it with a date which is not during DST (daylight saving time), but with tm_isdst is set to 1, what mktime usually does is change tm_isdst to 0, and adjust the time accordingly, shifting by 1 hour.
However, in the time period roughly in years 1928..1933 (couldn't find a different range), the behavior is different. The tm_isdst field is set to 0, but the time does not change. This results in weirdness when performing time computations etc.
I have a tiny test program which for a given input date prints: original struct tm, struct tm after calling mktime on it, mktime result, and struct tm which is the result of calling localtime on the mktime result (should represent the same moment in time as the original one).
The output is:
2013-01-01 12:00:00 (off=0, dst=1) -> 2013-01-01 11:00:00 (off=-28800, dst=0) -> 1357066800 -> 2013-01-01 11:00:00 (off=-28800, dst=0)
1927-01-01 12:00:00 (off=0, dst=1) -> 1927-01-01 11:00:00 (off=-28800, dst=0) -> -1356930000 -> 1927-01-01 11:00:00 (off=-28800, dst=0)
1929-01-01 12:00:00 (off=0, dst=1) -> 1929-01-01 12:00:00 (off=-28800, dst=0) -> -1293768000 -> 1929-01-01 12:00:00 (off=-28800, dst=0)
1932-01-01 12:00:00 (off=0, dst=1) -> 1932-01-01 12:00:00 (off=-28800, dst=0) -> -1199160000 -> 1932-01-01 12:00:00 (off=-28800, dst=0)
1934-01-01 12:00:00 (off=0, dst=1) -> 1934-01-01 11:00:00 (off=-28800, dst=0) -> -1136005200 -> 1934-01-01 11:00:00 (off=-28800, dst=0)
See that for years 2013, 1927, 1934, the hour is changed and dst set to 0. But in years 1929 and 1932 the hour is not changed, but dst is.
What's super weird is that in tzinfo there is nothing about that time range - zdump for Los Angeles shows that the closest changes are in 1919 and 1942.
This is on CentOS, kernel 2.6.32-358.11.1.el6.x86_64, glibc-2.12-1.107.el6.x86_64.
Further investigation seems to work as expected (consistently) on MacOSX. So this looks like a bug in mktime() to me, but maybe I'm missing something.
Test test program is below and also available here
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* printtm(struct tm tm)
{
static char buf[100];
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d (off=%ld, dst=%d)",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_gmtoff, tm.tm_isdst);
return buf;
}
void test(int y, int m, int d, int hh, int mm, int ss, int isdst)
{
// Prepare tm structs
struct tm tm, tm2;
memset(&tm, 0, sizeof(tm));
memset(&tm2, 0, sizeof(tm));
tm.tm_year = y - 1900;
tm.tm_mon = m - 1;
tm.tm_mday = d;
tm.tm_hour = hh;
tm.tm_min = mm;
tm.tm_sec = ss;
tm.tm_isdst = isdst;
// Convert tm -> t -> tm and print
printf("%s -> ", printtm(tm));
time_t t = mktime(&tm);
printf("%s -> ", printtm(tm));
printf("%12ld -> ", t);
localtime_r(&t, &tm2);
printf("%s\n", printtm(tm));
}
int main()
{
setenv("TZ", ":America/Los_Angeles", 1);
tzset();
test(2013,07,01, 12,0,0, 1);
test(2013,01,01, 12,0,0, 1);
test(1927,01,01, 12,0,0, 1);
test(1929,01,01, 12,0,0, 1);
test(1932,01,01, 12,0,0, 1);
test(1934,01,01, 12,0,0, 1);
return 0;
}