我正在编写运行在BusyBox嵌入式linux上的c++代码。我的代码和它的库有几个调用std::chrono::system_clock::now()
来获得当前时间。
因为现在我的机器被配置为默认时区(UTC),一切都很好,进程运行和结果ok。
现在我必须把我的linux设置在不同的时区。然后我在/etc/profile
框中配置:
export TZ=UTC+3
当我发出date
命令和控制台时,我得到了正确的时间,但是我对std::chrono::system_clock::now()
的调用仍然得到UTC时间,而不是date
命令中显示的时间(正确的时间)。
我不想改变我所有的now()
调用-有数百个…这导致我的进程工作的时间与控制台上设置的正确时间不同。
有没有办法解决,而不改变我的代码?有什么我遗漏的吗?
虽然标准没有指定,但std::chrono::system_clock::now()
的每个实现都跟踪Unix时间,这与UTC非常接近。
如果你想将std::chrono::system_clock::now()
转换为本地时间,你可以通过system_clock::to_time_t
将system_clock::time_point
转换为time_t
,然后通过C API(例如localtime
)工作,或者你可以尝试构建在<chrono>
之上的现代时区库:
你可以用它来获取当前的本地时间,像这样:
#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
是一个工厂函数,它返回zoned_time
类型,具有system_clock
支持的任何精度(例如纳秒)。是一个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时区数据库,但可以通过使用-DHAS_REMOTE_API=0
编译来禁用该API。这些都在安装说明中有详细说明。禁用远程API后,您必须手动从IANA时区数据库下载数据库(这只是一个tar.gz
)。
如果你需要当前的UTC偏移量,可以这样获取:
auto t = make_zoned(current_zone(), system_clock::now());
auto offset = t.get_info().offset;
std::cout << offset << 'n';
在上面的代码片段中,我利用在同一个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
的好例子来模拟POSIX时区。这可以用来更精确地回答原来的问题:
#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 now{Posix::time_zone{tz}, 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指定UTC偏移量的符号与其他所有使用的符号相反(包括POSIX的其他部分)。正偏移量是本初子午线西而不是东。这反映在输出中,在本例中将UTC偏移量格式化为负值,这与POSIX
strftime
规范一致。如果用c++ 11或c++ 14编译,你的工具箱中没有CTAD。在本例中:
zoned_time now{Posix::time_zone{tz}, system_clock::now()};
就变成:
zoned_time<system_clock::duration, Posix::time_zone> now{Posix::time_zone{tz}, system_clock::now()};