c++中time_point转字符串默认不包含毫秒,因std::time_t仅支持秒级精度;需手动提取毫秒余数并拼接,同时注意localtime线程不安全及跨平台时区处理。

time_point 转字符串时默认不包含毫秒
标准 std::chrono::system_clock::now() 返回的是高精度 time_point,但直接用 std::put_time 或 strftime 格式化,底层依赖 std::time_t,天然丢失毫秒(甚至微秒)信息。这不是格式化写错了,而是类型截断——time_t 通常只到秒级。
必须手动提取毫秒部分再拼接
正确做法是:先用 time_point.time_since_epoch() 拿到纳秒/毫秒级的持续时间,再分别算出秒数和毫秒余数。常见错误是直接对 time_point 做 std::chrono::duration_cast<:chrono::milliseconds></:chrono::milliseconds> 后取模,结果会因时区或系统 clock 偏移导致毫秒值跳变(比如跨秒时多出 1000)。
- 用
std::chrono::duration_cast<:chrono::seconds></:chrono::seconds>得到整秒部分,传给std::gmtime或std::localtime转结构体 - 用
std::chrono::duration_cast<:chrono::milliseconds>(tp.time_since_epoch()) % 1000</:chrono::milliseconds>得到严格 0–999 的毫秒值 - 注意:不要用
std::chrono::system_clock::to_time_t(tp)再转回tm,它会抹掉亚秒部分
推荐用 std::format(C++20)或 sprintf 配合 strftime
std::format 在 C++20 中支持 {:%Y-%m-%d %H:%M:%S},但目前(GCC 13/Clang 17)仍不支持毫秒占位符(如 {:fff}),所以得手动拼接。更稳的方式是:
auto now = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<:chrono::milliseconds>(now.time_since_epoch()) % 1000;
auto sec = std::chrono::time_point_cast<:chrono::seconds>(now);
std::time_t t = sec.time_since_epoch().count();
std::tm tm{};
#ifdef _WIN32
localtime_s(&tm, &t);
#else
localtime_r(&t, &tm);
#endif
char buf[64];
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
sprintf(buf + strlen(buf), ".%03d", static_cast<int>(ms.count()));
// buf 现在形如 "2024-05-22 14:32:18.123"
</int></:chrono::seconds></:chrono::milliseconds>
Windows 下要注意 local_time 的线程安全性
localtime 是非线程安全的,在多线程日志中直接用会崩溃或返回错乱时间。Linux 下可用 localtime_r,Windows 则必须用 localtime_s(MSVC)或 localtime_r(MinGW)。如果项目要跨平台,建议封装一层判断,或者统一用 std::chrono::utc_clock 避开本地时区转换——很多服务日志其实只需要 UTC 时间戳。
毫秒本身没问题,但时区转换和线程安全这两点,比格式化字符串难 debug 得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











