C++语言环境是否有关联的时区?如果是,您如何访问它?

does a C++ locale have an associated timezone? And if yes, how do you access it?

我对此做了一些研究,我有相当令人信服的证据 回答 YES,回答 NO。我不确定该相信哪一方。

首先,我在 cppreference.com 上找到的文档,以及 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf 似乎对此只字不提。 我认为这是语言环境不支持时区的证据。

但是https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put 都说:

%z writes offset from UTC in the ISO 8601 format (e.g. -0430), or no characters if the time zone information is not available %Z writes time zone name or abbreviation, or no characters if the time zone information is not available (locale dependent)

这似乎表明有一个时区 SOMETIMES 与 一个 locale () 对象。

现在,如果您选择语言环境 en_US.utf8(我的最爱之一 ;-)),那真的没有任何意义 要关联的时区(美国至少包含 4 个或更多时区)。

所以是时候获得经验了。

我运行代码:

#include <iostream>
#include <cstdlib>
#include <locale>
#include <sstream>
using namespace std;
int main ()
{
    //  locale l;
    locale                l = locale::classic ();
    tm                    when{};
    const time_put<char>& tmput = use_facet<time_put<char>> (l);
    ostringstream         oss;
    oss.imbue (l);
    static const string   kTZOffsetPattern_{"%z"};
    tmput.put (oss, oss, ' ', &when, kTZOffsetPattern_.c_str (), kTZOffsetPattern_.c_str () + kTZOffsetPattern_.length ());
    cout << oss.str ();
    return 0;
}

在 Linux (ubuntu) 上,这给出了我预期的答案,+0000(好的,我也不会对错误或空字符串感到惊讶)。

但是在 Windows 上(视觉 studio.net 2k17 - 15.8.7)- 这给出了: -0500

是的,您可能已经猜到了,我正在东部时区对此进行测试。但我还是会期待 0,或空字符串(特别是 locale::classic() 的情况)。

does a C++ locale have an associated timezone?

当前时区的所有方面都是实现定义的。

来自 C99(C++ 将 C 库函数规范委托给 C 标准)的 %Z 说明符的确切措辞是:

is replaced by the locale’s time zone name or abbreviation, or by no characters if no time zone is determinable.

好像有点暧昧。一种解释确实是语言环境可能会影响时区。另一个也不太符合措辞的地方是,语言环境会影响时区的名称或缩写。无论如何,似乎不能保证时区不受语言环境的影响,尽管我不希望它受到影响。


how do you access it?

据我所知,您不能使用标准库实用程序。反正不是直接的,也没法修改。

打印当前时区的一种方法是使用 %z%Z 格式说明符 strftime/put_time/time_put已经显示。

还有一种方法可以将区域差异作为整数。 std::mktime 根据语言环境将 std::tm 结构解析为时间戳,而 std::gmtime 根据 UTC 将时间戳解析为 std::tm 结构,因此如果您从纪元开始并且将这两者结合起来,您将获得当前语言环境时区和 UTC 的时差(以秒为单位)。

std::time_t t = 0;
std::cout << -1 * std::mktime(std::gmtime(&t));

直接回答您的问题

Does a C++ locale have an associated time zone?

没有

以后也不会。正如问题中正确指出的那样,对于许多语言环境来说,它没有意义,因为语言环境所代表的地理区域可以有多个时区。

C 标准确实在 strftime 的规范中说:

%Z is replaced by the locale’s time zone name or abbreviation, or by no characters if no time zone is determinable. [tm_isdst]

但是 struct lconv 的 C 规范没有提供这样的成员来存储该信息。该规范确实允许实现添加此类成员,但实际上,实现不使用 C 语言环境存储该信息。

C++ 语言环境方面 time_puttime_get 根据 strftime 的 C 规范、strptime 的 POSIX 规范和 strptime 定义自己一些添加,不包括时区名称或缩写。

strftime 的 POSIX 规范比 C 规范详细得多,并删除了与 "locale":

的关联

Z Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ tm_isdst]

struct lconv 的 POSIX 规范也比 C 规范详细得多,但仍然没有提供时区名称或缩写的存储。

