在 Linux 下使用 gcc 在 C 中的夏令时和 mktime

Posted

技术标签:

【中文标题】在 Linux 下使用 gcc 在 C 中的夏令时和 mktime【英文标题】:Daylight savings and mktime in C using gcc under Linux 【发布时间】:2015-11-10 14:11:00 【问题描述】:

我正在检测夏令时(夏令时)转换。 我遇到了一些我不明白的事情,希望得到一些解释。

我已将我的代码精简到几乎是最低限度以显示问题。

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]、时区和日光,但这些似乎并没有导致效果。 (我做了一个更复杂的测试程序版本来测试。)

我的时区是美国/埃德蒙顿 (MST(UTC-7)/MDT(UTC-6))

$ gcc --version<br>
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4

Kubuntu 14.04 LTS

任何见解或指示将不胜感激。

编辑: 回复 cmets: 1) 乔纳森——glibc 2.19 版 2) chux -- 在 mktime() 之后打印 tmX.tm_isdst、tmX.tm_hour 的值并没有给我任何见解。 (那是因为我是盲人吗?) one O'clock 转换显示一小时一小时和 is_dst 为 0 或 1,与时间戳推断完全一致。两点钟的转换当然显示一个小时为 2,is_dst 为 0。

回复 chux 的(?)回答:我想知道我是否还没有完全解释自己。 我意识到发生了什么:存在歧义,它必须“猜测”。我想知道的是为什么(以及如何)它的猜测取决于之前的转换?

第二次编辑:

为了确认 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 版本。

谢谢你的解释。

【问题讨论】:

观察:C 库 (glibc?) 的版本可能比 GCC 的版本更相关。然而,这是一个挑剔。有趣的问题。 建议在mktime() 之后 打印tmX.tm_isdst, tmX.tm_hour 的值,以便更深入地了解问题。 问题不是跨平台的。在 Mac OS X 10.10.5 上,我得到:time stamp 1=1446361200 time stamp 2=1446368400 difference=7200time stamp 1=1446368400 time stamp 2=1446361200 difference=-7200,这是自洽的(并且与您看到的不同)。 OTOH,我可以在旧版 Linux 上重现该问题(Linux 2.6.18-128.el5 #1 SMP Wed Dec 17 11:41:38 EST 2008 x86_64)。我得到了:time stamp 1=1446368400 time stamp 2=1446364800 difference=-3600time stamp 1=1446361200 time stamp 2=1446368400 difference=7200 “我想知道的是为什么(以及如何)它的猜测取决于之前的转换?” @Wumpus Q. Wumbley 很好地回答了这篇关于 gcc/Linux 的帖子——这是被问到的。我的回答集中在 C 规范上,该规范对此问题保持沉默,从而允许编译器之间的各种行为。最好为您感兴趣的编译器定义。然而,其他编译器的可移植解决方案不应该依赖于这种行为。建议在此帖子标签中包含[gcc] 【参考方案1】:

在当前的glibc源码中,time/mktime.c line 410有相关注释:

/* Invert CONVERT by probing.  First assume the same offset as last
   time.  */

尽可能在连续调用中使用相同的偏移量。没有全局变量可以设置来更改它,或者检查来检测它。它保存在第 578 行的 static time_t localtime_offset; 中。

【讨论】:

【参考方案2】:

问题归结为tmX.tm_isdst 的值之前/之后mktime() 调用。

“负值会导致它尝试确定夏令时是否在指定时间内有效。” C11dr §7.27.2.3 2 脚注

我在每个后面都加了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 是真/假都是合理的,并且不一定在各种平台上保持一致。例如,一个给定的系统可以使用以前的isdst 设置,总是0 或掷硬币。它只是没有指定。


注意:这是为什么某些法律规定“在凌晨 2:00/3:00 发生夏令时转换的地区,酒吧必须在午夜后 2 小时而不是凌晨 2:00 关闭。午夜后 2 小时每天只发生一次。凌晨 2:00 在那个特殊的过渡之夜发生两次,一年一次。

【讨论】:

以上是关于在 Linux 下使用 gcc 在 C 中的夏令时和 mktime的主要内容,如果未能解决你的问题,请参考以下文章

有没有啥简单的方法可以在 C/C++ 中获得 Linux 下的夏令时转换时间

linux下c语言gcc编译的时候如果不知道.c文件怎么链接的?

linux终端下如何进行C语言编译

linux下使用gcc编译运行C程序

linux下c编程怎么编译

如何在Linux环境下配置C/C++环境