【问题标题】:How do you get info for an arbitrary time zone in Linux / POSIX?如何在 Linux / POSIX 中获取任意时区的信息?
【发布时间】:2010-09-09 03:34:02
【问题描述】:

理想情况下,我想做的是获取时区的名称并调用一个函数来询问其相应的时区信息(与 UTC 的偏移量、DST 偏移量、DST 切换的日期等。 ) 在 Linux 中。但是,我找不到任何方法来做到这一点。这些信息存在于/usr/share/zoneinfo/ 中的各种二进制文件中,但我不知道如何阅读它们,或者是否有办法让操作系统为您提供其中的信息,而不必自己阅读它们。所以,我正在寻找一种方法来获取这些信息。理想情况下,会有一个 posix 函数可以为您完成它,以便它可以在 Linux 以外的 POSIX 系统上工作,但如果没有,我至少想知道正确的方法(或者至少是最好的方式)获取Linux中任意时区的时区信息。

【问题讨论】:

    标签: c++ c linux timezone posix


    【解决方案1】:

    tzcode package(与 ftp://ftp.iana.org/tz/releases 中的数据一起找到)包含 tzfile 格式的描述以及用于直接处理该数据的头文件 tzfile.h

    【讨论】:

      【解决方案2】:

      没有标准的方法可以做到这一点。你可以看看ICU。它声称:

      • 格式:根据所选区域设置的惯例格式化数字、日期、时间和货币金额。这包括将月份和日期名称翻译成所选语言、选择适当的缩写、正确排序字段等。这些数据也来自 Common Locale Data Repository。

      • 时间计算:除了传统的公历之外,还提供了多种类型的日历。 提供了一套完整的时区计算 API强调)。

      【讨论】:

      • ICU 确实有适当的日历实现。/
      【解决方案3】:

      旧问题的新答案。

      新答案的基本原理:现在有一个 modern, free, open-source, C++11/14/17 library 来执行此操作。1它确实需要 一些 installation。但它可以跨 Linux/macOS/Windows 移植。它有full documentation,甚至还有video introduction

      这是一个获取特定时区信息的示例程序。我以我自己的时区为例。这个库支持完整的IANA timezone database

      #include "tz.h"
      #include <iostream>
      
      int
      main()
      {
          auto zone = date::locate_zone("America/New_York");
          std::cout << *zone << '\n';
          std::cout << zone->get_info(std::chrono::system_clock::now()) << '\n';
      }
      

      第一行通过 IANA 名称查找数据库。

      auto zone = date::locate_zone("America/New_York");
      

      返回类型是date::time_zone const*。这个函数永远不会返回nullptr,但如果它无法定位时区,它会抛出(有一个很好的what())。

      第二行打印出时区的定义:

      std::cout << *zone << '\n';
      

      这一行的输出通常对这个库的客户没有用处。主要是对自己调试库有用:

      America/New_York                   -04:56:02                  LMT        1883 Nov/18                  12:03:58         1883-11-18 17:00:00 UTC   1883-11-18 12:03:58 STD   1883-11-18 12:03:58   00:00      {nullptr, -32768}   {nullptr, 32767}
                                         -05:00:00   US             E%sT       1920 Jan/01                  00:00:00         1920-01-01 05:00:00 UTC   1920-01-01 00:00:00 STD   1920-01-01 00:00:00   00:00   S   {US             1918    1919    Mar/Sun[last]           02:00:00       01:00   D, 1918}   {US             1918    1919    Oct/Sun[last]           02:00:00       00:00   S, 1919}
                                         -05:00:00   NYC            E%sT       1942 Jan/01                  00:00:00         1942-01-01 05:00:00 UTC   1942-01-01 00:00:00 STD   1942-01-01 00:00:00   00:00   S   {NYC            1920    1920    Mar/28                  02:00:00       01:00   D, 1920}   {NYC            1921    1954    Sep/Sun[last]           02:00:00       00:00   S, 1941}
                                         -05:00:00   US             E%sT       1946 Jan/01                  00:00:00         1946-01-01 05:00:00 UTC   1946-01-01 00:00:00 STD   1946-01-01 00:00:00   00:00   S   {US             1942    1942    Feb/09                  02:00:00       01:00   W, 1942}   {US             1945    1945    Sep/30                  02:00:00       00:00   S, 1945}
                                         -05:00:00   NYC            E%sT       1967 Jan/01                  00:00:00         1967-01-01 05:00:00 UTC   1967-01-01 00:00:00 STD   1967-01-01 00:00:00   00:00   S   {NYC            1921    1954    Apr/Sun[last]           02:00:00       01:00   D, 1946}   {NYC            1955    1966    Oct/Sun[last]           02:00:00       00:00   S, 1966}
                                         -05:00:00   US             E%sT       32767 Dec/31                  00:00:00UTC      32767-12-31 00:00:00 UTC   32767-12-30 19:00:00 STD   32767-12-30 19:00:00   00:00   S   {US             1967    1973    Apr/Sun[last]           02:00:00       01:00   D, 1967}   {US             2007    32767    Nov/Sun[1]              02:00:00       00:00   S, 32767}
      

      但我展示这一行的原因之一是为了说明在不提供时间点的情况下询问有关时区的信息,不太可能给你提供你想要的信息寻找。时区信息本身就是时间的函数,包括偏移量、夏令时细节、缩写等。

      最后一行:

      std::cout << zone->get_info(std::chrono::system_clock::now()) << '\n';
      

      可能最有帮助。这将返回一个聚合 sys_info,如下所示:

      struct sys_info
      {
          sys_seconds          begin;
          sys_seconds          end;
          std::chrono::seconds offset;
          std::chrono::minutes save;
          std::string          abbrev;
      };
      

      这只是为我输出:

      2016-11-06 06:00:00
      2017-03-12 07:00:00
      -05:00:00
      00:00
      EST
      

      这意味着:

      • 此信息的有效期为 2016-11-06 06:00:00 UTC 至(但不包括)2017-03-12 07:00:00 UTC。
      • 此时间段的 UTC 偏移量为 -5 小时。
      • 这不被视为夏令时 (save == 00:00)。
      • 此期间的缩写为 EST。

      当然,您可以在程序中访问此聚合的字段,而不仅仅是打印出来。

      如果您想看看这个结果在 6 个月后会是什么样子:

      std::cout << zone->get_info(std::chrono::system_clock::now() + date::months{6}) << '\n';
      

      当前输出:

      2017-03-12 07:00:00
      2017-11-05 06:00:00
      -04:00:00
      01:00
      EDT
      

      这在这个库中都被认为是低级访问。存在更高级别的 API,因此您不必处理诸如当前 UTC 偏移量之类的低级概念。如果您需要,低级别的东西就在那里(不是隐藏起来),但对于常见用例(例如获取任何特定时区的当前时间)不是必需的:

      using namespace date;
      using namespace std::chrono;
      std::cout << make_zoned("America/New_York", system_clock::now()) << '\n';
      

      这只是为我输出:

      2017-03-07 19:26:53.711662 EST
      

      在 C++17 中,由于模板推导指南,上述行将不再需要“make factory functions”进行推导:

      std::cout << zoned_time{"America/New_York", system_clock::now()} << '\n';
      

      zoned_time 是一个类模板,以持续时间为模板,由chrono::time_pointchrono::duration 推断(第二个参数——在我的例子中为microseconds)。

      这是一个功能齐全的日期/时间/时区库,具有低级访问和高级抽象(符合 C++ 哲学)。该库高度重视正确性和类型安全性。它是 C++11 中引入的 &lt;chrono&gt; 库的扩展,而不是替代品。


      1免责声明:我是这个库的主要作者,尽管有很多贡献者(我很感激)。

      【讨论】:

        猜你喜欢
        • 2023-03-23
        • 1970-01-01
        • 2020-07-17
        • 1970-01-01
        • 2011-12-14
        • 2011-03-08
        • 1970-01-01
        • 2019-08-06
        相关资源
        最近更新 更多