LIBC的mktime函数对于相同的输入返回不同的值

mktime function of LIBC returns different values for the same input

本文关键字:返回 输入 于相同 mktime 函数 LIBC      更新时间:2023-10-16

我们知道时区UTC+8有一些时钟变化。例如,在1928年1月1日,00:00:00时钟倒转0:05:52小时到1927年12月31日,23:54:08。此外,在1940-1941年和1986-1991年使用日光节约时间。当我用这些日期在linux下测试函数mktime时,我有不同的返回值。代码如下:

#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, "%lldn", mktime(&timeinfo));
    }
    return 0;
}

以测试输入和输出为例,相同的输入"1940 6 2 23 59 59"answers"1940 6 31 1 0 0"将根据调用顺序有不同的返回值:

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

相同输入1940 6 31 1 0 0

为什么?为什么mktime的返回值随调用顺序不同而不同?

我读了一些版本的mktime的源代码,但没有发现任何部分的代码,可能会导致这个问题。

谁能解释一下为什么会发生这种情况?非常感谢。

新增案例:

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

首先将timeinfo.tm_isdst设置为0,这要求mktime将时间视为非夏令时时间。

然后,

mktime将对传入的struct tm进行规范化,使字段处于适当的范围内;该过程的一部分将根据夏令时在指定时间是否实际生效来调整夏令时标志。(见文档。)如果DST生效,它会将该标志设置为正值,并相应地调整struct tm的其他字段。

循环的后续迭代将覆盖传递给fscanf的六个字段,但不覆盖DST字段。因此,如果循环的较早迭代导致设置了该标志,则稍后的迭代仍然会设置该标志。因此,您实际上没有将相同的时间传递给mktime,并且它返回不同的结果。

从您打印的内容来看,场景似乎是:

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

演示。

问题可能不是mktime,而是sscanf。我不能测试它,因为你的例子对我来说是有效的(好吧,我不确定它是正确的时间,但它是一致的),但很可能scanf读取的数字比它应该少,这样前一行的输入就会与当前一行混合在一起。

fscanf是已知的有行结束的问题,所以检查返回值(它应该是6)值尝试一行一行地读取fgets到缓冲区,然后使用sscanf