std::put_time/std::get_time序列化time_point后出现百年时间差问题
问题根源
GCC 8.5与GCC 11.4对std::get_time中%y(两位年份)格式符的世纪推断逻辑不一致:
- GCC 8.x及更早版本的libstdc++中,
%y解析的两位年份直接映射为1900 + yy(比如24→1924); - GCC 11.x开始的libstdc++遵循更现代的规则,将
00-69映射为2000-2069,70-99映射为1970-1999(比如24→2024)。
这种差异直接导致反序列化后的时间点相差100年。
解决方法
方案1:改用四位年份格式(推荐)
彻底避免两位年份的歧义,序列化/反序列化时使用yyyy格式(对应格式符%Y),这是最稳妥的跨版本兼容方案。
修改你的序列化/反序列化逻辑:
#include <nlohmann/json.hpp> #include <chrono> #include <iomanip> #include <sstream> using json = nlohmann::json; // 序列化:time_point → mm/dd/yyyy hh:mm:ss void to_json(json& j, const std::chrono::system_clock::time_point& tp) { auto tt = std::chrono::system_clock::to_time_t(tp); std::tm tm = *std::localtime(&tt); std::ostringstream oss; oss << std::put_time(&tm, "%m/%d/%Y %H:%M:%S"); // 用%Y代替%y j = oss.str(); } // 反序列化:mm/dd/yyyy hh:mm:ss → time_point void from_json(const json& j, std::chrono::system_clock::time_point& tp) { std::string str = j.get<std::string>(); std::tm tm = {}; std::istringstream iss(str); iss >> std::get_time(&tm, "%m/%d/%Y %H:%M:%S"); // 用%Y代替%y auto tt = std::mktime(&tm); tp = std::chrono::system_clock::from_time_t(tt); }
方案2:强制统一两位年份的解析逻辑
如果必须保留mm/dd/yy格式,手动在反序列化时覆盖编译器的默认推断规则,确保跨版本行为一致。
示例:将两位年份00-69映射到2000-2069,70-99映射到1970-1999(和GCC11行为对齐):
void from_json(const json& j, std::chrono::system_clock::time_point& tp) { std::string str = j.get<std::string>(); std::tm tm = {}; std::istringstream iss(str); iss >> std::get_time(&tm, "%m/%d/%y %H:%M:%S"); // 手动统一世纪推断逻辑 if (tm.tm_year < 70) { // tm_year是年份减1900,70对应1970 tm.tm_year += 100; } auto tt = std::mktime(&tm); tp = std::chrono::system_clock::from_time_t(tt); }
如果你的业务只需要支持2000年后的年份,也可以直接强制加100年:
if (tm.tm_year < 100) { // 100对应2000年 tm.tm_year += 100; }
方案3:完全自定义年份解析逻辑
不依赖std::get_time,手动拆分时间字符串的年份部分,自己计算对应的年份值:
void from_json(const json& j, std::chrono::system_clock::time_point& tp) { std::string str = j.get<std::string>(); // 手动拆分mm/dd/yy hh:mm:ss int month = std::stoi(str.substr(0, 2)); int day = std::stoi(str.substr(3, 2)); int yy = std::stoi(str.substr(6, 2)); int hour = std::stoi(str.substr(9, 2)); int minute = std::stoi(str.substr(12, 2)); int second = std::stoi(str.substr(15, 2)); // 自定义世纪推断 int year = (yy >= 0 && yy <= 69) ? 2000 + yy : 1900 + yy; std::tm tm = {}; tm.tm_year = year - 1900; tm.tm_mon = month - 1; // tm_mon从0开始(1月=0) tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = minute; tm.tm_sec = second; auto tt = std::mktime(&tm); tp = std::chrono::system_clock::from_time_t(tt); }
总结
优先选择方案1(四位年份),从根源消除版本差异;如果必须保留两位年份,方案2或方案3都能保证跨GCC版本的一致行为。
内容的提问来源于stack exchange,提问作者pptaszni
相关产品推荐
相关产品推荐

