优先用 std::put_time,它类型安全、可读性好、支持流操作;strftime 仅在需兼容旧代码或写入 C 风格缓冲区时使用。

直接说结论:优先用 std::put_time,它类型安全、可读性好、支持流操作;strftime 仅在需要兼容旧代码、或必须写入 C 风格缓冲区时才用。
为什么 std::put_time 是首选
它直接操作 std::tm 和流,不用手动管理缓冲区大小,也不会因长度预估错误导致截断或溢出。C++11 起就已标准化,主流编译器(GCC 5+、Clang 3.5+、MSVC 2015+)都完整支持。
常见错误是传入空指针或未初始化的 std::tm:
-
std::tm必须由std::localtime或std::gmtime返回,不能栈上直接声明后裸用 - 传给
std::put_time的指针不能为nullptr,否则流输出失败(os.fail()为 true) - 格式字符串必须是窄字符字面量(
"%Y-%m-%d %H:%M:%S"),宽字符(L"...")不被支持
示例:
立即学习“C++免费学习笔记(深入)”;
auto now = std::time(nullptr);
auto tm_ptr = std::localtime(&now); // 注意:返回值非 nullptr 才安全
if (tm_ptr) {
std::cout << std::put_time(tm_ptr, "%Y-%m-%d %H:%M:%S") << '\n';
}
strftime 的典型使用场景和坑
它仍是 C 标准库函数,适用于需要写入固定大小缓冲区、或与 C API 交互的场合(比如日志系统底层封装)。但极易因缓冲区太小而静默截断——strftime 返回的是“本应写入的字符数”,不是实际写入数。
关键点:
- 目标缓冲区必须足够大,推荐至少
256字节(含\0) - 调用后要检查返回值:
0表示缓冲区不足或格式非法,不是成功 - 格式说明符和
std::put_time基本一致,但部分扩展(如%q)不可移植 - 线程不安全:内部可能使用静态
struct tm,多线程下需用strftime_s(MSVC)或自己加锁
示例:
立即学习“C++免费学习笔记(深入)”;
char buf[256];
auto now = std::time(nullptr);
auto tm_ptr = std::localtime(&now);
size_t written = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm_ptr);
if (written == 0 || written >= sizeof(buf) - 1) {
// 处理失败:缓冲区不够或格式错
}时区与 UTC 的关键区别
std::localtime 和 std::gmtime 返回的 std::tm 结构体本身不带时区信息,只是本地时间或 UTC 时间的分解表示。误用会导致时间偏移:
- 想输出 UTC 时间,必须用
std::gmtime+std::put_time,不能只改格式串 -
std::put_time不解析时区,它只按传入的std::tm成员字段原样格式化 - C++20 引入了
时区支持(zoned_time),但目前仍需手动转换,put_time无法直接消费std::chrono::system_clock::time_point
所以最稳妥的 UTC 输出写法是:
auto now = std::time(nullptr);
auto utc_tm = std::gmtime(&now);
if (utc_tm) {
std::cout << std::put_time(utc_tm, "%Y-%m-%d %H:%M:%S UTC") << '\n';
}格式化字符串里容易忽略的细节
看似简单的 "%Y-%m-%d",其实有隐含行为:
-
%Y输出 4 位年份,%y是 2 位;用错会导致 2024 显示成24 -
%m和%d默认补零(01–12 / 01–31),但%_m(GNU)或%-m(MSVC)会去零——跨平台时避免使用 -
%H是 24 小时制,%I是 12 小时制;混用会导致下午时间显示异常 -
%Z输出时区缩写(如 PST),但依赖系统 locale 和tzset(),不可靠;%z输出 UTC 偏移(如+0800),更稳定
真正跨平台安全的常用组合是:"%Y-%m-%d %H:%M:%S%z" —— 它明确、无歧义、不依赖 locale。










