std::chrono::system_clock::now() 考虑操作系统配置的时区
Posted
技术标签:
【中文标题】std::chrono::system_clock::now() 考虑操作系统配置的时区【英文标题】:std::chrono::system_clock::now() considering the OS configured time zone 【发布时间】:2016-09-01 14:28:34 【问题描述】:我正在编写一个在 BusyBox 嵌入式 linux 上运行的 C++ 代码。我的代码及其库多次调用std::chrono::system_clock::now()
来获取当前时间。
从现在开始,我的盒子被配置为默认时区 (UTC),一切正常,进程运行,结果正常。
现在我必须将我的 linux 设置为留在不同的时区。然后我通过在框中配置/etc/profile
做到了:
export TZ=UTC+3
当我发出date
命令和控制台时,我得到了正确的时间,但我对std::chrono::system_clock::now()
的调用仍然得到UTC 时间,而不是date
命令中显示的时间(正确的时间)。
我不想更改我所有的 now()
调用 - 它们有数百个......这导致我的进程使用的时间与控制台上设置的正确时间不同。
有什么方法可以在不更改我的代码的情况下解决这个问题吗?我在这里缺少什么吗?
感谢您的帮助。
【问题讨论】:
在内部使用 UTC 以外的任何东西都是错误的。将其显示给人类用户时将其转换为当地时间。使用 UTC 的程序非常好,它们不关心我们的昼夜节律。 n.m. - 从 now() 收集的数据然后使用 int64 格式存储在本地和云数据库中,并将它们上传到 Web 服务器。然后显示给最终用户......看来我需要在存储到数据库之前在我的 C++ 代码上获取正确的时间,因为我不会跟踪上述层中的时区...... @mendes 天哪,不,不要将本地时间存储在云数据库中。 也许存储用户设置某些事件时使用的时区除了通用时间戳(对于日历应用程序,他们选择了 8 月 12 日和晚上 7 点),但本地时间几乎在所有情况下都用于用户输入和输出。这种情况(选择的时区)是指用户在某种意义上选择一个绝对时间点(而不是某个时间、实际事件发生或“从现在起 6 小时”)。 "然后显示给最终用户" 这似乎是根据用户的偏好(而不是设备所在的时区)将其转换为用户时区的时刻。 “获取正确的时间”只要您有多个位置,UTC 就是 正确的时间。 我明白了。我将结束这个问题并发布另一个关于云存储的问题...... 【参考方案1】:尽管标准未指定,但 std::chrono::system_clock::now()
的每个实现都在跟踪 Unix Time,这与 UTC 非常接近。
如果您想将std::chrono::system_clock::now()
转换为当地时间,您可以通过system_clock::to_time_t
将system_clock::time_point
转换为time_t
,然后通过C API(例如localtime
)进行操作,或者您也可以试试这个建立在<chrono>
之上的现代时区库:
https://howardhinnant.github.io/date/tz.html
您可以使用它来获取当前本地时间,如下所示:
#include "tz.h"
#include <iostream>
int
main()
using namespace date;
using namespace std::chrono;
auto t = make_zoned(current_zone(), system_clock::now());
std::cout << t << '\n';
make_zoned
是一个工厂函数,它以 system_clock
支持的任何精度(例如纳秒)返回 zoned_time
类型。它是time_zone
和system_clock::time_point
的配对。
您可以像这样得到local_time<Duration>
,即std::chrono::time_point
:
auto t = make_zoned(current_zone(), system_clock::now());
auto lt = t.get_local_time();
std::cout << lt.time_since_epoch().count() << '\n';
虽然库有一个远程 API 可以自动下载 IANA timezone database,但可以通过使用 -DHAS_REMOTE_API=0
编译来禁用该 API。这一切都在installation instructions 中有详细说明。禁用远程 API 后,您必须手动从 IANA timezone database 下载数据库(它只是一个 tar.gz
)。
如果您需要当前的 UTC 偏移量,可以这样获得:
auto t = make_zoned(current_zone(), system_clock::now());
auto offset = t.get_info().offset;
std::cout << offset << '\n';
在上面的 sn-p 中,我利用在同一 github 存储库中找到的 "chrono_io.h"
来打印 offset
。 offset
的类型为 std::chrono::seconds
。这只是为我输出:
-14400s
(-0400)
最后,如果你想知道你当前时区的 IANA 名称,那就是:
std::cout << current_zone()->name() << '\n';
对我来说只是输出:
America/New_York
从name()
返回的类型是std::string
。如果需要,可以记录该字符串,然后使用带有该名称的time_zone
,即使这不是计算机的当前时区:
auto tz_name = current_zone()->name();
// ...
auto t = make_zoned(tz_name, system_clock::now()); // current local time in tz_name
4 年后更新
这个库现在是 C++20 的一部分,有以下修改:
"tz.h"
的内容在<chrono>
中。
namespace date
中名称的内容在namespace std::chrono
中。
make_zoned
不是 C++20 的一部分,因为 CTAD 使它变得不必要。您可以使用zoned_time
代替它并推导出模板参数。
此外,自定义(用户编写的)time_zone
现在可以与 https://howardhinnant.github.io/date/tz.html 或新的 C++20 <chrono>
一起使用。自定义time_zone
的一个很好的示例已写入model POSIX time zones here。这可以用来更准确地回答原始问题:
#include "date/ptz.h"
#include <cstdlib>
#include <iostream>
int
main()
using namespace date;
using namespace std;
using namespace std::chrono;
const char* tz = getenv("TZ");
if (tz == nullptr)
tz = "UTC0";
zoned_time nowPosix::time_zonetz, system_clock::now();
cout << format("%F %T%z", now) << '\n';
将我的TZ
环境变量设置为UTC+3
这只是我的输出:
2020-09-17 10:28:06.343050-0300
注意事项:
尽管依赖于tz.h
,ptz.h
是一个仅包含标头的库。无需安装 IANA tz 数据库,也无需编译tz.cpp
。
POSIX specifies UTC 偏移量的符号与其他一切使用的符号相反(包括 POSIX 的其他部分)。正偏移是本初子午线的 west 而不是 east。这反映在此示例中将 UTC 偏移格式化为负的输出中,这与 POSIX strftime
spec 一致。
如果使用 C++11 或 C++14 编译它,您的工具箱中没有 CTAD。在这种情况下:
`
zoned_time nowPosix::time_zonetz, system_clock::now();
变成:
zoned_time<system_clock::duration, Posix::time_zone> nowPosix::time_zonetz, system_clock::now();
【讨论】:
好吧,我不需要字符串,而是与原始 now() 相同的格式,但时钟代表正确的时区。嵌入式系统上没有互联网连接,以防图书馆需要一些外部访问...... @Mendes:更新答案以解决您的问题。以上是关于std::chrono::system_clock::now() 考虑操作系统配置的时区的主要内容,如果未能解决你的问题,请参考以下文章
如何找到纪元时间戳和 std::chrono::system_clock::now 之间的时间差(以毫秒为单位)
std::chrono::system_clock.now().time_since_epoch().count() 的值是不是单调增加?