为何chrono::parse无法解析POSIX日期时间字符串?
<chrono>解析POSIX日期字符串失败(不受全局locale影响) 尝试用C++的<chrono>库解析POSIX格式的日期时间字符串,确保不受全局区域设置影响,编写了如下代码,但程序始终输出解析错误,无法得到预期结果。
环境:Arch Linux(内核6.10.7-arch1-1)、g14.2.1,编译时使用C20/C++23标准。
输入字符串:"Fri Jul 5 14:58:21 2019"
预期输出:2019-07-5 14:58:21
代码:
#include <iostream> #include <locale> #include <sstream> #include <string> #include <chrono> using namespace std; void printPosixDateString(string const &str) { istringstream stringStream{}; stringStream.str(str); locale cLocale{"C"};//locale of C/POSIX language stringStream.imbue(cLocale); chrono::sys_seconds timeHolder{};//default value is unix time stringStream >> chrono::parse("%c", timeHolder); if(stringStream.fail()) { cout << "Error while parsing date string : " << stringStream.str() << endl; } else { cout << timeHolder;//should print the date object } } int main(int argc, char **argv) { locale french("fr_FR.UTF8"); locale::global(french); cout.imbue(french); printPosixDateString("Fri Jul 5 14:58:21 2019");//must work no matter the global locale return 0; }
问题现象:程序始终输出"Error while parsing date string: Fri Jul 5 14:58:21 2019"。
核心原因
问题出在%c格式符的定义与输入字符串的不匹配:
在C/POSIX locale("C" locale)中,%c对应的标准日期时间格式是"%a %b %e %H:%M:%S %Y",其中%e表示日部分为带前导空格的两位字符(例如5号会被格式化为 5,而非单个数字5)。你的输入字符串中日期是5(无前置空格),与%c要求的格式不匹配,导致解析失败。
解决办法
根据你的需求(希望依赖locale的%c而非自定义格式),有两种可行方案:
调整输入字符串格式
将输入字符串的日期部分改为带前导空格的形式,例如:printPosixDateString("Fri Jul 5 14:58:21 2019"); // Jul后为两个空格,日期是" 5"这样就能匹配
%c在C locale下的格式,解析成功。使用自定义格式符匹配输入
若无法修改输入字符串,可放弃%c,改用与输入完全匹配的自定义格式符:stringStream >> chrono::parse("%a %b %d %H:%M:%S %Y", timeHolder);其中
%d可以匹配1位或2位的日数字(g++的<chrono>实现支持这种匹配),能正确解析5或05格式的日期。
额外说明
若要输出2019-07-5(日不带前导零)的格式,默认的operator<<无法实现,需要使用chrono::format自定义输出格式:
cout << chrono::format("%F %T", timeHolder); // 默认输出2019-07-05 14:58:21 // 若要日不带前导零,使用: cout << chrono::format("%Y-%m-%-d %H:%M:%S", timeHolder);
内容的提问来源于stack exchange,提问作者LightGolgot

