如何使用std::chrono::parse解析RFC 3164格式的Syslog时间戳?
解决方法
你的测试2失败的核心原因是:Syslog时间戳(%b %e %T)仅包含月、日、时分秒,缺少年份信息,而std::chrono::system_clock的时间点必须包含完整的年月日时分秒,std::chrono::parse无法自动推断年份;%e输出的带前导空格的日期本身不影响解析,但年份缺失是主要障碍。
以下是两种可行的修复方案:
方案1:手动指定年份(适合测试场景)
直接在解析时补充已知年份,将时间戳转换为包含完整日期的格式后解析:
修改test函数中的解析逻辑,针对Syslog格式单独处理:
void test ( std::string const& out_fmt, std::string const& in_fmt, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> const& tp ) { std::ostringstream out; out << std::vformat(out_fmt, std::make_format_args(tp)); std::cout << "Tstamp: " << out.str() << "\n"; std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> tpp; std::istringstream in(out.str()); if (in_fmt == "%b %e %T") { // 先解析月日和时分秒 std::chrono::month_day md; std::chrono::hh_mm_ss<std::chrono::seconds> hms; if (!(in >> std::chrono::parse("%b %e", md) >> std::chrono::parse(" %T", hms))) { std::cout << "Parsing failed!\n"; return; } // 补充测试用的年份2023 std::chrono::year y{2023}; std::chrono::sys_days sd = y/md; tpp = sd + hms.to_duration(); } else { // 原有通用解析逻辑 if (!(in >> std::chrono::parse(in_fmt, tpp))) { std::cout << "Parsing failed!\n"; return; } } std::cout << "Parsed: " << std::vformat(out_fmt, std::make_format_args(tpp)) << "\n"; }
方案2:自动推断年份(符合Syslog规范)
根据RFC3164规则,Syslog时间戳默认使用当前系统年份;若当前月份小于时间戳的月份(比如1月解析12月的日志),则年份需减1。实现代码如下:
void test ( std::string const& out_fmt, std::string const& in_fmt, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> const& tp ) { std::ostringstream out; out << std::vformat(out_fmt, std::make_format_args(tp)); std::cout << "Tstamp: " << out.str() << "\n"; std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> tpp; std::istringstream in(out.str()); if (in_fmt == "%b %e %T") { std::chrono::month_day md; std::chrono::hh_mm_ss<std::chrono::seconds> hms; if (!(in >> std::chrono::parse("%b %e", md) >> std::chrono::parse(" %T", hms))) { std::cout << "Parsing failed!\n"; return; } // 获取当前系统的年月 auto now = std::chrono::system_clock::now(); std::chrono::year current_year = std::chrono::floor<std::chrono::years>(now); std::chrono::month current_month = std::chrono::floor<std::chrono::months>(now); // 自动调整年份 std::chrono::year target_year = current_year; if (current_month < md.month()) { target_year -= std::chrono::years{1}; } // 生成完整时间点 std::chrono::sys_days sd = target_year/md; tpp = sd + hms.to_duration(); } else { if (!(in >> std::chrono::parse(in_fmt, tpp))) { std::cout << "Parsing failed!\n"; return; } } std::cout << "Parsed: " << std::vformat(out_fmt, std::make_format_args(tpp)) << "\n"; }
测试结果
修改后运行代码,测试2将成功输出:
Test 2 Tstamp: May 9 18:25:51 Parsed: May 9 18:25:51
注:std::chrono::parse的%e格式说明符本身支持处理带前导空格或前导零的日期,无需额外处理空格问题。
内容的提问来源于stack exchange,提问作者evolvia
相关产品推荐
相关产品推荐

