Songe
Songe

Reputation: 39

Strange mktime() behaviour

Why mktime() convert 31/03/2019 02:00 to 01:00 with tm.tm_isdst = 1 with CET timezone?

I think this is an invalid combination.

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

void reset(struct tm* tm){
    (*tm) = (const struct tm){0};

    tm->tm_sec = 0;
    tm->tm_min = 1;
    tm->tm_hour = 2;
    tm->tm_mon = 2;
    tm->tm_mday = 31;
    tm->tm_year = 2019 - 1900;
}

int main(int argc, char **argv)
{
    struct tm   tm;
    int secs;

    reset(&tm);
    printf("Before mktime\n");
        printf(" %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec);

    tm.tm_isdst = 0;
    secs = mktime(&tm);
                printf("After mktime tm.tm_isdst = 0;\n");
    printf(" %04d-%02d-%02d %02d:%02d:%02d TZ=%s , tm_isdst = %d, timestamp=%i\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone, tm.tm_isdst, secs);

    reset(&tm);
    tm.tm_isdst = 1;
    secs = mktime(&tm);
    printf("After mktime tm.tm_isdst = 1;\n");
    printf(" %04d-%02d-%02d %02d:%02d:%02d TZ=%s , tm_isdst = %d, timestamp=%i\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone, tm.tm_isdst, secs);

    reset(&tm);    
    tm.tm_isdst = -1;
    secs = mktime(&tm);
    printf("After mktime tm.tm_isdst = -1\n");
    printf(" %04d-%02d-%02d %02d:%02d:%02d TZ=%s , tm_isdst = %d, timestamp=%i\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone, tm.tm_isdst, secs);

    return 0;
}

Output:

% date
Sun Mar 31 06:50:42 CEST 2019
% test_mktime
Before mktime
 2019-03-31 02:01:00
After mktime tm.tm_isdst = 0;
 2019-03-31 03:01:00 TZ=CEST , tm_isdst = 1, timestamp=1553994060
After mktime tm.tm_isdst = 1;
 2019-03-31 01:01:00 TZ=CET , tm_isdst = 0, timestamp=1553990460
After mktime tm.tm_isdst = -1
 2019-03-31 03:01:00 TZ=CEST , tm_isdst = 1, timestamp=1553994060

Upvotes: 1

Views: 291

Answers (2)

John Bollinger
John Bollinger

Reputation: 180103

With 02:00, March 31, 2019 being the time for the DST change the CET / CEST region, times with hour 2 on that date are not used for timekeeping in that area. mktime is documented to normalize the data in the input struct tm, and that comes into play here.

Specifically,

  • if the timestamp 2019-03-31 02:01:00 is interpreted according to standard time, in other words, as the time one hour after 01:01:00, then it corresponds to 2019-03-31 03:01:00 CEST. That's how the struct tm is normalized, as your program shows. The return value corresponds to that time.

  • if the timestamp 2019-03-31 02:01:00 is interpreted according to daylight saving time, in other words, as the time one hour before 03:01:00, then it corresponds to 2019-03-31 01:01:00 CET. That's how the struct tm is normalized, as your program shows. The return value corresponds to that time, and represents a time exactly 3600 seconds earlier than does the value returned in the other case.

  • if you specify that it is unknown whether the given time is to be interpreted as if DST is in effect then mktime must guess. For most times and dates, it would choose based on whether DST is in effect for that date and time, but that does not yield any answer here. It seems reasonable to me that it in fact interprets standard time over daylight saving time here, because that seems more likely to be the expectation of a program that provides that input.

Upvotes: 1

Ian Abbott
Ian Abbott

Reputation: 17403

  1. 2019-03-31 02:01:00 CET (is_dst = 0, UTC+1) doesn't exist, but is equivalent to 2019-03-31 01:01:00 UTC (1553994060 seconds after the epoch), which is equivalent to 2019-03-31 03:01:00 CEST (is_dst = 1, UTC+2).
  2. 2019-03-31 02:01:00 CEST (is_dst = 1, UTC+2) doesn't exist, but is equivalent to 2019-03-31 00:01:00 UTC (1553990460 seconds after the epoch), which is equivalent to 2019-03-31 01:01:00 CET (is_dst = 0, UTC+1). (Note: 1553994060 - 1553990460 = 3600, which is a difference of 1 hour.)
  3. For is_dst = -1 the implementation tries to figure out whether daylight saving time is in effect, but since 2019-03-31 02:01:00 is equally invalid for both CET and CEST, it just picks one or the other. In this case, it assumed the input was in standard time 2019-03-31 02:01:00 CET (is_dst = 0, UTC+1), which doesn't exist, but is equivalent to 2019-03-31 01:01:00 UTC (1553994060 seconds after the epoch), which is equivalent to 2019-03-31 03:01:00 CEST (is_dst = 1, UTC+2).

Upvotes: 2

Related Questions