问题
最近经历了一次半夜提交代码,却发现单元测试无法通过,而无法合并到主线的小事故。经过检查,是一个日志清理模块的实现有问题,一会儿使用 UTC,一会儿使用本地时间(东八区),导致只要在 [0:00, 8:00) 提交代码就无法通过单元测试!而平时都是 10 点上班,所以没长期发现。
在纠正实现的时候,首先想到可以用 _get_timezone 来修正时间,但它是个 CRT 函数,显得不够现代,所以打算用 C++ 20 来实现。
解决
先来看 C 和 C++ 混合的解决方式:
1 | long tz{}; |
这个代码除了不够现代,它还是 MS 特有的(Microsoft Specific),文档都埋坑(见文末)……C++ 20 里有跨平台的封装:std::chrono::zoned_time,下面用它来实现:
1 |
|
可能的输出:
1 | System boot time: 963185693626400ns |
PS: 目前为止,g++ 对 C++ 20 支持不好,请用 MSVC 测试。
注意事项:std::chrono::zoned_time
may throw if location
is not in the time zone database. 需要 catch 类型为 std::chrono::nonexistent_local_time 的异常。
_get_timezone 的坑
_get_timezone
的返回值的含义是 UTC 和 localtime 的差值,单位为秒,比如东八区是 -28800。它的实现是这样的:
1 | extern "C" errno_t __cdecl _get_timezone(long* result) |
目前它的文档里并没有提到需要“前置调用”……如果直接使用,可能得到一个错误的默认值 28800,这是“西八区”的意思!正确的做法是调用 _tzset
、gmtime
或 localtime
等函数后,再调用 _get_timezone。
参考
- _get_timezone
std::chrono::zoned_time
: https://en.cppreference.com/w/cpp/chrono/zoned_time