【问题标题】:tzset and daylight global variable interpretation in time.htime.h 中的 tzset 和日光全局变量解释
【发布时间】:2015-11-30 06:42:49
【问题描述】:

在日光全局变量的 time.h 标头中,它说: “如果夏令时规则适用,则此变量具有非零值。非零值并不一定意味着夏令时现在有效;它仅表示夏令时有时有效。”

现在我注意到在 Solaris 11.2 和 Linux 中,“daylight”变量都设置为 1,即使我的时区根本不使用夏令时(澳大利亚/布里斯班)。

示例代码证实了这一点,如果我运行 tzset 并输出我们得到的全局变量: 日光 = 1 tz[0] = [AEST] tz[1] = [AEDT] 时区 = [-36000]

但据我了解,日光应设置为 0,因为我的区域在一年中的任何时候都没有夏令时。

我还注意到 struct tm 在设置为当前时间时返回 tm_isdst = 0,这是正确的。

那么为什么将日光变量设置为 1?不应该设置为0吗?还是我误解了这个?

代码是:

#include <stdio.h>
#include <time.h>
void main()
{
  time_t t;
  struct tm     *tms = { 0 };
  tzset();
  time(&t);
  tms = localtime(&t);
  printf("date and time : %s",ctime(&t));
  printf("daylight = %d tz[0] = [%s] tz[1] = [%s] timezone = [%ld]\n", daylight, tzname[0], tzname[1], timezone);
  printf("tm_isdst = %d\n",tms->tm_isdst);
}

输出是:

date and time : Mon Nov 30 16:41:01 2015
daylight = 1 tz[0] = [AEST] tz[1] = [AEDT] timezone = [-36000]
tm_isdst = 0

