You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 16:11:19