std::put_time是C++11起推荐的类型安全日期格式化方法,需配合std::ostringstream使用,先将std::time_t转为std::tm并检查非空,再imbue locale后传const std::tm给std::put_time。

直接说结论:在 C++ 中格式化日期为字符串,std::put_time 是现代、类型安全、推荐的首选;_strftime(或标准 strftime)是 C 风格、需手动管理缓冲区、容易出错但兼容性极广——选哪个,取决于你是否能用 C++11 以上且不需支持老旧环境。
std::put_time 要怎么写才不崩溃?
std::put_time 本身不生成字符串,它只是个流操纵器,必须配合 std::ostringstream 使用。常见错误是直接传 std::time_t 或忘记 imbue locale:
- 必须先用
std::localtime或std::gmtime把std::time_t转成std::tm*,再解引用(注意检查是否为空) - 输出流需提前
imbue一个 locale,否则某些平台(如 Windows MSVC)可能抛std::runtime_error:“locale not supported” - 格式符用
%Y、%m等,和strftime一致,但必须传const std::tm*,不能传std::tm值
std::time_t now = std::time(nullptr);
std::tm tm_buf {};
if (auto* ptm = std::localtime(&now)) {
tm_buf = *ptm; // 复制,避免多线程问题
} else {
throw std::runtime_error("localtime failed");
}
std::ostringstream oss;
oss.imbue(std::locale("")); // 使用系统默认 locale
oss << std::put_time(&tm_buf, "%Y-%m-%d %H:%M:%S");
std::string s = oss.str(); // "2024-06-15 14:23:07"
strftime 为什么总写到一半就截断或乱码?
strftime 要求目标缓冲区大小精确可控,且返回值是实际写入字符数(不含结尾 \0),不是成功/失败标志。常见坑:
- 传入缓冲区太小:比如用
char buf[10]去存"2024-06-15"(10 字节 +\0→ 至少 11 字节),结果被截断 - 忽略返回值:返回 0 表示“缓冲区不够或格式非法”,但很多人只检查指针是否为空
- Windows 下用
_strftime(带下划线)是微软扩展,行为与标准strftime一致,但头文件仍是<ctime></ctime>,无需额外宏
std::time_t now = std::time(nullptr);
std::tm* ptm = std::localtime(&now);
if (!ptm) return;
char buf[64];
size_t len = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ptm);
if (len == 0) {
// 缓冲区不足 或 格式串非法(如 %z 在旧 libc 不支持)
}
std::string s(buf); // 安全:strftime 保证 null-terminated
格式符差异:哪些在 put_time 和 strftime 里表现不一致?
绝大多数通用格式符(%Y、%m、%d、%H 等)行为一致,但以下几点要注意:
立即学习“C++免费学习笔记(深入)”;
-
%z(UTC 偏移,如 +0800):Linux glibc 支持;MSVC 的_strftime也支持;但std::put_time在 C++20 前不保证支持,部分标准库(如 libstdc++ 12 之前)会静默忽略 -
%Z(时区缩写,如 CST):依赖系统 locale 数据,跨平台输出不稳定;put_time和strftime都可能返回空字符串 -
%%:两者都表示字面量%,无差异 - 宽度修饰(如
%04Y):strftime不支持;put_time也不支持——C++20 引入std::format才有真正格式控制
性能和可移植性怎么权衡?
单次调用性能差异可忽略,但长期维护成本差别大:
-
put_time:类型安全,自动管理内存,异常安全,但依赖 locale 设置;若 locale 构造失败(如容器中 locale="" 无效),运行时报错 -
strftime:零依赖,纯 C 函数,嵌入式或最小化构建必选;但需手工算缓冲区大小,tm结构体非线程安全(localtime返回静态内存) - 替代方案:C++20 的
std::format("{:%Y-%m-%d}", std::chrono::system_clock::now())更简洁安全,但目前编译器支持度有限(GCC 13+ / Clang 15+)
真正容易被忽略的是时区:无论用哪个函数,std::localtime 和 std::gmtime 返回的 tm 都不带时区信息,%z 实际靠 timezone 全局变量或系统 API 推导——这意味着多线程下改 tzset() 可能影响所有调用。



















