最干净的方式是用 std::chrono + std::format(C++20):先用 system_clock::now() 获取时间点,转为毫秒精度后通过 zoned_time 转本地时区,再用 {:%Y-%m-%d %H:%M:%S}.{:3} 格式化。

用 std::chrono + std::format(C++20)最干净
如果你的编译器支持 C++20 且启用了 std::format(如 MSVC 19.3x、GCC 13+、Clang 15+),这是目前最简洁、类型安全、无手动缓冲管理的方式。
关键点:必须用 std::chrono::system_clock::now() 获取时间点,再转换为本地时区(std::chrono::current_zone()),否则默认是 UTC;毫秒需从 time_point 中显式截取到毫秒精度。
-
std::format的格式字符串中{:%Y-%m-%d %H:%M:%S}控制年月日时分秒,.{:3}单独格式化毫秒(补零到 3 位) - 完整示例:
auto now = std::chrono::system_clock::now(); auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now); auto local = std::chrono::zoned_time{std::chrono::current_zone(), ms}; std::string s = std::format("{:%Y-%m-%d %H:%M:%S}.{:3}", local.get_local_time(), ms.time_since_epoch().count() % 1000); - 注意:GCC 13 默认未启用
std::format,需加编译选项-D_GLIBCXX_USE_CXX11_ABI=1 -std=c++20并确认 libstdc++ 版本足够新
兼容 C++11 的方案:用 std::chrono + std::put_time
std::put_time 不支持毫秒,所以得拆解处理:先用 std::time_t 部分走 std::put_time 格式化,再单独提取毫秒拼接。
- 错误常见于直接对
system_clock::now()调用std::time_t转换——会丢掉亚秒部分,必须先 cast 到毫秒精度再分离 - 正确步骤:
auto now = std::chrono::system_clock::now(); auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now); auto time_t_part = std::chrono::system_clock::to_time_t(ms); auto ms_part = ms.time_since_epoch().count() % 1000; std::stringstream ss; ss << std::put_time(std::localtime(&time_t_part), "%Y-%m-%d %H:%M:%S") << '.' << std::setfill('0') << std::setw(3) << ms_part; -
std::localtime是线程不安全的,多线程下应改用std::localtime_r(POSIX)或std::localtime_s(MSVC)
为什么不用 ctime 或 asctime
这两个函数返回的是固定格式(如 "Wed Dec 31 19:00:00 1969\n"),无法插入毫秒,也不支持自定义分隔符或精度控制。
立即学习“C++免费学习笔记(深入)”;
-
ctime(&t)返回带换行符的字符串,末尾有\n,直接拼接毫秒会变成"2024-04-05 14:23:11\n.123" - 它们只接受
time_t,天然丢失毫秒信息;即使你先减去毫秒再传入,也无法还原回原格式中的毫秒位置 - 已废弃(C++20 标记为 [[deprecated]]),现代代码应避免
Windows 下用 GetLocalTime 的陷阱
Win32 API 的 GetLocalTime 确实返回含毫秒的 SYSTEMTIME,但要注意它不经过时区数据库,仅基于系统当前时区设置,且无法处理夏令时自动切换等复杂逻辑。
-
wMilliseconds字段是 0–999,但SYSTEMTIME没有纳秒字段,精度上限就是毫秒 - 格式化仍需自己写:用
printf或std::format拼接,例如"%04d-%02d-%02d %02d:%02d:%02d.%03d" - 跨平台代码中混用 Win32 API 会破坏可移植性;若必须用,建议封装在
#ifdef _WIN32内,并注意SYSTEMTIME的月份和星期从 1/0 开始,易错
std::localtime 和 std::gmtime 这类全局静态缓冲区函数,在并发场景下可能返回被覆盖的时间数据。


















