奇怪的 mktime() 行为

Posted

技术标签:

【中文标题】奇怪的 mktime() 行为【英文标题】:Strange mktime() behaviour 【发布时间】:2019-09-18 01:54:42 【问题描述】:

为什么 mktime() 将 31/03/2019 02:00 转换为 01:00 而 tm.tm_isdst = 1 和 CET 时区?

我认为这是一个无效的组合。

#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;

输出:

% 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

【问题讨论】:

为什么 mktime() 将 31/03/2019 02:00 转换为 01:00 而 tm.tm_isdst = 1 和 CET 时区? 你为什么认为这是无效的? @AndrewHenle 因为 03:00 转换为 03:00 宋歌,第一个转换为03:0003:00 在哪里?在所有情况下,小时都以 2 开头。tm-&gt;tm_hour = 2;. 相关信息Most of Europe sets the clocks forward one hour on Sunday, March 31, 2019 @chux tm->tm_hour = 3 的输出:pastebin.com/hVubjtMp 我在您的链接上看不到我的问题的答案 【参考方案1】:
    2019-03-31 02:01:00 CET (is_dst = 0, UTC+1) 不存在,但相当于 2019-03-31 01:01:00 UTC(纪元后 1553994060 秒) ,相当于 2019-03-31 03:01:00 CEST (is_dst = 1, UTC+2)。 2019-03-31 02:01:00 CEST (is_dst = 1, UTC+2) 不存在,但相当于 2019-03-31 00:01:00 UTC(纪元后 1553990460 秒) ,相当于 2019-03-31 01:01:00 CET (is_dst = 0, UTC+1)。 (注:1553994060 - 1553990460 = 3600,相差1小时。) 对于is_dst = -1,实现尝试确定夏令时是否有效,但由于 2019-03-31 02:01:00 对于 CET 和 CEST 同样无效,它只是选择其中一个。在这种情况下,它假设输入是标准时间 2019-03-31 02:01:00 CET (is_dst = 0, UTC+1),它不存在,但相当于 2019-03-31 01: UTC 01:00(纪元后 1553994060 秒),相当于 2019-03-31 03:01:00 CEST(is_dst = 1, UTC+2)。

【讨论】:

【参考方案2】:

由于 2019 年 3 月 31 日 02:00 是 DST 更改 CET / CEST 地区的时间,该日期的第 2 小时的时间不用于该地区的计时。记录了mktime 以规范化输入struct tm 中的数据,这在这里发挥了作用。

具体来说,

如果时间戳2019-03-31 02:01:00按照标准时间解释,即01:01:00后一小时的时间,则对应2019-03- 31 03:01:00 CEST。这就是struct tm 的标准化方式,如您的程序所示。返回值对应那个时间。

如果时间戳2019-03-31 02:01:00按照夏令时解释,即03:01:00前一小时的时间,则对应2019-03 -31 01:01:00 CET。这就是struct tm 的标准化方式,如您的程序所示。返回值与该时间相对应,并且表示比其他情况下的返回值早 3600 秒的时间。

如果您指定未知是否将给定时间解释为好像 DST 有效,那么mktime 必须猜测。对于大多数时间和日期,它会根据 DST 在该日期和时间是否有效进行选择,但这不会在这里产生任何答案。在我看来,它实际上在这里解释了标准时间而不是夏令时,这似乎是合理的,因为这似乎更有可能是提供该输入的程序的期望。

【讨论】:

以上是关于奇怪的 mktime() 行为的主要内容,如果未能解决你的问题,请参考以下文章

日期时间的奇怪行为

时间函数date(),mktime(),strtotime()

在linux中使用mktime函数的时间差异

Atmega328P 中的奇怪延迟行为

《linux 内核全然剖析》 mktime.c

计算时间差