推荐用 std::chrono::system_clock::now() 配合 std::gmtime_r(posix)或 std::gmtime_s(msvc)转时间,显式 imbue std::locale("c") 后用 std::put_time 格式化;windows 下建议降级使用 strftime 以规避 msvc 实现缺陷。

用 std::chrono + std::put_time 生成可读时间戳
直接拼接字符串或调用 time() + localtime() 容易出时区/线程安全问题,std::chrono 是现代 C++ 的推荐起点。关键不是“获取时间”,而是“可控、无副作用地转成字符串”。std::put_time 配合 std::ostringstream 能稳定输出格式化结果,且不依赖全局 locale(避免多线程下被意外覆盖)。
实操建议:
- 每次格式化前显式设置 locale:
oss.imbue(std::locale("C")),防止系统 locale 影响数字/分隔符(比如某些 locale 用点号代替冒号) - 用
std::chrono::system_clock::now()获取时间点,转成std::time_t再用std::gmtime或std::localtime—— 注意后者非线程安全,优先选std::gmtime_r(POSIX)或std::gmtime_s(MSVC) - 格式串用
"%Y-%m-%d %H:%M:%S"最稳妥;加毫秒需手动计算:ms = duration_cast<milliseconds>(tp.time_since_epoch()).count() % 1000</milliseconds>
封装成线程安全的 log_timestamp() 函数
日志函数频繁调用,不能每次 new locale 或锁全局 time_t 缓冲区。常见错误是把 std::localtime 返回的静态结构体指针直接存起来复用,导致多线程下数据错乱。
实操建议:
- 用
thread_local std::tm buffer{}+std::gmtime_r(&t, &buffer)避免分配和锁 - 毫秒部分别用
std::chrono::steady_clock—— 它不保证对应 wall-clock,但差值稳定;真正要对齐系统时间,必须用system_clock - 返回
std::string比const char*更安全,避免悬挂指针;若性能敏感,可预分配 buffer(如char buf[64])+strftime
跨平台兼容性陷阱:Windows 下 std::put_time 可能失效
MSVC 2015–2019 对 std::put_time 支持不全,尤其在宽字符或非"C" locale 下常返回空字符串。这不是你代码写错了,是标准库实现缺陷。
实操建议:
- Windows 上直接降级用
strftime:strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm),它稳定且 POSIX/Win32 都支持 - 避免依赖
std::put_time的流操作符重载;统一走char[]+strftime,再构造std::string - 若必须用
std::put_time,先测试:std::ostringstream().imbue(std::locale("C")) 是否输出 "2024"
性能关键:避免每次日志都重新格式化
高频日志(如每毫秒一条)下,strftime 或 std::put_time 的格式解析开销明显。有人尝试缓存整条时间字符串,但会引入“时间戳滞后”问题(比如缓存 1 秒,实际日志时间漂移)。
实操建议:
- 按秒缓存:只在秒变化时更新缓存的
"YYYY-MM-DD HH:MM:SS"字符串,毫秒部分单独拼接(ss.str() + '.' + std::to_string(ms)) - 用
std::atomic<:time_t></:time_t>记录上一次更新的秒数,比锁快一个数量级 - 真正极致场景(如网络包逐包打日志),考虑用无锁环形缓冲 + 后台线程批量格式化,但复杂度陡增,多数情况没必要
std::put_time 的静默失败,以及缓存策略导致的时间漂移。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