【问题讨论】:

    标签: c timezone dst


    【解决方案1】:

    关于 C 标准 tm_isdst 成员。

    如果夏令时有效,tm_isdst 的值为正,如果夏令时无效,则为零,如果信息不可用,则为负。 C11dr §7.27.1 4

    这与 *nix 关于 *nix 全局变量 daylight 的规范略有不同。
    daylight 不是标准 C 的一部分。


    gnu.org 报告

    变量:int 日光
    如果适用夏令时规则,则此变量具有非零值。非零值并不一定意味着夏令时现在有效;这意味着只有夏令时有时有效。


    tm_isdst 指的是struct tm 时间戳。这仅表示DST 在该时间戳内有效。

    daylight != 0 暗示有时在时区的时间戳中使用 DST。

    由于澳大利亚/布里斯班曾在 (@Jon Skeet) 至 1972 年之前观察到 DST,因此使用 daylight == 1 是合理的,因为 daylight 意味着 DST 在该时区的某些时间段内有效(可能自 1970 年以来)。

    OP 的“......即使我的时区根本不使用夏令时”是不正确的。


    以下代码显示,自 1970 年以来,“澳大利亚/布里斯班”中使用 DST(至少 timezone DB 认为如此)已有几年。

    #include<time.h>
    #include<stdlib.h>
    #include<sys/time.h>
    
    int main(void) {
      setenv("TZ", "Australia/Brisbane", 1);
      tzset();
      time_t now;
      time(&now);
      struct tm tm;
      int isdst = 42; // See Hitchhiker's_Guide_to_the_Galaxy
      time_t t;
      for (t = 0; t < now; t += 3600) {
        tm = *localtime(&t);
        if (tm.tm_isdst != isdst) {
          printf("dst:%d %s", tm.tm_isdst, ctime(&t));
          isdst = tm.tm_isdst;
        }
      }
      printf("dst:%d %s", tm.tm_isdst, ctime(&t));
      return 0;
    }
    

    输出

    dst:0 Thu Jan  1 10:00:00 1970
    dst:1 Sun Oct 31 03:00:00 1971
    dst:0 Sun Feb 27 02:00:00 1972
    dst:1 Sun Oct 29 03:00:00 1989
    dst:0 Sun Mar  4 02:00:00 1990
    dst:1 Sun Oct 28 03:00:00 1990
    dst:0 Sun Mar  3 02:00:00 1991
    dst:1 Sun Oct 27 03:00:00 1991
    dst:0 Sun Mar  1 02:00:00 1992
    dst:0 Tue Dec  1 16:00:00 2015
    

    【讨论】:

    • 谢谢。如果您是对的,那么诸如 man tzset 之类的文档在说明日光变量“如果一年中的某个时间适用夏令时,则非零”时具有误导性和不正确性。谁能解决这个问题?还有其他关于它的确切信息吗?
    • @Andrew Gray 对这个答案进行了重大修改,以帮助区分daylighttm_isdst,因为我的原始答案似乎使问题复杂化。
    • 感谢您的澄清。我确实理解了您对差异的解释,但我想知道为什么这么多文档错误地说如果日光= 1 则该年在该时区使用夏令时,例如在 tzset 手册页中。这似乎很模棱两可。但是您的代码输出实际上非常有帮助,谢谢!
    • @Andrew Gray 我没有看到也没有得出结论“如果日光=1,那么那个的时区正在使用夏令时”。也许您正在阅读手册页中不存在的内容?
    • ... OTOH 使用daylight 表示是否曾经将 DST 应用于该区域,这有点更有意义。 IMO,C 时间功能简单不足,并且在没有重大检修的情况下超出了重大改进。坦率地说,我怀疑@Andrew Gray 你目前的工作超出了我目前在这方面的专业知识。
    【解决方案2】:

    澳大利亚/布里斯班目前不使用夏令时,但过去使用过;查看australasia 文件,您会看到它观察到夏令时的几年。

    我对@9​​87654324@ 的解释是,它表明该时区是否曾经遵守(或将遵守当前规则)夏令时。换句话说,如果这是 1,则在执行日期/时间处理时需要小心,而如果它是 0,则可以假设一个恒定的 UTC 偏移量。

    (我不清楚一个从未观察到 DST,但 随着时间推移其标准 UTC 偏移量的时区是否会将 daylight 设置为 1。我猜它设置它是绝对错误的,但出于上述原因这样做是可行的......)

    【讨论】:

    • 谢谢。这个变量的描述是模棱两可的,我还没有找到一个明确的答案。但我认为你一定是对的。我遇到了这个问题,因为一个大型软件供应商使用这个变量来确定是否应该在一年中的某个时候观察到夏令时。因此,他们的软件错误地将布里斯班时区的服务器识别为实际上位于悉尼时区,因为“daylight=1”。这就是为什么我将这个问题命名为关于解释的原因。如果可能的话,我仍然想要参考一些文档的明确答案,以便我可以将其带回给供应商。
    • man ctime 显示有关日光变量的评论:----“localtime() 函数将日历时间 timep 转换为中断时间表示,表示相对于用户指定的时区。该函数充当如果它调用 tzset(3) 并使用有关当前时区的信息设置外部变量 tzname,使用协调世界时 (UTC) 和本地标准时间之间的差异(以秒为单位)的时区,如果夏令时将夏令时设置为非零值规则在一年中的某些时候适用。” ---- 最后一点与我们相矛盾。对此有什么想法吗?
    • @AndrewGray:正如你所说,听起来我错了。在我看来,非常奇怪的 API...
    • 我想我需要查看实际的实现,看看该文档是否准确。然而,github.com/eggert/tz/blob/master/localtime.c#L308 似乎支持了我的假设——这似乎是一个“随时检查”的假设。但我从来没有发现那段 C 代码易于阅读,所以我可能会误会。
    • 再次感谢。我在 Linux 上做了一个 man tzset,它说:----“在类似 System-V 的环境中,它还将设置变量时区(格林威治标准时间以西的秒数)和日光(如果该时区没有任何日光,则为 0节省时间规则,如果一年中有时间适用夏令时,则非零)。” ---- 同样,这似乎与我们的假设相矛盾。好困惑。
    【解决方案3】:

    如果您考虑一下 tzset() 的作用,那就更容易了——它安装了时区定义。也就是说,它解释 TZ 环境变量的内容,加载 zoneinfo 文件或类似文件。 tzset() 不做的一件事是根据任何特定时间提供信息,例如,它不查看系统时钟或对当前年份做出假设。有处理特定日期和时间的标准函数,如 gmtime()、localtime()、mktime()。 tzset() 只是为这些函数的工作设置了通用框架,并提供了一些关于外部变量中的整体时区定义的不同基本信息。 tzset() 不会告诉你现在是否有夏令时,因为它不知道也不关心“现在”是什么。它可以告诉您整个时区定义是否包含有关夏令时的任何信息,例如是否定义了 tz[1]。

    似乎有些文档也过时了,因为它没有明确说明 tzset() 如何在其外部变量中呈现更现代/复杂的时区定义,但了解 tzset() 的作用主要是清楚预期会发生什么来自它。

    “daylight”和“timezone”以及一些文档的问题在于,它们是在很早的时候创建的,当时时区定义只不过是一个 TZ 字符串,它的语法可以指定名称和时间偏移量以及诸如“DST”之类的东西从 3 月的第 1 个星期日开始,到 10 月的第 3 个星期日结束”。它不包含历史信息,如果时区规则发生变化,那么您只需更改 TZ 变量即可实施新规则。我们现在有了诸如带有历史信息的 zoneinfo 数据库之类的东西,不幸的是 tzset() 的定义并没有跟上这一点。内部没问题:当 tzset() 加载复杂的时区时,localtime() 等会做正确的事情,但简单的外部变量并不总是能给出准确的总结。 “夏令时”还不错:因为 tzset() 不考虑任何特定的日期/时间,所以如果在时区数据中的任何地方定义了任何夏令时,那么设置“夏令时”是合理的,基本上如果tz[1] 已定义。原则上,时区中的几乎任何东西都可能随着时间的推移而改变,包括时区名称和基本时区偏移量。这种情况很少发生,但一些真实的时区有着不同寻常的历史,尤其是在国际日期变更线附近。

    【讨论】:

      猜你喜欢
      • 2011-08-03
      • 1970-01-01
      • 2013-01-03
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 2013-10-06
      • 2012-06-09
      • 1970-01-01
      相关资源
      最近更新 更多