bug or feature? mktime sets tzname to historical zone names
I discovered, in the course of working on my thread-safe time zone changes, an interesting side-effect of mktime(). The localsub() function sets tzname[tm->tm_isdst] to the correct zone abbreviation, as of the time it calculates. This is probably correct for localsub() as called by localtime(). However, since time2sub() calls localsub() (as the funcp passed it by mktime()), mktime() *also* sets tzname, to the time zone in effect at the time of mktime()'s argument. Additionally, because of time2sub()'s binary search, the other element of tzname (tzname[!tm->tm_isdst]) is often also set, to something random. (This will usually be the other time zone abbreviation in effect at that time, but it'd probably be pretty straightforward to construct some weird cases where it's a zone abbreviation used long in the past or future.) Is this: * a bug, which should be fixed in tzcode? * a feature, and thus I need to make sure this behavior is maintained by any changes I make to the guts of tzcode when adding the thread-safe functions? * undefined behavior, since mktime() behaves "as though it called tzset()", but tzset() is poorly defined in the presence of changing time zone abbreviations? The attached C program illustrates the behavior in question. (Europe/Riga uses EET/EEST now, but used MSK/MSD in 1981.) #include "tztimeext.h" #include <stdio.h> #include <stdlib.h> #include <string.h> struct tm then = { 0, /* int tm_sec; */ 0, /* int tm_min; */ 0, /* int tm_hour; */ 1, /* int tm_mday; */ 8, /* int tm_mon; */ 81, /* int tm_year; */ }; int main() { time_t now, then_t; struct tm* a_tm; putenv("TZ=Europe/Riga"); now = time(NULL); a_tm = localtime(&now); printf("now = %ld -- %s", (long) now, asctime(a_tm)); printf("tzname = {%s, %s}\n", tzname[0], tzname[1]); then_t = mktime(&then); printf("then = %ld -- %s", (long) then_t, asctime(&then)); printf("tzname = {%s, %s}\n", tzname[0], tzname[1]); exit(0); } -- Jonathan Lennox lennox@cs.columbia.edu
From: Jonathan Lennox <lennox@cs.columbia.edu> Date: Sat, 16 Jun 2001 20:29:35 -0400 (EDT)
mktime() *also* sets tzname, to the time zone in effect at the time of mktime()'s argument.
If the TZ environment variable has the POSIX-style value "EET-2EEST,M3.5.0,M10.5.0/3", then POSIX requires that mktime must behave as though it called tzset, and tzset must set tzname[0] to "EET" and tzname[1] to "EEST". From what you write, the Olson code conforms to POSIX in this respect. If TZ uses an Olson extension like "Europe/Riga", then POSIX does not define the behavior. However, some programs rely on tzname[!!tm->tm_isdst] being set for the requested time, and (from what you write) the Olson code is compatible with these programs. tzname[!tm->tm_isdist] is a bit trickier. Few programs rely on this value, and the few that I've seen have all been broken, one way or another. In the Olson model, every instant of time can (in principle) have a separate time zone abbreviation, time zone abbreviations do not come in standard-time/daylight-saving-time pairs, and it's possible for a DST abbreviation to have no corresponding standard-time abbreviation and vice-versa. Hence in the Olson model, programs should not rely on tzname[!tm->tm_isdst] having useful information after functions like mktime and localtime are invoked.
Is this: * a bug, which should be fixed in tzcode? * a feature, and thus I need to make sure this behavior is maintained by any changes I make to the guts of tzcode when adding the thread-safe functions? * undefined behavior, since mktime() behaves "as though it called tzset()", but tzset() is poorly defined in the presence of changing time zone abbreviations?
It's defined behavior for POSIX-style TZ settings. For non-POSIX-style settings, I would say that only the one member of tzname is defined; the other member is not. But this should probably be documented somewhere....
Date: Sun, 17 Jun 2001 22:20:56 -0700 (PDT) From: Paul Eggert <eggert@twinsun.com> Message-ID: <200106180520.f5I5KuO24701@green-office.twinsun.com> | For non-POSIX-style settings, I would say that only the one member of | tzname is defined; the other member is not. But this should probably | be documented somewhere.... Actually, tzname[] is a crick of shot, and should be undocumented everywhere... kre
participants (3)
-
Jonathan Lennox -
Paul Eggert -
Robert Elz