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_put
和 time_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++ 具有以下知识:
单一时间标准:UTC,与 Unix Time.
密切相关
单一时区:"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 时间点)。
offset
是 time_zone
在 seconds
中的当前 UTC 偏移量。
- 如果
save != 0min
,time_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 需要修正他们草率的措辞。
我对此做了一些研究,我有相当令人信服的证据 回答 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_put
和 time_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++ 具有以下知识:
单一时间标准:UTC,与 Unix Time.
密切相关
单一时区:"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 时间点)。 offset
是time_zone
在seconds
中的当前 UTC 偏移量。- 如果
save != 0min
,time_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 需要修正他们草率的措辞。