在 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; 中。