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

用 std::put_time 格式化 std::tm 时间结构体
这是 C++11 起最标准、最推荐的方式,依赖 <iostream></iostream> 和 <iomanip></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 <h3>用 <code>std::format</code>(C++20)直接格式化 <code>std::chrono::system_clock::time_point</code> </h3><p>C++20 引入的 <code>std::format</code> 支持原生日期时间格式化,无需手动拆解成 <code>std::tm</code>,语义更清晰,且类型安全。但它要求编译器支持 C++20 并启用 <code><format></format></code> 头文件(GCC 13+、Clang 15+、MSVC 19.30+)。</p><p>容易被忽略的是:它只接受 <code>std::chrono::time_point</code> 类型,不能直接传 <code>std::time_t</code> 或 <code>std::tm</code>;而且目前不支持自定义 locale 的格式化(所有输出都是 C locale 行为)。</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill4025" title="C++ 算法竞赛自动化测试数据生成与校验框架"><img src="https://img.php.cn/upload/skill/000/000/081/178988956499722.jpg" alt="C++ 算法竞赛自动化测试数据生成与校验框架" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill4025" title="C++ 算法竞赛自动化测试数据生成与校验框架" class="overflowclass">C++ 算法竞赛自动化测试数据生成与校验框架</a> <p class="overflowclass">根据原题生成新题面、验证器及完整测试数据,自动套用 testlib 模板,用于用户要求生成测试数据时。</p> </div> <a rel="nofollow" href="/xiazai/skill4025" title="C++ 算法竞赛自动化测试数据生成与校验框架" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
- 需先将
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></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 会花掉比写代码多十倍的时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










