std::put_time是C++11起推荐的std::tm格式化方式,需配合locale使用;std::format(C++20)支持time_point直接格式化,类型安全但依赖编译器支持;跨平台建议用strftime兜底,注意线程安全的时间转换函数选择。

用 std::put_time 格式化 std::tm 时间结构体
这是 C++11 起最标准、最推荐的方式,依赖 <iostream> 和 <iomanip>。它不直接操作原始时间戳,而是基于已转换好的 std::tm 结构体(比如从 std::time_t 用 std::localtime 或 std::gmtime 转换而来)。
常见错误是传入空指针或未初始化的 std::tm,导致程序崩溃或输出乱码;另一个坑是忘记设置流的 locale(尤其在 Windows 上默认 locale 不支持 %Y 等宽字符格式符)。
- 必须先调用
std::locale::global(std::locale(""))或为流单独 imbue locale,否则某些格式符(如%A、%B)可能输出空字符串 - 格式字符串用
std::put_time的第二个参数传入,例如"%Y-%m-%d %H:%M:%S" -
std::put_time是流操纵器,只能用于<<操作,不能单独调用
std::time_t now = std::time(nullptr); std::tm* t = std::localtime(&now); // 注意:返回静态缓冲区指针,线程不安全 std::cout << std::put_time(t, "%Y-%m-%d %H:%M:%S") << '\n';
用 std::format(C++20)直接格式化 std::chrono::system_clock::time_point
C++20 引入的 std::format 支持原生日期时间格式化,无需手动拆解成 std::tm,语义更清晰,且类型安全。但它要求编译器支持 C++20 并启用 <format> 头文件(GCC 13+、Clang 15+、MSVC 19.30+)。
容易被忽略的是:它只接受 std::chrono::time_point 类型,不能直接传 std::time_t 或 std::tm;而且目前不支持自定义 locale 的格式化(所有输出都是 C locale 行为)。
立即学习“C++免费学习笔记(深入)”;
- 需先将
std::time_t转为std::chrono::system_clock::time_point,用std::chrono::system_clock::from_time_t() - 格式说明符和
strftime兼容,但必须用{:%Y-%m-%d}这种大括号包裹语法 - 若编译失败提示 “no matching function for call to format”,大概率是没开 C++20 或未链接 libstdc++ 的 format 实现(GCC 需加
-lstdc++fs)
auto now = std::chrono::system_clock::now();
std::string s = std::format("{:%Y-%m-%d %H:%M:%S}", now); // 输出如 "2024-06-12 14:23:05"跨平台兼容:当 std::put_time 在 Windows 上输出为空时怎么办
Windows 默认的 C++ 标准库实现(尤其是 MSVC 的旧版本)对 std::put_time 的 locale 支持不完整,即使设置了全局 locale,%A、%B 等仍可能输出空。这不是 bug,而是实现差异。
最稳妥的绕过方式不是硬改 locale,而是降级使用 C 风格的 std::strftime —— 它在所有平台都稳定,且行为一致。
- 先用
std::strftime写入固定大小的char[]或std::vector<char>缓冲区 - 注意目标缓冲区必须足够大(建议至少 256 字节),否则截断不报错
- 返回值是实际写入字节数(不含末尾
\0),若为 0 表示缓冲区太小或格式非法
std::time_t now = std::time(nullptr);
std::tm t;
#ifdef _WIN32
localtime_s(&t, &now); // Windows 安全版
#else
localtime_r(&now, &t); // POSIX 线程安全版
#endif
char buf[256];
if (std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &t)) {
std::string s(buf);
}性能与线程安全的关键细节
格式化本身开销不大,但背后的时间转换函数(std::localtime、std::gmtime)在多数实现中返回静态缓冲区指针,多线程下会互相覆盖 —— 这是线上服务偶发时间错乱的常见根源。
- Linux/macOS 优先用
std::localtime_r(POSIX),Windows 用localtime_s(MSVC),它们把结果写入用户提供的std::tm*缓冲区 -
std::put_time和std::format本身是纯函数式,无状态,线程安全;问题全出在上游时间转换环节 - 如果只是输出 UTC 时间,用
std::gmtime/gmtime_r更轻量,避免时区计算开销
真正麻烦的从来不是“怎么写格式串”,而是“谁负责分配和管理 std::tm 内存”以及“要不要考虑夏令时跳变”。这些点不提前想清楚,上线后 debug 会花掉比写代码多十倍的时间。


