但未来确实带来了更轻松有效地访问时区信息的希望,至少在 C++ 中是这样。

在 C++20 之前,C++ 具有以下知识:

  1. 单一时间标准:UTC,与 Unix Time.

  2. 密切相关
  3. 单一时区:"local time zone"由计算机的用户或管理员设置。 UTC 也可以用作本地时区。

如上所述,本地时区 不是 C++(或 C)区域设置数据的一部分。语言环境数据 确实 包括一些日历数据,例如:

  • 工作日的全称和缩写。
  • 完整和缩写的月份名称。
  • 显示日期和时间的本地常规格式(例如年、月、日顺序)。

UTC 偏移量 (%z) 和时区缩写 (%Z) 可能 可用,但会作为本地时间的一部分存储区域数据,而不是当前区域设置数据,主要是因为时区和区域设置之间没有很好的一对一映射。

对 OP 问题中提供的代码发生了什么的解释

在您的示例中:tm when{};tm 的所有成员归零,包括 tm_isdst。当 tm_isdst 为零时,这意味着对于这个特定的 tm.

夏令时已知无效

tm 也允许有标准未指定的成员。一个流行的扩展是有一个成员 tm_gmtoff ,它以秒为单位保存 UTC 偏移量。如果您的 Linux 实现有这样的成员,tm when{}; 会将其设置为 0 秒。如果您的 Windows 实现 没有这样的成员,则本地时区的 UTC 偏移量将存储在其他地方。这解释了您所看到的差异,并且两种实现都符合要求。


有关如何访问时区的有用信息,因为 C++ 语言环境不提供访问权限

在 C++20 规范中,存在一种名为 std::chrono::time_zone 的新类型。 time_zone的成员函数之一是:

template<class Duration> sys_info get_info(const sys_time<Duration>& st) const;

sys_time<Duration> 只是 system_clock::time_point,但具有 any 精度。所以你给一个 time_zone 一个 time_point,然后你得到一个 sys_info,其中包含关于 that time_zone 的各种有用信息在 那个 time_point:

struct sys_info
{
    sys_seconds begin;
    sys_seconds end;
    seconds     offset;
    minutes     save;
    string      abbrev;
};
  • 范围 [begin, end) 告诉您此信息的有效时间(这些是 UTC 时间点)。
  • offsettime_zoneseconds 中的当前 UTC 偏移量。
  • 如果 save != 0mintime_zone 当前被认为处于夏令时。
  • time_zone的当前缩写存储在abbrev

此外,还有一个非成员函数:

const time_zone* current_zone();

其中 returns 指向您当前本地时区的指针。综上所述,这是一个 C++20 程序,它可以打印出有关您当前本地时区的有趣信息:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std::chrono;
    std::cout << current_zone()->get_info(system_clock::now()) << '\n';
}

这只是为我输出:

2018-03-11 07:00:00
2018-11-04 06:00:00
-04:00:00
01:00
EDT

如果愿意,您可以使用 Howard Hinnant's timezone library 使用 C++11、14 或 17 来试验 C++20 的这一部分。这个库把所有东西都放在命名空间 date 而不是 std::chrono.

您还可以获得关于任何 IANA time zone的信息,例如:

#include "date/tz.h"
#include <chrono>
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    std::cout << locate_zone("Australia/Sydney")->get_info(system_clock::now()) << '\n';
}

这只是为我输出:

2018-10-06 16:00:00
2019-04-06 16:00:00
11:00:00
01:00
AEDT

请注意,即使在 C++20 中,时区和语言环境也耦合。这样做是没有意义的。

C标准的相关段落(C++标准所依赖的)说

%z is replaced by the offset from UTC in the ISO 8601 format ‘‘−0430’’ (meaning 4 hours 30 minutes behind UTC, west of Greenwich), or by no characters if no time zone is determinable. [tm_isdst]

%Z is replaced by the locale’s time zone name or abbreviation, or by no characters if no time zone is determinable. [tm_isdst]

请注意,时区 name 据说是区域设置相关的,但时区 offset 不是。

Cppreference 需要修正他们草率的措辞。