<p>std::put_time 是 C++11 起提供的类型安全时间格式化工具,需配合 std::localtime 或 std::gmtime 将 std::time_t 转为 std::tm* 后使用,输出受流 locale 影响。</p>

用 std::put_time 格式化本地时间最直接
标准库从 C++11 起就提供了 std::put_time,配合 std::localtime 或 std::gmtime 就能完成常见时间格式化。它比手拼字符串安全,也比旧式 strftime 更类型安全。
关键点:必须传入 std::tm*,且不能直接对 std::time_t 调用;输出依赖流的 locale(默认通常没问题)。
常见错误现象:std::put_time(t, "%Y-%m-%d") 编译失败——因为 t 是 std::time_t,不是 std::tm*。
- 先调用
std::time(&t)获取当前时间戳 - 再用
std::localtime(&t)转成std::tm*(注意:返回值是静态缓冲区指针,多线程下需用std::localtime_r或封装) - 用
std::cout
示例:
立即学习“C++免费学习笔记(深入)”;
std::time_t t = std::time(nullptr);
std::tm tm_buf{};
#ifdef _WIN32
localtime_s(&tm_buf, &t);
auto* tm_ptr = &tm_buf;
#else
auto* tm_ptr = std::localtime(&t); // POSIX
#endif
std::cout << std::put_time(tm_ptr, "%Y-%m-%d %H:%M:%S") << '\n';
跨平台处理 std::localtime 线程安全问题
std::localtime 返回指向静态内存的指针,在多线程中可能被覆盖。Windows 和 Linux 行为不一致,容易引发偶发性时间错乱。
使用场景:服务端程序、并发日志、定时器回调中调用时间格式化时必须考虑这点。
- Linux/macOS 推荐用
std::localtime_r(&t, &tm_buf),把结果写入用户提供的std::tm实例 - Windows 用
localtime_s(&tm_buf, &t)(注意参数顺序相反) - 封装一个 inline 函数统一处理,避免散落条件编译
性能影响几乎为零,但能彻底避开竞态。
自定义格式字符串里这些转义要小心
std::put_time 的格式符和 C 的 strftime 完全一致,但有几个高频踩坑点:
-
%y是两位年份(24),%Y是四位(2024)——日志归档时混用会导致路径错乱 -
%H是 24 小时制,%I是 12 小时制,漏掉%p会丢失 AM/PM -
%z输出时区偏移(如 +0800),但 Windows 默认 locale 下可能为空或报错;需要显式设置std::locale("C")或改用%Z(时区缩写) - 中文 Windows 下若未设置 locale,
%c可能输出乱码,建议固定用 ASCII 格式如"%Y/%m/%d %H:%M"
不需要 <chrono> 也能做毫秒级时间戳
如果只要“当前时间 + 毫秒”,不必动用 std::chrono 那套复杂时钟转换。用 std::time + std::clock_gettime(Linux/macOS)或 GetSystemTimeAsFileTime(Windows)更轻量。
但注意:std::put_time 不支持毫秒,得手动拼接:
- 先用
std::localtime格式化到秒级 - 再用高精度 API 获取毫秒部分(例如
std::chrono::steady_clock::now().time_since_epoch().count() % 1000) - 最后组合输出:
std::cout
这个组合在日志系统里很常见,但要注意 std::setw 和 std::setfill 是流状态,会影响后续输出,记得重置或限定作用域。
实际用起来最麻烦的往往不是格式本身,而是线程安全那块静态缓冲区,还有 Windows 和 POSIX 对 localtime 变体的参数顺序差异。写一次封装函数,后面就省心了。


















