mktime problems when adjusting tm_isdst
With TZ=Europe/Paris, when I invoke mktime on the equivalent of `1996-01-01 00:00:00 tm_isdst=1', it returns the equivalent of `1995-12-31 23:09:21 tm_isdst=0'. Surely the result should be `1996-12-31 23:00:00 tm_isdst=0'. (This is tzcode95d compiled on Solaris 2.4.) The problem seems to be that mktime discovers that the requested time exists only in standard time, so it corrects it by subtracting the first known GMT offset using DST and adding the first known GMT offset not using DST. But Europe/Paris currently uses different DST rules (and a different GMT offset) than it did in 1916 and 1911, respectively. The obvious workaround for this case is to change time1() so that it uses the nearest DST and non-DST transitions just before the requested time, instead of the first possible DST and non-DST transitions. But this breaks down in other cases. For example, in Europe/Paris at 1940-06-15 00:00:00, the GMT offset is 2 hours, tm_isdst=1 and there's a 1-hour DST offset, but at the most recent non-DST period the GMT offset is 0 hours, so mktime will wrongly adjust the time by 2 hours. There's a deeper problem here. How should mktime adjust for DST in a locale that has multiple DST offsets? For example, suppose TZ=America/St_Johns and I invoke mktime on the equivalent of `1989-01-01 00:00:00 tm_isdst=1'. Newfoundland used a 2-hour DST offset in summer 1988 and a 1-hour DST offset in summer 1989, so should the requested time be adjusted by 1 hour or by 2? One possible way to address the deeper problem, suggested by Mark Brader, is to represent different DST offsets by using different tm_isdst values. For example, instead of simply being 1 when some sort of DST is in effect, tm_isdst could be the DST offset, in seconds modulo INT_MAX+1. That way, mktime can tell which DST offset was used to derive its input time, and it can adjust the time correctly when it finds that a different DST offset is needed. I think this change would make it easy to fix the Europe/Paris bug mentioned above. But it's a nontrivial change to the tz code and I worry that it might break some applications that assume that tm_isdst==1 means ``use normal DST''. Here's a test program to reproduce the bug describe above. Run it with TZ set to Europe/Paris as built from tzdata95i.tar.gz, and give it the arguments `1996-01-01 00:00:00 1'. #include <stdio.h> #include <time.h> #define TM_YEAR_BASE 1900 static void print_tm (tp) struct tm *tp; { printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d", tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, tp->tm_yday, tp->tm_wday, tp->tm_isdst); } int main (argc, argv) int argc; char **argv; { time_t t; struct tm tm; char trailer; if (argc == 4 && (sscanf (argv[1], "%d-%d-%d%c", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer) == 3) && (sscanf (argv[2], "%d:%d:%d%c", &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer) == 3) && (sscanf (argv[3], "%d%c", &tm.tm_isdst, &trailer) == 1)) { tm.tm_year -= TM_YEAR_BASE; tm.tm_mon--; t = mktime (&tm); printf ("mktime returns %ld == ", (long) t); print_tm (&tm); printf ("\n"); } else printf ("Usage:\t%s YYYY-MM-DD HH:MM:SS ISDST # Test given time.\n", argv[0]); return 0; }
participants (1)
-
Paul Eggert