localtime、mktime:第二次规范化和 DST 行为
localtime, mktime: second normalization and DST behaviour
取下面这段代码:
static void printTime(const struct tm* t, const time_t stamp){
printf("%d-%d-%d, %d:%d:%d (DST %s) (stamp: %zu)\n",
1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, t->tm_isdst ? "Active" : "Inactive", stamp);
}
int main(){
time_t t = 1540633936;
struct tm tStruct;
localtime_r(&t, &tStruct);
printTime(&tStruct, t);
for (unsigned i = 0; i < 14; ++i){
tStruct.tm_sec += 7200;
//tStruct.tm_hour += 2;
tStruct.tm_isdst = -1;
t = mktime(&tStruct);
localtime_r(&t, &tStruct);
printTime(&tStruct, t);
}
return 0;
}
它显示了两种增加日期的方法。 mktime 的文档告诉我:
The mktime() function modifies the fields of the tm structure as
follows: tm_wday and tm_yday are set to values determined from the
contents of the other
fields; if structure members are outside their valid interval, they will be normalized (so that, for example, 40 October is
changed into 9 November);
tm_isdst is set (regardless of its initial value) to a positive value or to 0, respectively, to indicate whether DST is or is not in
effect at the specified
time.
基于此,我希望规范化的工作方式是增加 7200 秒相当于增加两个小时。但输出不同:
tStruct.tm_sec += 7200;
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136)
2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336)
2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536)
2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736)
2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936)
2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136)
2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336)
2018-10-28, 2:52:16 (DST Inactive) (stamp: 1540691536)
2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136)
2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336)
2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536)
2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736)
2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936)
2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136)
(请注意夏令时更改后直接错误的时间跳跃)
tStruct.tm_hour += 2;
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136)
2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336)
2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536)
2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736)
2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936)
2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136)
2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336)
2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136)
2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336)
2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536)
2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736)
2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936)
2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136)
2018-10-28, 15:52:16 (DST Inactive) (stamp: 1540738336)
这是预期的行为(至少对我而言)。
所以,我的问题是:真的有错误吗?或者这种记录在案的行为在某处?
当 tm_hour 需要由 mktime 更改时,也会发生此行为。举个例子:
tStruct.tm_hour += 25;
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-28, 12:52:16 (DST Inactive) (stamp: 1540727536)
2018-10-29, 13:52:16 (DST Inactive) (stamp: 1540817536)
2018-10-30, 14:52:16 (DST Inactive) (stamp: 1540907536)
2018-10-31, 15:52:16 (DST Inactive) (stamp: 1540997536)
2018-11-1, 16:52:16 (DST Inactive) (stamp: 1541087536)
2018-11-2, 17:52:16 (DST Inactive) (stamp: 1541177536)
2018-11-3, 18:52:16 (DST Inactive) (stamp: 1541267536)
2018-11-4, 19:52:16 (DST Inactive) (stamp: 1541357536)
2018-11-5, 20:52:16 (DST Inactive) (stamp: 1541447536)
2018-11-6, 21:52:16 (DST Inactive) (stamp: 1541537536)
2018-11-7, 22:52:16 (DST Inactive) (stamp: 1541627536)
2018-11-8, 23:52:16 (DST Inactive) (stamp: 1541717536)
2018-11-10, 0:52:16 (DST Inactive) (stamp: 1541807536)
2018-11-11, 1:52:16 (DST Inactive) (stamp: 1541897536)
tStruct.tm_sec += 90000
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936)
2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936)
2018-10-29, 12:52:16 (DST Inactive) (stamp: 1540813936)
2018-10-30, 13:52:16 (DST Inactive) (stamp: 1540903936)
2018-10-31, 14:52:16 (DST Inactive) (stamp: 1540993936)
2018-11-1, 15:52:16 (DST Inactive) (stamp: 1541083936)
2018-11-2, 16:52:16 (DST Inactive) (stamp: 1541173936)
2018-11-3, 17:52:16 (DST Inactive) (stamp: 1541263936)
2018-11-4, 18:52:16 (DST Inactive) (stamp: 1541353936)
2018-11-5, 19:52:16 (DST Inactive) (stamp: 1541443936)
2018-11-6, 20:52:16 (DST Inactive) (stamp: 1541533936)
2018-11-7, 21:52:16 (DST Inactive) (stamp: 1541623936)
2018-11-8, 22:52:16 (DST Inactive) (stamp: 1541713936)
2018-11-9, 23:52:16 (DST Inactive) (stamp: 1541803936)
2018-11-11, 0:52:16 (DST Inactive) (stamp: 1541893936)
根据您的确切时区(和管辖区),在 2018 年 10 月 28 日清晨的某个时间,时钟向后移动 1 小时,因为 DST 结束。从您的示例来看,这似乎发生在您所在时区/管辖区的 3:00。
第一种情况(2018-10-28加7200秒,1:52:16),tm_sec
值超出正常范围(0-59),所以mktime
可以确定您已经增加了 2 小时,并且因为它知道那是穿越 DST 边界,所以它会相应地调整时间。这导致 2018-10-28 2:52:16,即 2018-10-28 1:52:16.
后 2 小时
对于第一种情况下的下一个增量(向 2018-10-28 添加 7200 秒,2:52:16),完全相同的事情再次发生(因为你再次穿越 DST 边界 - 你有毕竟将 tm_isdst
重置为 -1
)。这导致 2018-10-28 3:52:16,即 2018-10-28 2:52:16.
后 2 小时
第二种情况(2018-10-28加2小时,1:52:16),tm_hour
值仍在正常范围内(0-23),所以mktime
无法确定您已经添加了 2 小时,它只是将其视为当地时间。这导致 2018-10-28 3:52:16,即 2018-10-28 1:52:16.
后 3 小时
为避免此类问题:
- 除非需要,否则不要将
tm_isdst
重置为 -1
(您明白会发生什么)
- 尽可能使用 UTC 时间戳,显示时只转换为本地时间。
取下面这段代码:
static void printTime(const struct tm* t, const time_t stamp){
printf("%d-%d-%d, %d:%d:%d (DST %s) (stamp: %zu)\n",
1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, t->tm_isdst ? "Active" : "Inactive", stamp);
}
int main(){
time_t t = 1540633936;
struct tm tStruct;
localtime_r(&t, &tStruct);
printTime(&tStruct, t);
for (unsigned i = 0; i < 14; ++i){
tStruct.tm_sec += 7200;
//tStruct.tm_hour += 2;
tStruct.tm_isdst = -1;
t = mktime(&tStruct);
localtime_r(&t, &tStruct);
printTime(&tStruct, t);
}
return 0;
}
它显示了两种增加日期的方法。 mktime 的文档告诉我:
The mktime() function modifies the fields of the tm structure as follows: tm_wday and tm_yday are set to values determined from the contents of the other fields; if structure members are outside their valid interval, they will be normalized (so that, for example, 40 October is changed into 9 November); tm_isdst is set (regardless of its initial value) to a positive value or to 0, respectively, to indicate whether DST is or is not in effect at the specified time.
基于此,我希望规范化的工作方式是增加 7200 秒相当于增加两个小时。但输出不同:
tStruct.tm_sec += 7200;
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136) 2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336) 2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536) 2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736) 2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936) 2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136) 2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336) 2018-10-28, 2:52:16 (DST Inactive) (stamp: 1540691536) 2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136) 2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336) 2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536) 2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736) 2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936) 2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136)
(请注意夏令时更改后直接错误的时间跳跃)
tStruct.tm_hour += 2;
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-27, 13:52:16 (DST Active) (stamp: 1540641136) 2018-10-27, 15:52:16 (DST Active) (stamp: 1540648336) 2018-10-27, 17:52:16 (DST Active) (stamp: 1540655536) 2018-10-27, 19:52:16 (DST Active) (stamp: 1540662736) 2018-10-27, 21:52:16 (DST Active) (stamp: 1540669936) 2018-10-27, 23:52:16 (DST Active) (stamp: 1540677136) 2018-10-28, 1:52:16 (DST Active) (stamp: 1540684336) 2018-10-28, 3:52:16 (DST Inactive) (stamp: 1540695136) 2018-10-28, 5:52:16 (DST Inactive) (stamp: 1540702336) 2018-10-28, 7:52:16 (DST Inactive) (stamp: 1540709536) 2018-10-28, 9:52:16 (DST Inactive) (stamp: 1540716736) 2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936) 2018-10-28, 13:52:16 (DST Inactive) (stamp: 1540731136) 2018-10-28, 15:52:16 (DST Inactive) (stamp: 1540738336)
这是预期的行为(至少对我而言)。
所以,我的问题是:真的有错误吗?或者这种记录在案的行为在某处?
当 tm_hour 需要由 mktime 更改时,也会发生此行为。举个例子:
tStruct.tm_hour += 25;
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-28, 12:52:16 (DST Inactive) (stamp: 1540727536) 2018-10-29, 13:52:16 (DST Inactive) (stamp: 1540817536) 2018-10-30, 14:52:16 (DST Inactive) (stamp: 1540907536) 2018-10-31, 15:52:16 (DST Inactive) (stamp: 1540997536) 2018-11-1, 16:52:16 (DST Inactive) (stamp: 1541087536) 2018-11-2, 17:52:16 (DST Inactive) (stamp: 1541177536) 2018-11-3, 18:52:16 (DST Inactive) (stamp: 1541267536) 2018-11-4, 19:52:16 (DST Inactive) (stamp: 1541357536) 2018-11-5, 20:52:16 (DST Inactive) (stamp: 1541447536) 2018-11-6, 21:52:16 (DST Inactive) (stamp: 1541537536) 2018-11-7, 22:52:16 (DST Inactive) (stamp: 1541627536) 2018-11-8, 23:52:16 (DST Inactive) (stamp: 1541717536) 2018-11-10, 0:52:16 (DST Inactive) (stamp: 1541807536) 2018-11-11, 1:52:16 (DST Inactive) (stamp: 1541897536)
tStruct.tm_sec += 90000
给出:
2018-10-27, 11:52:16 (DST Active) (stamp: 1540633936) 2018-10-28, 11:52:16 (DST Inactive) (stamp: 1540723936) 2018-10-29, 12:52:16 (DST Inactive) (stamp: 1540813936) 2018-10-30, 13:52:16 (DST Inactive) (stamp: 1540903936) 2018-10-31, 14:52:16 (DST Inactive) (stamp: 1540993936) 2018-11-1, 15:52:16 (DST Inactive) (stamp: 1541083936) 2018-11-2, 16:52:16 (DST Inactive) (stamp: 1541173936) 2018-11-3, 17:52:16 (DST Inactive) (stamp: 1541263936) 2018-11-4, 18:52:16 (DST Inactive) (stamp: 1541353936) 2018-11-5, 19:52:16 (DST Inactive) (stamp: 1541443936) 2018-11-6, 20:52:16 (DST Inactive) (stamp: 1541533936) 2018-11-7, 21:52:16 (DST Inactive) (stamp: 1541623936) 2018-11-8, 22:52:16 (DST Inactive) (stamp: 1541713936) 2018-11-9, 23:52:16 (DST Inactive) (stamp: 1541803936) 2018-11-11, 0:52:16 (DST Inactive) (stamp: 1541893936)
根据您的确切时区(和管辖区),在 2018 年 10 月 28 日清晨的某个时间,时钟向后移动 1 小时,因为 DST 结束。从您的示例来看,这似乎发生在您所在时区/管辖区的 3:00。
第一种情况(2018-10-28加7200秒,1:52:16),tm_sec
值超出正常范围(0-59),所以mktime
可以确定您已经增加了 2 小时,并且因为它知道那是穿越 DST 边界,所以它会相应地调整时间。这导致 2018-10-28 2:52:16,即 2018-10-28 1:52:16.
对于第一种情况下的下一个增量(向 2018-10-28 添加 7200 秒,2:52:16),完全相同的事情再次发生(因为你再次穿越 DST 边界 - 你有毕竟将 tm_isdst
重置为 -1
)。这导致 2018-10-28 3:52:16,即 2018-10-28 2:52:16.
第二种情况(2018-10-28加2小时,1:52:16),tm_hour
值仍在正常范围内(0-23),所以mktime
无法确定您已经添加了 2 小时,它只是将其视为当地时间。这导致 2018-10-28 3:52:16,即 2018-10-28 1:52:16.
为避免此类问题:
- 除非需要,否则不要将
tm_isdst
重置为-1
(您明白会发生什么) - 尽可能使用 UTC 时间戳,显示时只转换为本地时间。