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

时间:2018-10-16 16:04:16

标签: c++ locale

我对此进行了一些研究,并且我有令人信服的证据表明 答案为是,答案为否。我不确定该相信哪一方。

首先,我在cppreference.com上找到了文档,并且 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf对此一无所获。 我以此为依据,证明语言环境不支持时区。

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

  

%z以ISO 8601格式(例如-0430)写入UTC的偏移量,或者否   时区信息不可用时输入的字符%Z写入   时区名称或缩写,如果时区则没有字符   信息不可用(取决于语言环境)

似乎暗示存在与SOMETIMES相关的时区 语言环境()对象。

现在,如果您使用语言环境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上(visual studio.net 2k17-15.8.7)-这给出了:     -0500

是的,正如您可能已经猜到的,我正在东部时区进行测试。但是我仍然会期待 0或空字符串(尤其是locale :: classic()情况)。

3 个答案:

答案 0 :(得分:10)

直接回答您的问题

  

C ++语言环境有关联的时区吗?

否。

将来也不会。正如问题中正确指出的那样,对于许多语言环境而言,这毫无意义,因为该语言环境所代表的地理区域可以具有多个时区。

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

  

%Z替换为语言环境的时区名称或缩写,如果没有则替换为任何字符   时区是可以确定的。 [tm_isdst]

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

C ++语言环境方面time_puttime_get根据strftime的C规范,strptime的POSIX规范以及一些附加的内容来定义自己。包括时区名称或缩写。

strftime的POSIX规范比C规范详细得多,并删除了与“ locale”的关联:

  

Z替换为时区名称或缩写,如果不存在时区信息,则替换为无字节。 [ tm_isdst]

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

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

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

  1. 单一时间标准:UTC,由Unix Time严格建模。

  2. 单个时区:计算机的用户或管理员设置的“本地时区”。 UTC也可以用作本地时区。

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

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

UTC偏移量(%z)和时区缩写(%Z 可能可用,但将被存储为本地时区数据的一部分,而不是使用当前的语言环境数据,主要是因为在时区和语言环境之间没有良好的一对一映射。

OP提出的问题代码的解释

在您的示例中:tm when{};tm的所有成员(包括tm_isdst)清零。当tm_isdst为零时,这意味着对于特定的tm,夏令时无效。

tm也被允许具有标准未指定的成员。一个流行的扩展名是拥有成员tm_gmtoff,该成员拥有以秒为单位的UTC偏移量。如果您的Linux实现具有这样的成员,则tm when{};会将其设置为0秒。如果您的Windows实现 not 没有这样的成员,则本地时区的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,但精度为任何。因此,给time_zone一个time_point,然后返回一个sys_info,其中包含有关 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 != 0min,则time_zone当前被认为是夏令时。
  • time_zone的当前缩写存储在abbrev中。

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

const time_zone* current_zone();

返回指向您当前本地时区的指针。综上所述,这是一个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中,时区和语言环境也是耦合的。这样做没有意义。

答案 1 :(得分:3)

  

C ++语言环境是否具有关联的时区?

当前时区的所有方面均已实现定义。

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

  

替换为语言环境的时区名称或缩写,如果无法确定时区,则不替换任何字符。

似乎有点模棱两可。一种解释确实是,语言环境可能会影响时区。另一个也不很恰当的措词是,语言环境会影响时区的名称或缩写。无论如何,似乎无法保证时区不受语言环境的影响,尽管我不希望这样。


  

您如何访问它?

据我所知,您不能使用标准库实用程序。无论如何都不是直接的,也没有办法对其进行修改。

打印当前时区的一种方法是使用%z / %Z / strftime的{​​{1}}或put_time格式说明符

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

std::tm

答案 2 :(得分:0)

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

  

%z被替换为ISO 8601格式“ −0430”中的UTC偏移量(意味着比格林尼治西部的UTC落后4小时30分钟),或者如果无法确定时区,则不替换任何字符。 [tm_isdst]

     

%Z替换为语言环境的时区名称或缩写,如果无法确定时区,则不替换任何字符。 [tm_isdst]

请注意,时区 name 被认为是与语言环境相关的,而时区 offset 则不受限制。

Cppreference需要修正其草率的措辞。