[tz] question about mktime_tzname()

Kees Dekker Kees.Dekker at infor.com
Wed Jan 11 20:55:18 UTC 2017

Many thanks for reply.

I will check your reply with a colleague tomorrow. He is knowing more details than I. I only don't understand why system libraries (including Linux, that also uses AFAIK tz code for the system libraries) think that DST is not into effect. Based on my debugging tests, the initialization order resulted in the assumption that DST was on (sp->defaulttype). If EST is at the northern part of the world, I would expect that DST starts in april and end in 1970-10-25. But I have to do more checking tomorrow (I'm typing this email from home). I've some feeling, but I've to do more research, that the detection of DST is incorrect at the boundary (and probably theoretical cases).

The reason why we use this old dates (it is not very old, otherwise, I'm also very old :-)) is to check our own software. Our own software provides interfaces to programmer's, and checking the boundaries of the UTC ranges is part of a test. I agree that these tests are probably beyond any practical case. But our customers may have used it in the past, and so we have to remain compatible. That's why we have tests that prove the past behavior, even if I update the tz sour code and/or data/rules in our product.

I've much more failing tests in our test-suite, that worked well before (with older tz code + most probably own modification). My goal is just to eliminate our own modifications and just the IANA code as is. Any other changes that are probably useful for IANA will be sent to this mailing list one I have isolated them (and still find them useful, probably a faster algorithm for the binary search).

Van: Arthur David Olson [arthurdavidolson at gmail.com]
Verzonden: woensdag 11 januari 2017 20:39
Aan: Kees Dekker
CC: tz at iana.org
Onderwerp: Re: [tz] question about mktime_tzname()

So it looks as if this has nothing to do with non-normalized values and, actually, nothing to do with mktime but everything with to do with how the TZ variable is being handled. At the bottom of this message there's output from "zdump."
It shows that, for TZ=EST5EDT,M4.1.0/02:00,M10.5.0/02:00, the first detectable transition is on 1970-10-25, from EDT to EST; that means that daylight saving is applied to all instants before 1970-10-25, leading to the reported results.

The small fix is to ensure that a transition to DST shows up in the table that's generated at run time when the given TZ environment variable is used.

The philosophical question: for such environment variables, in what year should application of daylight saving start (or should it apply to all years, no matter how far in the past)?


Script started on Wed, Jan 11, 2017  2:24:59 PM
$ zdump -v EST5EDT,M4.1.0/02:00,M10.5.0/02:00 | head
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  -9223372036854775808 = NULL
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  -9223372036854689408 = NULL
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Oct 25 05:59:59 1970 UT = Sun Oct 25 01:59:59 1970 EDT isdst=1 gmtoff=-14400
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Oct 25 06:00:00 1970 UT = Sun Oct 25 01:00:00 1970 EST isdst=0 gmtoff=-18000
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Apr  4 06:59:59 1971 UT = Sun Apr  4 01:59:59 1971 EST isdst=0 gmtoff=-18000
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Apr  4 07:00:00 1971 UT = Sun Apr  4 03:00:00 1971 EDT isdst=1 gmtoff=-14400
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Oct 31 05:59:59 1971 UT = Sun Oct 31 01:59:59 1971 EDT isdst=1 gmtoff=-14400
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Oct 31 06:00:00 1971 UT = Sun Oct 31 01:00:00 1971 EST isdst=0 gmtoff=-18000
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Apr  2 06:59:59 1972 UT = Sun Apr  2 01:59:59 1972 EST isdst=0 gmtoff=-18000
EST5EDT,M4.1.0/02:00,M10.5.0/02:00  Sun Apr  2 07:00:00 1972 UT = Sun Apr  2 03:00:00 1972 EDT isdst=1 gmtoff=-14400
$ exit

Script done on Wed, Jan 11, 2017  2:25:31 PM

On Wed, Jan 11, 2017 at 11:27 AM, Kees Dekker <Kees.Dekker at infor.com<mailto:Kees.Dekker at infor.com>> wrote:

Can anyone help me?

In short, I set TZ to EST5EDT,M4.1.0/02:00,M10.5.0/02:00, which means daylight saving for EST (=GMT-5 or GMT-4 if DST is active), with switching DST at first Sunday of April and move to standard time at the last Sunday of October.

The input to mktime is (See also attached source code) 1-1-1970, 19:00h (tm_min=1140 = 1140/60 = 19h) local EST time and tm_isdst set to -1. Because of this value, the tz code should figure out that 1-1-1970 is not in DST so the time difference with GMT/UTC is 5 hours. I would exect then mktime() to return 0. This is true if I use the system functions, but mktime() returns -3600 when using the tz library.

The output of running the test case with system libraries is:
enter mktime with: 1970/Jan/00 00:1140:00, week day: -858993460, year day: -858993460, dst: -1
the return value of mktime is: 0
normalized times: 1969/Dec/31 19:00:00, week day: Wed, year day: 364, dst: 0

The output of the test case with the tz lib is:
enter mktime with: 1970/Jan/00 00:1140:00, week day: -858993460, year day: -858993460, dst: -1
the return value of mktime is: -3600
normalized times: 1969/Dec/31 19:00:00, week day: Wed, year day: 364, dst: 1

Please correct me if I’m wrong. Is this a bug in the tz code? Please note the differences.

From: Kees Dekker
Sent: Thursday, January 05, 2017 15:03
To: 'tz at iana.org<mailto:tz at iana.org>' <tz at iana.org<mailto:tz at iana.org>>
Subject: question about mktime_tzname()


I’m running into a potential bug. Please also see the attached C-source file for a reproduction.

Reproduction steps:

1.       Build tz code, in such way that a libtz.a exists

2.       Compile attached source with (e.g. on Linux): gcc -g -o tztestcase2016j tztestcase.c -L <tzlibdir> -ltz

3.       Checking the same test case source code with a system library (instead of IANA libtz.a) can be achieved by omitting -L and -l flags in previous command (see #2).

It does not make much sense which version of tzcode is used, I tried 2013i and 2016j and both show the same behavior.

The mktime() code returns -3600, while I expect 0. Because the TZ zone is explicitly specified, EST5EDT is GMT-5. So the provided time stamp (31-Jan-1969, 19:00) is exactly the same as UTC value 0 (1-Jan-1970, 00:00 GMT). Both Linux and Windows with system libraries return 0 in this specific case (I used these for reference).

The remainder of this email is based on the tzcode2016j source code (e.g. for used line numbers).

The provided TZ value is parsed in tzparse() and results in sp->ttis[0] set as DST value, and sp->ttis[1] as standard time value. This is at least contrary to in the else part of the if (*name == ',' || *name == ';') block (there DST is saved in sp->ttis[1]). Just grep on init_ttinfo() calls in localtime.c. However, in localsub(), around line 1428, the i becomes sp->defaulttype (which is zero), i.e. points to the DST value of sp->ttis[], which is assigned to ttisp. As result, because t is below sp->ats[0], DST takes into effect, which is incorrect, as everything after the last Sunday in October and before the first Sunday in April (see attachment, TZ=”EST5EDT,M4.1.0/02:00,M10.5.0/02:00”) is assumed to be standard time.

I’m not sure whether I draw the right conclusion, but because the incorrect assumption of using DST, mktime() returns -3600 (on hour off) instead of the expected 0. Is this conclusion correct?

I’m not sure how to fix it. When changing the call to init_ttinfo() and effectively swapping DST and standard time (also for symmetry with the else part) in sp-ttis[] (0 to 1 and 1 to 0) + keeping sp->defaulttype = 0 solved this problem, but introduces other conversion errors around DSTchanges (I don’t have yet an reproduction, but can make one if needed). The sp->default type is used (in localsub ()) when if (sp->timecnt == 0 || t < sp->ats[0]). The other approach is to assign sp->defaulttype = 1 when the init_ttinfo() calls are not swapped. Anyhow, there is a tight relationship between the value of sp->defaulttype and the order in which DST/standard times are written in sp->ttis[].

If the previous idea is incorrect (and I guess it is), additional code may be needed in time2sub() if yourtm.tm_isdst == -1. In that case, the provided data in yourtm should be checked whether DST is (or is not) into effect. The found mytm struct should match. Otherwise a DST correction is needed (one-hour shift). The current code mistakenly uses DST, and that is why (if I understand all well) mktime() now returns -3600.

If I was not able to explain the issue correctly, please let me know. I’m not sure to understand what’s going wrong now, but may be, the provided attachment will help in giving better understanding. Because the provided date is near 1970, quite few steps are needed to see what’s happing in localtime.c.

Kees Dekker

PS. This email has been sent assuming that attachments will not get lost. Otherwise, just copy paste the code below and save to e.g. tztestcase.c.

#ifdef _WIN32

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
                int ret = 0;
                const char *months[] = { "Jan", "Feb", "Mar", "April", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec" };
                const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Tur", "Fri", "Sat" };

                /* DST switch in April in first Sunday and October, last Sunday */

                /* January, 1970, invalid input for tm_mday (0 instead 1..31) */
                /* non-normalized input for minutes: 1140/60 = 19, so 19:00 EST5EDT time, which is 5 hours before GMT, and should result in t == 0 below */
                /* let system figure out whether DST is effect */
                struct tm tmp = { 0, 1140, 0, 0, 0, 70, -858993460, -858993460, -1};

                printf("enter mktime with: %04d/%s/%02d %02d:%02d:%02d, week day: %d, year day: %d, dst: %d\n",
                                                                tmp.tm_year + 1900, months[tmp.tm_mon], tmp.tm_mday,
                                                                tmp.tm_hour, tmp.tm_min, tmp.tm_sec,
                                                                tmp.tm_wday, tmp.tm_yday, tmp.tm_isdst);

                time_t t = mktime(&tmp);

                /* expect: t == 0, DST is off, time is exactly at the boundary of lowest possible UTC value */
                printf("the return value of mktime is: %lld\n", (long long)t);
                printf("normalized times: %04d/%s/%02d %02d:%02d:%02d, week day: %s, year day: %d, dst: %d\n",
                                                                tmp.tm_year + 1900, months[tmp.tm_mon], tmp.tm_mday,
                                                                tmp.tm_hour, tmp.tm_min, tmp.tm_sec,
                                                                days[tmp.tm_wday], tmp.tm_yday, tmp.tm_isdst);

                if (t >= 0) {
                                printf("tz=%s, %s\n", tzname[0], tzname[1]);

                return 0;

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mm.icann.org/pipermail/tz/attachments/20170111/8bd62584/attachment-0001.html>

More information about the tz mailing list