关于mktime和DST的另一个问题
Linux,Ubuntu,时区设置为欧洲/柏林,即当前时间为CEST:
>date
Mon Aug 22 16:08:10 CEST 2016
>date --utc
Mon Aug 22 14:08:14 UTC 2016
到目前为止一切都还好。
现在我尝试运行以下代码:
#include <stdio.h>
#include <time.h>
int main()
{
struct tm tm = {0};
int secs;
tm.tm_sec = 0;
tm.tm_min = 0;
tm.tm_hour = 12;
tm.tm_mon = 9 - 1;
tm.tm_mday = 30;
tm.tm_year = 2016 - 1900;
tm.tm_isdst = 0;
secs = mktime(&tm);
printf("%i\n", secs);
tm.tm_isdst = 1;
secs = mktime(&tm);
printf("%i\n", secs);
tm.tm_isdst = -1;
secs = mktime(&tm);
printf("%i\n", secs);
return 0;
}
并获取
1475233200
1475233200
1475233200
这三种情况都是错误的(1小时偏移):
>date -d @1475233200
Fri Sep 30 13:00:00 CEST 2016
所以我现在有点困惑,我的时区有点破碎吗?为什么完全忽略tm_isdst标志?
编辑:@Nominal Animal有答案:mktime修改tm_hour!我想知道它在哪里记录?!
#include <stdio.h>
#include <time.h>
void reset(struct tm* tm){
(*tm) = (const struct tm){0};
tm->tm_sec = 0;
tm->tm_min = 0;
tm->tm_hour = 12;
tm->tm_mon = 9 - 1;
tm->tm_mday = 30;
tm->tm_year = 2016 - 1900;
}
int main()
{
struct tm tm;
int secs;
reset(&tm);
tm.tm_isdst = 0;
secs = mktime(&tm);
printf("%i\n", secs);
reset(&tm);
tm.tm_isdst = 1;
secs = mktime(&tm);
printf("%i\n", secs);
reset(&tm);
tm.tm_isdst = -1;
secs = mktime(&tm);
printf("%i\n", secs);
return 0;
}
给出
1475233200
1475229600
1475229600
答案 0 :(得分:1)
成功完成后,相应地设置结构的
tm_wday
和tm_yday
组件的值,并设置其他组件以表示指定的日历 时间,......C11dr§7.27.2.32
调用mktime(&tm)
时,tm
的原始值不受范围限制。
由于第一个mktime(&tm)
电话,当然tm.tm_isdst
和tm.tm_hour
已调整为1和11.因此OP的代码为tm.tm_isdst = 1;
和tm.tm_isdst = -1;
不影响时间戳。
最好设置所有要调查的字段。
struct tm tm0 = {0};
struct tm tm;
int secs;
tm0.tm_sec = 0;
tm0.tm_min = 0;
tm0.tm_hour = 12;
tm0.tm_mon = 9 - 1;
tm0.tm_mday = 30;
tm0.tm_year = 2016 - 1900;
tm = tm0;
tm.tm_isdst = 0;
secs = mktime(&tm);
printf("%i\n", (int) secs);
tm = tm0;
tm.tm_isdst = 1;
secs = mktime(&tm);
printf("%i\n", (int) secs);
tm = tm0;
tm.tm_isdst = -1;
secs = mktime(&tm);
printf("%i\n", (int) secs);
答案 1 :(得分:1)
我想我现在可以看到人们如何发现这种混乱。将mktime()
视为签名
time_t mktime_actual(struct tm *dst, const struct tm *src);
根据(标准化)time_t
计算*src
结果,并且标准化字段以及当时是否应用夏令时,会保存到*dst
。
只是C语言开发人员历来选择只使用一个指针,将src
和dst
结合起来。不过,上述逻辑仍然存在。
请参阅`man mktime手册页,尤其是此部分:
mktime()函数转换故障时间结构, 以当地时间表示,以日历时间表示。该 函数忽略调用者在tm_wday和中提供的值 tm_yday字段。 tm_isdst字段中指定的值通知 mktime()是否夏令时(DST)生效 在tm结构中提供的时间:正值表示DST 有效;零表示DST无效;和负值 意味着mktime()应该(使用时区信息和系统 数据库,以试图确定DST是否有效 指定的时间。
mktime()函数将tm结构的字段修改为 如下:tm_wday和tm_yday设置为从中确定的值 其他领域的内容;如果结构成员在他们之外 有效间隔,它们将被标准化(例如,40 十月改为11月9日);设置tm_isdst(不管是什么 它的初始值为正值或分别为0 指示DST是否在指定时间生效。 调用mktime()还会将外部变量tzname设置为 有关当前时区的信息。
如果指定的细分时间无法表示为日历 时间(自纪元以来的秒数),mktime()返回(time_t)-1并且确实如此 不要改变破碎时间结构的成员。
换句话说,如果你改变测试程序,比如说
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
static const char *dst(const int flag)
{
if (flag > 0)
return "(>0: is DST)";
else
if (flag < 0)
return "(<0: Unknown if DST)";
else
return "(=0: not DST)";
}
static struct tm newtm(const int year, const int month, const int day,
const int hour, const int min, const int sec,
const int isdst)
{
struct tm t = { .tm_year = year - 1900,
.tm_mon = month - 1,
.tm_mday = day,
.tm_hour = hour,
.tm_min = min,
.tm_sec = sec,
.tm_isdst = isdst };
return t;
}
int main(void)
{
struct tm tm = {0};
time_t secs;
tm = newtm(2016,9,30, 12,0,0, -1);
secs = mktime(&tm);
printf("-1: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);
tm = newtm(2016,9,30, 12,0,0, 0);
secs = mktime(&tm);
printf(" 0: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);
tm = newtm(2016,9,30, 12,0,0, 1);
secs = mktime(&tm);
printf("+1: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);
return EXIT_SUCCESS;
}
然后运行它会产生输出
-1: 2016-09-30 12:00:00 (>0: is DST) 1475226000
0: 2016-09-30 13:00:00 (>0: is DST) 1475229600
+1: 2016-09-30 12:00:00 (>0: is DST) 1475226000
换句话说,它的行为完全如上所述(在上面的引文中)。这种行为记录在C89,C99和POSIX.1中(我认为C11也是如此,但是没有检查过。)