Ziyu Chen
Ziyu Chen

Reputation: 43

mktime function of LIBC returns different values for the same input

We know that timezone UTC+8 has some clock changes. For example, on January 1st 1928, 00:00:00 the clock was turned backward 0:05:52 hours to 31 December 1927, 23:54:08.
Besides, in the year 1940-1941 and 1986-1991 the daylight saving time was used. When I'm testing the function mktime under linux with these dates, I have different return values. The code is as follow:

#include <stdio.h>
#include <string.h>
#include <time.h>
int main(int argc, char *argv[])
{
    struct tm timeinfo;
    memset(&timeinfo, 0, sizeof(timeinfo));

    while(fscanf(stdin, "%d%d%d%d%d%d",
            &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday,
            &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec) != EOF)
    {
        timeinfo.tm_year -= 1900;
        timeinfo.tm_mon -= 1;
        fprintf(stdout, "%lld\n", mktime(&timeinfo));
    }

    return 0;
}

The test input and output for example is like that, the same input "1940 6 2 23 59 59" and "1940 6 3 1 0 0" will have different return value depending on the calling sequence:

1940 6 2 23 59 59
-933494401    
1940 6 3 1 0 0
-933490800
1940 6 3 1 0 0
-933494400
1940 6 2 23 59 59
-933498001
1940 6 2 23 59 59
-933494401

The same input 1940 6 3 1 0 0

Why is that? Why the return value of mktime is different depending on the calling sequence?

I've read some version of source code of mktime but didn't find any part of the code that might cause this problem.

Can anyone explain why is this happening? Thanks a lot.

Newly added cases:

1927 12 31 23 54 8
-1325491552
1927 12 31 23 54 7
-1325491905
1927 12 31 23 54 8
-1325491904
1928 1 1 0 0 0
-1325491200
1927 12 31 23 54 8
-1325491552

Upvotes: 0

Views: 526

Answers (2)

T.C.
T.C.

Reputation: 137315

You start with timeinfo.tm_isdst set to 0, which asks mktime to consider the time as non-DST.

mktime will then normalize the struct tm passed in so that the fields are in their proper ranges; part of that process will adjust the DST flag based on whether DST was actually in effect at the specified time. (See Documentation.) If DST is in effect, it will set that flag to a positive value and adjust the other fields of the struct tm accordingly.

Later iterations of your loop will overwrite the six fields passed to your fscanf, but not the DST field. Thus, if an earlier iteration of the loop resulted in that flag being set, a later iteration will still have the flag being set. As a result, you are not actually passing the same time to mktime, and it returns different results.

From what you are printing, the scenario seems to be:

1940 6 2 23 59 59  // tm_isdst == 0, asks mktime to consider this non-DST, and DST not in effect at this time
-933494401         // tm_isdst still 0
1940 6 3 1 0 0     // tm_isdst == 0, asks mktime to consider this non-DST, but at this time DST was in effect
-933490800         // tm_isdst now positive
1940 6 3 1 0 0     // tm_isdst > 0, asks mktime to consider this DST, and DST was actually in effect
-933494400         // tm_isdst still positive
1940 6 2 23 59 59  // tm_isdst > 0, asks mktime to consider this DST, but actually DST wasn't in effect
-933498001         // tm_isdst becomes 0
1940 6 2 23 59 59  // tm_isdst == 0, asks mktime to consider this non-DST
-933494401         // tm_isdst still 0

Demo.

Upvotes: 2

Evan Dark
Evan Dark

Reputation: 1341

The problem is probably isn't with mktime but with sscanf. I can't test it since for me your example works (well I'm not sure it's the correct time, but it's consistent) but it's very likely that scanf is reading less numbers than it should, and this way the input from the prevoius line gets mixed in with the current one.

fscanf is known to have a problem with line endings, so check the return value (it should be 6) value try reading line by line with fgets into a buffer, and then use sscanf.

Upvotes: 0

Related Questions