在 Linux 下使用 gcc 在 C 中使用夏令时和 mktime
Daylight savings and mktime in C using gcc under Linux
我正在检测夏令时(夏令时)转换。
我遇到了一些我不明白的事情,希望得到一些解释。
我已将我的代码精简到几乎最低限度以显示问题。
int main(void)
{
struct tm tm1,tm2;
time_t time1, time2;
int order=0;//change this betwee 0 and 1
tm1.tm_hour=2;
if (order)
{
tm1.tm_hour=1;
}
tm1.tm_min=0;
tm1.tm_sec=0;
tm1.tm_mday=1;
tm1.tm_mon=10;
tm1.tm_year=115;
tm1.tm_isdst=-1;
time1=mktime(&tm1);
//insert here
tm2.tm_hour=1;
if (order)
{
tm2.tm_hour=2;
}
tm2.tm_min=0;
tm2.tm_sec=0;
tm2.tm_mday=1;
tm2.tm_mon=10;
tm2.tm_year=115;
tm2.tm_isdst=-1;
time2=mktime(&tm2);
printf("\n\n time stamp 1=%zu time stamp 2=%zu difference=%zi\n\n",time1 ,time2, time2-time1);
exit(0);
}
order = 0时的输出为:
time stamp 1=1446368400 time stamp 2=1446364800 difference=-3600
order = 1时的输出为:
time stamp 1=1446361200 time stamp 2=1446368400 difference=7200
(注意这里是故意设置的夏令时结束时间,2015年11月1日01:59:59后一秒,就是01:00:00。)
简单来说,hour = 2 时结构的转换取决于紧接在它之前发生的转换。一点可以(正确地)是 1446364800(标准时间)或 1446361200(DST)。如果发现之前的转换为 DST,则使用第二个选择,反之亦然。显而易见的解决方案是 mktime 设置一些变量,它在下一次使用。但是我找不到它的任何记录。 mktime 确实设置了三个(四个?)外部变量,tzname[0]、tzname[1]、timezone 和 daylight,但这些似乎并没有造成影响。 (我做了一个更复杂的测试程序版本来测试它。)
我的时区是 America/Edmonton ( MST(UTC-7)/MDT(UTC -6) )
$ gcc --version<br>
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
Kubuntu 14.04 LTS
任何见解或指示将不胜感激。
编辑:
回复评论:
1) 乔纳森——glibc 2.19 版
2) chux——在 mktime() 之后打印 tmX.tm_isdst、tmX.tm_hour 的值并没有给我任何洞察力。 (那是因为我是盲人吗?)一点钟转换显示一个小时为 1,is_dst 为 0 或 1,与时间戳所推断的完全一样。当然,两点钟的转换显示小时为 2,is_dst 为 0。
回复chux的(?)回答: 不知我是不是没有完全解释清楚。
我意识到发生了什么:有一个歧义,它必须 "guess"。我想知道的是为什么(以及如何)猜测取决于之前的转换?
第二次编辑:
为了确认Wumpus Q. Wumbley 的回答在上面代码指示的地方插入了以下代码:
tm0.tm_hour=0;
tm0.tm_min=0;
tm0.tm_sec=0;
tm0.tm_mday=1;
tm0.tm_mon=10;
tm0.tm_year=115;
tm0.tm_isdst=-1;
time0=mktime(&tm0);
(基本上是一次性转换)无论顺序如何,我现在都会有两个小时的差异,所以无论哪种方式我都会得到 DST 版本。
感谢您的解释。
问题归结为 tmX.tm_isdst
before/after mktime()
调用的值。
"A negative value causes it to attempt to determine whether Daylight Saving Time is in effect for the specified time." C11dr §7.27.2.3 2 footnote
我在每个后面加了printf("H:%d DST:%d\n", tm1 (or 2).tm_hour, tm1.tm_isdst);
:
// order 0
H:2 DST:0
H:1 DST:1
time stamp 1=1446364800 time stamp 2=1446357600 difference=-7200
// order 1
H:1 DST:1
H:2 DST:0
time stamp 1=1446357600 time stamp 2=1446364800 difference=7200
这些值对于系统所做的关于未指定的 "Y-M-D H:M:S dst=unknown" 时间戳的假设是正确的,这可能是任何一种方式。
由于 C 没有指定 在这个过渡时间内应该使用什么,假设 dst 是 true/false 是合理的并且不一定在各种平台上一致。例如,给定的系统可以使用以前的 isdst
设置,总是 0
或掷硬币。它只是没有指定。
注意:这是为什么一些法律规定“酒吧必须在午夜后 2 小时关闭而不是 2:00 A.M 的一个例子。在 DST 转换发生在 2:00 的地区/3:00 A.M.小时。午夜后 2 小时仅出现一次 - 每天。2:00 A.M。每年在那个特殊的过渡夜晚出现两次。
在目前的glibc源码中,在time/mktime.c
line 410处有一个相关的注释:
/* Invert CONVERT by probing. First assume the same offset as last
time. */
它有意在可能的情况下在连续调用中使用相同的偏移量。没有全局变量可以设置来更改它,或者检查以检测它。它保存在第 578 行的 static time_t localtime_offset;
中。
我正在检测夏令时(夏令时)转换。 我遇到了一些我不明白的事情,希望得到一些解释。
我已将我的代码精简到几乎最低限度以显示问题。
int main(void)
{
struct tm tm1,tm2;
time_t time1, time2;
int order=0;//change this betwee 0 and 1
tm1.tm_hour=2;
if (order)
{
tm1.tm_hour=1;
}
tm1.tm_min=0;
tm1.tm_sec=0;
tm1.tm_mday=1;
tm1.tm_mon=10;
tm1.tm_year=115;
tm1.tm_isdst=-1;
time1=mktime(&tm1);
//insert here
tm2.tm_hour=1;
if (order)
{
tm2.tm_hour=2;
}
tm2.tm_min=0;
tm2.tm_sec=0;
tm2.tm_mday=1;
tm2.tm_mon=10;
tm2.tm_year=115;
tm2.tm_isdst=-1;
time2=mktime(&tm2);
printf("\n\n time stamp 1=%zu time stamp 2=%zu difference=%zi\n\n",time1 ,time2, time2-time1);
exit(0);
}
order = 0时的输出为:
time stamp 1=1446368400 time stamp 2=1446364800 difference=-3600
order = 1时的输出为:
time stamp 1=1446361200 time stamp 2=1446368400 difference=7200
(注意这里是故意设置的夏令时结束时间,2015年11月1日01:59:59后一秒,就是01:00:00。)
简单来说,hour = 2 时结构的转换取决于紧接在它之前发生的转换。一点可以(正确地)是 1446364800(标准时间)或 1446361200(DST)。如果发现之前的转换为 DST,则使用第二个选择,反之亦然。显而易见的解决方案是 mktime 设置一些变量,它在下一次使用。但是我找不到它的任何记录。 mktime 确实设置了三个(四个?)外部变量,tzname[0]、tzname[1]、timezone 和 daylight,但这些似乎并没有造成影响。 (我做了一个更复杂的测试程序版本来测试它。)
我的时区是 America/Edmonton ( MST(UTC-7)/MDT(UTC -6) )
$ gcc --version<br>
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
Kubuntu 14.04 LTS
任何见解或指示将不胜感激。
编辑: 回复评论: 1) 乔纳森——glibc 2.19 版 2) chux——在 mktime() 之后打印 tmX.tm_isdst、tmX.tm_hour 的值并没有给我任何洞察力。 (那是因为我是盲人吗?)一点钟转换显示一个小时为 1,is_dst 为 0 或 1,与时间戳所推断的完全一样。当然,两点钟的转换显示小时为 2,is_dst 为 0。
回复chux的(?)回答: 不知我是不是没有完全解释清楚。 我意识到发生了什么:有一个歧义,它必须 "guess"。我想知道的是为什么(以及如何)猜测取决于之前的转换?
第二次编辑:
为了确认Wumpus Q. Wumbley 的回答在上面代码指示的地方插入了以下代码:
tm0.tm_hour=0;
tm0.tm_min=0;
tm0.tm_sec=0;
tm0.tm_mday=1;
tm0.tm_mon=10;
tm0.tm_year=115;
tm0.tm_isdst=-1;
time0=mktime(&tm0);
(基本上是一次性转换)无论顺序如何,我现在都会有两个小时的差异,所以无论哪种方式我都会得到 DST 版本。
感谢您的解释。
问题归结为 tmX.tm_isdst
before/after mktime()
调用的值。
"A negative value causes it to attempt to determine whether Daylight Saving Time is in effect for the specified time." C11dr §7.27.2.3 2 footnote
我在每个后面加了printf("H:%d DST:%d\n", tm1 (or 2).tm_hour, tm1.tm_isdst);
:
// order 0
H:2 DST:0
H:1 DST:1
time stamp 1=1446364800 time stamp 2=1446357600 difference=-7200
// order 1
H:1 DST:1
H:2 DST:0
time stamp 1=1446357600 time stamp 2=1446364800 difference=7200
这些值对于系统所做的关于未指定的 "Y-M-D H:M:S dst=unknown" 时间戳的假设是正确的,这可能是任何一种方式。
由于 C 没有指定 在这个过渡时间内应该使用什么,假设 dst 是 true/false 是合理的并且不一定在各种平台上一致。例如,给定的系统可以使用以前的 isdst
设置,总是 0
或掷硬币。它只是没有指定。
注意:这是为什么一些法律规定“酒吧必须在午夜后 2 小时关闭而不是 2:00 A.M 的一个例子。在 DST 转换发生在 2:00 的地区/3:00 A.M.小时。午夜后 2 小时仅出现一次 - 每天。2:00 A.M。每年在那个特殊的过渡夜晚出现两次。
在目前的glibc源码中,在time/mktime.c
line 410处有一个相关的注释:
/* Invert CONVERT by probing. First assume the same offset as last
time. */
它有意在可能的情况下在连续调用中使用相同的偏移量。没有全局变量可以设置来更改它,或者检查以检测它。它保存在第 578 行的 static time_t localtime_offset;
中。