【问题标题】:Weird mktime logic with negative seconds带有负秒数的奇怪 mktime 逻辑
【发布时间】:2013-12-04 22:51:56
【问题描述】:

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

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

获取下面的代码。 2013 年 11 月 3 日洛杉矶发生了 DST 变化。如果我将 tm 中的时间指定为 2013 年 11 月 4 日午夜并减去 24 小时,我得到的值与 2013 年 11 月 3 日午夜相同。 UTC 时间相差 25 小时,这很好,就像 isdst=-1 一样,可以说我们正在查看“挂钟时间”。如果我减去 1440 分钟(24*60),则相同。但是,如果我减去 86400 (24*60*60) 秒,我会得到 2013-11-03 1am。这是 24 小时的 UTC 时差。这是下面代码的输出:

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

对我来说这没有意义 - 为什么秒与分钟、小时和天的处理方式不同?我查看了 man 和 C 标准,但找不到任何东西。

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

提前感谢您的任何想法:)

#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;
}

【问题讨论】:

  • @hobbs:是的,确实如此。 C11 7.27.2.3,描述mktime函数:“结构的tm_wday和tm_yday组件的原始值被忽略,原始值被忽略其他成分不限于上述范围。” (而你刚刚删除了我正在回复的评论。)
  • 谢谢基思。首先,我没有删除任何cmets,不知道发生了什么。无论如何,我知道 wday/yday,但你的回答并没有说明为什么秒在这里的处理方式与分钟不同。
  • 一个名叫“hobbs”的人发表了评论,然后在我写回复时将其删除。我留下了我的回复,因为它似乎仍然相关。你是对的,我没有说明为什么秒数会被区别对待——这就是我发表评论而不是答案的原因。稍后我可能会仔细看看。

标签: c time libc mktime


【解决方案1】:

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

请注意,一种简单的方法是在应用算术确定夏令时是否有效(即填写一个确定的值)之前,简单地在原始的struct tm(带有tm_isdst==-1)上调用mktime tm_isdst) 然后在进行算术调整后再次调用它。

【讨论】:

  • 谢谢!你说得对,isdst=0/1 解决了它,我认为我可以使用它。但我对这种行为感到非常困惑。
  • 我完全同意这种行为令人困惑且不自然。但是该标准没有描述协调超出范围的值和确定日光时间状态如何相互作用,实际上没有办法满足每个人的需求,因为这种分解的时间值本质上是模棱两可的。顺便说一句,我的猜测是您使用的系统只是忽略了秒,因为通常以分钟为单位指定夏令时转换。
  • 啊,标准最好的一点是它们是如此一致:) 与 mktime 类似的是,当您指定一个不明确的挂钟时间时(在 1 小时的秋季重叠期间) - mktime 可以返回两种可能值,取决于它的调用历史。乐趣!感谢所有的答复! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-18
  • 2010-12-14
  • 1970-01-01
  • 1970-01-01
  • 2013-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多