最标准方式是先用 system_clock::to_time_t 转为 time_t,再用 localtime/gmtime 转 tm,最后用 std::put_time 格式化;%S 不含毫秒,多线程需用 localtime_r 或 localtime_s。

用 std::put_time 格式化 system_clock::time_point
直接转字符串最常用、最标准的方式是先转成 std::time_t,再用 std::put_time 配合 std::ostringstream。注意不能跳过 std::time_t 这一层——system_clock::to_time_t 是唯一标准转换入口。
常见错误是试图对 time_point 直接调用 std::ctime 或传给 strftime,编译会失败,因为它们只认 std::time_t 或 std::tm。
- 必须先调用
system_clock::to_time_t(tp)得到秒级时间戳 -
std::gmtime和std::localtime返回的是std::tm*,注意线程安全:后者是全局静态缓冲区,多线程下要用std::localtime_r(POSIX)或std::localtime_s(MSVC) - 格式字符串如
"%Y-%m-%d %H:%M:%S"中的%S只显示整秒,毫秒需额外处理
auto now = system_clock::now(); auto tt = system_clock::to_time_t(now); auto tm = *std::localtime(&tt); // 单线程安全;多线程请用 localtime_s / localtime_r std::ostringstream oss; oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); std::string s = oss.str(); // 例如 "2024-05-22 14:30:45"
带毫秒精度的字符串输出
system_clock::time_point 通常以纳秒或微秒为单位存储,但 std::time_t 只有秒级精度。要保留亚秒部分,得手动提取。
关键点:用 tp.time_since_epoch() 拿到从纪元开始的 duration,再用 duration_cast 提取毫秒部分,避免浮点运算和舍入误差。
立即学习“C++免费学习笔记(深入)”;
- 别用
double除法算毫秒——容易因浮点精度丢掉最后几位 - 推荐用
duration_cast<milliseconds></milliseconds>获取总毫秒数,再对 1000 取余得毫秒字段 - 注意时区:上面的
std::localtime是本地时区;若需 UTC,用std::gmtime
auto now = system_clock::now();
auto ms = duration_cast<milliseconds>(now.time_since_epoch());
auto ss = duration_cast<seconds>(ms);
auto msec = ms.count() % 1000;
std::time_t t = ss.count();
std::tm tm = {};
#ifdef _WIN32
localtime_s(&tm, &t);
#else
localtime_r(&t, &tm);
#endif
std::ostringstream oss;
oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << '.' << std::setfill('0') << std::setw(3) << msec;
std::string s = oss.str(); // 例如 "2024-05-22 14:30:45.123"
跨平台兼容性与 C++ 标准版本影响
C++20 引入了 std::format 和日历库,但目前主流项目仍以 C++17 为主。不同标准下行为差异明显:
- C++11/14/17:只能靠
std::put_time+ 手动拼接毫秒,std::put_time在某些旧版 libstdc++(如 GCC 4.8)中不支持宽字符或部分格式符 - C++20:
std::format("{:%Y-%m-%d %H:%M:%S}", sys_time{tp})更简洁,但需编译器支持(GCC 13+、Clang 15+、MSVC 2022 17.5+),且sys_time转换需显式构造 - Windows 下
std::put_time对中文 locale 支持不稳定,建议固定用"C"locale 避免乱码
如果项目要求严格跨平台且不升级编译器,老老实实用 std::put_time + 手动毫秒拼接最稳妥。
为什么不用 ctime 或 asctime
这两个函数返回 char*,内容在静态缓冲区里,不可重入,且格式完全固定(如 "Wed Apr 24 15:23:12 2024\n"),无法自定义分隔符、时区或精度。
-
ctime(&t)总是带换行符,必须strcspn截断,易出错 - 它们不接受
time_point,仍需先转time_t,没省步骤 - 输出格式硬编码,比如没有毫秒、不能去掉年份、不能切分成 ISO 8601 格式
除非你只要一行调试输出且能接受固定格式,否则绕开它们更干净。


















