我一直在玩mktime,我注意到一种奇怪的,不一致的行为。
我提供的日期不是夏令时(夏令时),但是当tm_isdst设置为1时,mktime通常会将tm_isdst更改为0,并相应地调整时间,移动1小时。 / p>
然而,在大约1928年至1933年的时间段内(找不到不同的范围),行为是不同的。 tm_isdst字段设置为0,但时间不会更改。这在执行时间计算等时会导致奇怪。
我有一个很小的测试程序,对于给定的输入日期打印:原始struct tm,在调用mktime之后的struct tm,mktime结果和struct tm,这是在mktime结果上调用localtime的结果(应该代表和原来一样的时刻。)
输出结果为:
2013-01-01 12:00:00 (off=0, dst=1) -> 2013-01-01 11:00:00 (off=-28800, dst=0) -> 1357066800 -> 2013-01-01 11:00:00 (off=-28800, dst=0)
1927-01-01 12:00:00 (off=0, dst=1) -> 1927-01-01 11:00:00 (off=-28800, dst=0) -> -1356930000 -> 1927-01-01 11:00:00 (off=-28800, dst=0)
1929-01-01 12:00:00 (off=0, dst=1) -> 1929-01-01 12:00:00 (off=-28800, dst=0) -> -1293768000 -> 1929-01-01 12:00:00 (off=-28800, dst=0)
1932-01-01 12:00:00 (off=0, dst=1) -> 1932-01-01 12:00:00 (off=-28800, dst=0) -> -1199160000 -> 1932-01-01 12:00:00 (off=-28800, dst=0)
1934-01-01 12:00:00 (off=0, dst=1) -> 1934-01-01 11:00:00 (off=-28800, dst=0) -> -1136005200 -> 1934-01-01 11:00:00 (off=-28800, dst=0)
见到2013年,1927年,1934年,小时改变,dst设为0.但在1929年和1932年,小时没有改变,但dst是。
超级奇怪的是,在tzinfo中没有任何关于该时间范围的内容 - 洛杉矶的zdump显示最接近的变化发生在1919年和1942年。
这是在CentOS上,内核2.6.32-358.11.1.el6.x86_64,glibc-2.12-1.107.el6.x86_64。
进一步调查似乎在MacOSX上按预期(一致)工作。所以这对我来说就像mktime()中的一个错误,但也许我错过了一些东西。
测试测试程序如下,也可用here
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* printtm(struct tm tm)
{
static char buf[100];
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d (off=%ld, dst=%d)",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_gmtoff, tm.tm_isdst);
return buf;
}
void test(int y, int m, int d, int hh, int mm, int ss, int isdst)
{
// Prepare tm structs
struct tm tm, tm2;
memset(&tm, 0, sizeof(tm));
memset(&tm2, 0, sizeof(tm));
tm.tm_year = y - 1900;
tm.tm_mon = m - 1;
tm.tm_mday = d;
tm.tm_hour = hh;
tm.tm_min = mm;
tm.tm_sec = ss;
tm.tm_isdst = isdst;
// Convert tm -> t -> tm and print
printf("%s -> ", printtm(tm));
time_t t = mktime(&tm);
printf("%s -> ", printtm(tm));
printf("%12ld -> ", t);
localtime_r(&t, &tm2);
printf("%s\n", printtm(tm));
}
int main()
{
setenv("TZ", ":America/Los_Angeles", 1);
tzset();
test(2013,07,01, 12,0,0, 1);
test(2013,01,01, 12,0,0, 1);
test(1927,01,01, 12,0,0, 1);
test(1929,01,01, 12,0,0, 1);
test(1932,01,01, 12,0,0, 1);
test(1934,01,01, 12,0,0, 1);
return 0;
}
答案 0 :(得分:2)
在source to glibc's mktime中你可以找到:
/* tm.tm_isdst has the wrong value. Look for a neighboring
time with the right value, and use its UTC offset.
Heuristic: probe the adjacent timestamps in both directions,
looking for the desired isdst. This should work for all real
time zone histories in the tz database. */
/* Distance between probes when looking for a DST boundary. In
tzdata2003a, the shortest period of DST is 601200 seconds
(e.g., America/Recife starting 2000-10-08 01:00), and the
shortest period of non-DST surrounded by DST is 694800
seconds (Africa/Tunis starting 1943-04-17 01:00). Use the
minimum of these two values, so we don't miss these short
periods when probing. */
int stride = 601200;
/* The longest period of DST in tzdata2003a is 536454000 seconds
(e.g., America/Jujuy starting 1946-10-01 01:00). The longest
period of non-DST is much longer, but it makes no real sense
to search for more than a year of non-DST, so use the DST
max. */
int duration_max = 536454000;
/* Search in both directions, so the maximum distance is half
the duration; add the stride to avoid off-by-1 problems. */
int delta_bound = duration_max / 2 + stride;
如果你做数学运算,你会发现delta_bound
是268828200秒,相当于大约8年半。这几乎完全是zdump日期(1919年和1942年)与神秘转换时期(1928年和1933年)之间的差异。每个人都在25小时30分钟之内休息。我没有更深入地找到那个幻数的原因。
在不理解整个事情的情况下,我认为评论基本上意味着当你处于从1919年到1942年的那个长期非DST延伸的中间部分时,该算法试图找到一个有效的dst = 1时间戳附近你提供了错误的dst = 1,并在它找到一个因为it makes no real sense
之前放弃了。在中间部分之外的年份也可能没有多大意义,但作为与Jujuy相关的调整参数的副作用表现不同。