我正在使用 .json 实现时间戳的 json 序列化和反序列化nlohmann::json
。转换std::chrono::time_point
为mm/dd/yy hh:mm:ss
日期时间样式(根据我们的要求,它必须是人类可读的)后,我自然会丢失一些信息,因此我对反序列化后时间点相等的标准是“相差不超过一秒”。
在使用 gcc 11.4.0 的本地计算机上一切正常,但在使用 gcc 8.5.0 的生产服务器上失败。
在 godbolt 上重现问题的最少代码:
#include <iostream>
#include <iomanip>
#include <chrono>
#include <string>
int main() {
// serialization
auto ts = std::chrono::system_clock::now();
auto timeS_t = std::chrono::system_clock::to_time_t(ts);
std::ostringstream outStream;
outStream << std::put_time(std::localtime(&timeS_t), "%x %X");
std::string tsStr = outStream.str();
// deserialization
std::istringstream ss(tsStr);
std::tm t = {};
ss >> std::get_time(&t, "%x %X");
auto parsedTs = std::chrono::system_clock::from_time_t(std::mktime(&t));
// difference in seconds
int secDiff = std::chrono::duration_cast<std::chrono::seconds>(ts - parsedTs).count();
// just debug prints
std::cout << secDiff << std::endl;
std::time_t ttp1 = std::chrono::system_clock::to_time_t(ts);
std::time_t ttp2 = std::chrono::system_clock::to_time_t(parsedTs);
std::cout << "ts1: " << std::ctime(&ttp1);
std::cout << "ts2: " << std::ctime(&ttp2);
// my equality criterion
if (std::abs(secDiff) > 1) {
std::cout << "FAIL\n";
}
}
使用较新的编译器我得到相同的结果,例如
ts1: Thu Feb 22 16:06:25 2024
ts2: Thu Feb 22 16:06:25 2024
但对于旧的,我看到时间点上的差异正好是 100 年:
ts1: Thu Feb 22 16:06:17 2024
ts2: Fri Feb 22 16:06:17 1924
我想这可能与序列化例程有关std::localtime(&timeS_t)
,但我没有找到反序列化器的“反向”功能。我可以改变什么来保持一致的行为?
我认为核心问题是使用“%X %x”作为格式。根据cppreference,这取决于区域设置。这意味着您将获得不同的表示形式,具体取决于运行的位置,甚至如您所见,不同的编译器版本。对于格式,我建议
"%Y-%m-%d %H:%M:%S"
避免依赖于区域设置的行为。我还建议不要使用当地时间,因为它取决于时区。
std::gmtime
会更好。查看结果: https: //godbolt.org/z/osT9741vz
您不想将日期/时间序列化为与区域设置相关的格式。相反,序列化与区域设置无关的内容,例如自纪元以来的毫秒数:
现场演示
问题是如何解释两位数年份。当人类看到时,
'80
他假设 1980 年。当他看到时,'22
他假设 2022 年。现在,这种人为偏见并未反映在旧版本库中,因此
std::get_time
仅假设两位数年份指的是 1900 年。新版本库试图平衡这一点,并根据两位数值假设 1900 或 2000。以下是问题的演示,包括 gcc 8.5 的可能解决方法: https: //godbolt.org/z/MThhac943
最好能在文档中找到此行为的解释。如果世纪交替的边界随着当前时间的变化而变化,那么我不会感到惊讶,所以未来的 2100 年是确定的。
编辑:
在perplexity.ai的帮助下,我找到了一些解释这种行为的文档(google 和 bing 在此搜索失败):
日期
因此,这种行为已被标准化,
IEEE Std 1003.1-2001
并且预计将来会更新。到目前为止,我无法找到该标准的公开文档。Howard Hinnant 提供了 C++20 标准的良好链接作为评论:
[时间.解析]