带有负秒的奇怪mktime逻辑

时间:2022-04-03 20:19:11

I've been using mktime/localtime for time management, including some heavy arithmetic on dates/times.

我一直在使用mktime/localtime进行时间管理,包括一些关于日期/时间的复杂算法。

I noticed something very weird when providing to mktime a struct tm that contains negative values.

当为mktime提供包含负值的struct tm时,我注意到一些非常奇怪的事情。

Take the code below. There was a DST change in LA on Nov 3rd, 2013. If I specify time in tm as 2013-11-04 midnight and subtract 24 hours, I get the same value as 2013-11-03 midnight. It is 25 hours difference UTC-wise, which is fine, as with isdst=-1 one could say we're looking at 'wallclock-time'. Same if I subtract 1440 minutes (24*60). But, if I subtract 86400 (24*60*60) seconds, I get 2013-11-03 1am. That is 24 hours difference UTC-wise. Here's the output from the code below:

下面的代码。2013年11月3日洛杉矶发生了DST变化。如果我在tm中指定时间为2013-11-04午夜,减去24小时,我得到的值与2013-11-03午夜相同。这是25个小时的差值UTC-wise,这很好,就像isdst=-1一样,我们可以看到“wallc锁-time”。如果我减去1440分钟(24*60)也是一样的。但是,如果我减去86400(24*60*60)秒,我得到2013-11-03早上1点。这是24小时的时差。下面是代码的输出:

2013-11-03 00:00:00 (gmtoff=0, isdst=-1) -> 2013-11-03 00:00:00 (gmtoff=-25200, isdst=1) -> 1383462000
2013-12--27 00:00:00 (gmtoff=0, isdst=-1) -> 2013-11-03 00:00:00 (gmtoff=-25200, isdst=1) -> 1383462000
2013-11-04 -24:00:00 (gmtoff=0, isdst=-1) -> 2013-11-03 00:00:00 (gmtoff=-25200, isdst=1) -> 1383462000
2013-11-04 00:-1440:00 (gmtoff=0, isdst=-1) -> 2013-11-03 00:00:00 (gmtoff=-25200, isdst=1) -> 1383462000
2013-11-04 00:00:-86400 (gmtoff=0, isdst=-1) -> 2013-11-03 01:00:00 (gmtoff=-25200, isdst=1) -> 1383465600

For me it doesn't make sense - why are seconds treated differently than minutes, hours and days? I looked at man and the C standard but couldn't find anything.

对我来说,这是没有意义的——为什么几秒钟的处理时间和几分钟、几小时和几天不同?我看了看man和C标准,但什么也没找到。

This behavior breaks some of my assumptions and complicates things. Does someone know a good alternative to mktime/localtime (I tested boost, ICU and tzcode, all too slow for what I need).

这种行为打破了我的一些假设,使事情变得复杂。有人知道mktime/localtime的一个好的替代方案吗(我测试了boost、ICU和tzcode,对于我所需要的来说都太慢了)。

Thanks in advance for any thoughts :)

提前谢谢你的想法。

#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* printtm(struct tm tm)
{
  static char buf[100];
  sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d (gmtoff=%ld, isdst=%d)",
    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
    tm.tm_hour, tm.tm_min, tm.tm_sec,
    tm.tm_gmtoff, tm.tm_isdst);
  return buf;
}

void test(int y, int m, int d, int hh, int mm, int ss, int isdst)
{
  struct tm tm;
  memset(&tm, 0, sizeof(tm));
  tm.tm_year = y - 1900;
  tm.tm_mon = m - 1;
  tm.tm_mday = d;
  tm.tm_hour = hh;
  tm.tm_min = mm;
  tm.tm_sec = ss;
  tm.tm_isdst = isdst;
  printf("%s -> ", printtm(tm));
  time_t t = mktime(&tm);
  printf("%s -> %ld\n", printtm(tm), t);
}


int main()
{
  setenv("TZ", ":America/Los_Angeles", 1);
  tzset();

  test(2013,11,03, 0,0,0, -1);
  test(2013,12,-27, 0,0,0, -1);
  test(2013,11,04, -24,0,0, -1);
  test(2013,11,04, 0,-1440,0, -1);
  test(2013,11,04, 0,0,-86400, -1);

  return 0;
}

1 个解决方案

#1


3  

Using mktime with out-of-range values in the struct tm and tm_isdst==-1 is problematic and under-specified. Personally, I think the way your system is behaving here is wrong, but the standard isn't clear on how it's supposed to behave, and thus any such usage is non-portable at best. To do arithmetic on struct tm, you should make sure tm_isdst is set to either 0 or 1 beforehand, so that the result of unambiguous.

在struct tm和tm_isdst==-1中使用带有超出范围值的mktime是有问题的,而且是未指定的。就个人而言,我认为您的系统在这里的行为方式是错误的,但是标准并不清楚它应该如何表现,因此任何这样的用法最好都是不可移植的。要在struct tm上进行算术运算,您应该确保tm_isdst事先设置为0或1,以便不歧义的结果。

Note that one easy way to do this is to simply call mktime on the original struct tm (with tm_isdst==-1) before applying the arithmetic to determine whether daylight time is in effect (i.e. to fill in a definitive value for tm_isdst) then call it again after making your arithmetic adjustments.

注意,一种简单的方法是在初始结构tm上调用mktime(使用tm_isdst==-1),然后应用该算法来确定日光时间是否有效(例如,为tm_isdst填充一个最终值),然后在进行算术调整之后再次调用它。

#1


3  

Using mktime with out-of-range values in the struct tm and tm_isdst==-1 is problematic and under-specified. Personally, I think the way your system is behaving here is wrong, but the standard isn't clear on how it's supposed to behave, and thus any such usage is non-portable at best. To do arithmetic on struct tm, you should make sure tm_isdst is set to either 0 or 1 beforehand, so that the result of unambiguous.

在struct tm和tm_isdst==-1中使用带有超出范围值的mktime是有问题的,而且是未指定的。就个人而言,我认为您的系统在这里的行为方式是错误的,但是标准并不清楚它应该如何表现,因此任何这样的用法最好都是不可移植的。要在struct tm上进行算术运算,您应该确保tm_isdst事先设置为0或1,以便不歧义的结果。

Note that one easy way to do this is to simply call mktime on the original struct tm (with tm_isdst==-1) before applying the arithmetic to determine whether daylight time is in effect (i.e. to fill in a definitive value for tm_isdst) then call it again after making your arithmetic adjustments.

注意,一种简单的方法是在初始结构tm上调用mktime(使用tm_isdst==-1),然后应用该算法来确定日光时间是否有效(例如,为tm_isdst填充一个最终值),然后在进行算术调整之后再次调用它。