LIBC的mktime函数为同一输入返回不同的值

时间:2014-08-19 02:36:28

标签: c++ c timezone glibc mktime

我们知道时区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, "%lld\n", mktime(&timeinfo));
    }

    return 0;
}

例如,测试输入和输出就像那样,相同的输入&#34; 1940 6 2 23 59 59&#34;和&#34; 1940 6 3 1 0 0&#34;将根据调用顺序具有不同的返回值:

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 3 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

2 个答案:

答案 0 :(得分:2)

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

然后

mktime将传入的struct tm标准化,以便字段处于适当的范围内;该过程的一部分将根据DST是否在指定时间实际生效来调整DST标志。 (参见Documentation。)如果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

Demo

答案 1 :(得分:0)

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

fscanf已知行结尾存在问题,因此检查返回值(应该是6)值尝试逐行读取fgets到缓冲区中,并且然后使用sscanf