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_tsystem_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_zonesystem_clock::time_point 的配对。

您可以像这样得到local_time&lt;Duration&gt;,即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" 来打印 offsetoffset 的类型为 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"的内容在&lt;chrono&gt;中。 namespace date 中名称的内容在namespace std::chrono 中。 make_zoned 不是 C++20 的一部分,因为 CTAD 使它变得不必要。您可以使用zoned_time 代替它并推导出模板参数。

此外,自定义(用户编写的)time_zone 现在可以与 https://howardhinnant.github.io/date/tz.html 或新的 C++20 &lt;chrono&gt; 一起使用。自定义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.hptz.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() 的值是不是单调增加?

C++中时间转换

如何使用 std::chrono 库设置特定时间?

当前本地时间高精度

c++11测试时间封装