应使用 std::chrono::system_clock::now() 获取高精度时间点,通过 std::localtime() 转本地时区,再分离毫秒拼接字符串;避免直接操作 tm_sec 或依赖未完全支持的 zoned_time。

用 std::chrono 获取毫秒级时间点再转本地时区
标准库里没有直接“带毫秒的本地时间字符串”函数,std::time_t 只能到秒,必须靠 std::chrono::system_clock 拿高精度时间点,再手动转成本地时区(注意:不是 UTC)。很多代码直接用 std::chrono::system_clock::now() 然后丢给 std::gmtime,结果输出的是 UTC 时间,和系统设置的时区无关。
实操建议:
- 用
std::chrono::system_clock::now()得到当前时间点(含纳秒级精度) - 调用
std::chrono::system_clock::to_time_t()提取秒数部分,再用std::localtime()转成本地std::tm - 用
std::chrono::duration_cast<:chrono::milliseconds>()</:chrono::milliseconds>单独算出毫秒偏移(需减去秒数部分) - 格式化时,先用
std::strftime()处理年月日时分秒,再拼接三位毫秒数字
手拼字符串时避免 std::tm 的 tm_sec 溢出问题
std::localtime() 返回的 std::tm 结构体里,tm_sec 是 0–61(支持闰秒),但毫秒计算只依赖时间点与 std::time_t 的差值,和 tm_sec 本身无关。真正容易出错的是:有人把毫秒直接加到 tm_sec 上再调用 strftime(),这会导致秒字段非法、格式化失败或返回空字符串。
正确做法是分开处理:
立即学习“C++免费学习笔记(深入)”;
- 先用
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm)得到不含毫秒的时间串 - 单独计算毫秒值:
auto ms = std::chrono::duration_cast<:chrono::milliseconds>(tp.time_since_epoch()) % 1000</:chrono::milliseconds> - 用
sprintf或std::format(C++20)拼接,例如"%s.%03d"
C++20 std::format 简化写法但要注意平台支持
如果你用的是 C++20 且编译器/标准库已实现 std::format(如 GCC 13+、Clang 15+、MSVC 19.30+),可以直接用 std::format("{:%Y-%m-%d %H:%M:%S}.{:03}", std::chrono::current_zone()->to_local(tp), ms) —— 但注意:std::chrono::current_zone() 在某些 libstdc++ 版本中尚未完全支持本地时区解析,可能 fallback 到 UTC。
更稳妥的 C++20 写法是:
- 仍用
std::localtime()+std::strftime()生成主干字符串 - 用
std::format拼接毫秒:std::format("{}.{:03}", time_str, ms.count()) - 避免依赖
std::chrono::zoned_time,它在 macOS 和旧 Linux 上时区数据库路径可能不一致,导致current_zone()返回 null
Windows 下 GetLocalTime 更快但不可移植
Windows API 的 GetLocalTime() 直接返回带毫秒的 SYSTEMTIME,无需手动计算,性能略好且线程安全。但它只在 Windows 生效,Linux/macOS 会编译失败。
如果项目限定 Windows,可以这样写:
- 调用
GetLocalTime(&st),st.wMilliseconds就是 0–999 的毫秒值 - 用
std::snprintf格式化:"%04d-%02d-%02d %02d:%02d:%02d.%03d" - 注意
SYSTEMTIME的月份是 1–12、星期是 0–6,和std::tm一致,但年份是完整四位数(不用加 1900) - 跨平台项目别混用;若要兼容,必须用预处理器隔离:
#ifdef _WIN32...
毫秒部分本质是时间点对齐到秒的余数,和时区转换逻辑正交;最容易被忽略的是:不同系统对“本地时间”的定义依赖运行时环境变量(如 TZ),而 std::localtime 在多线程下不是 always-safe,必要时加锁或改用 std::localtime_r(POSIX)或 localtime_s(Windows)。


















