std::chrono + std::format(C++20)最干净;localtime() 返回指针指向内部静态缓冲区,多次调用会覆盖,多线程下不安全,应立即深拷贝或改用localtime_r()/localtime_s(),且其“本地时区”由程序启动时TZ决定。

直接用 std::chrono + std::format(C++20)最干净;若只能用 C 风格时间 API,time() + localtime() 可行但必须注意线程安全和时区缓存问题。
为什么 localtime() 返回的指针不能长期保存
localtime() 内部使用静态缓冲区,每次调用都会覆盖前一次结果。多线程下更危险——两个线程同时调用会相互污染。
常见错误写法:
tm* t1 = localtime(&t1_sec); tm* t2 = localtime(&t2_sec); // t1 现在可能已失效
正确做法是立即拷贝结构体内容:
立即学习“C++免费学习笔记(深入)”;
- 用
tm t_copy = *localtime(&t_sec);深拷贝 - 或改用线程安全的
localtime_r()(POSIX,Linux/macOS)或localtime_s()(Windows) - C++20 起推荐绕过它,用
std::chrono::system_clock::now()+std::chrono::current_zone()
time() 和 localtime() 的典型组合用法
time() 获取的是自 1970-01-01 00:00:00 UTC 的秒数(time_t),localtime() 把它转成本地时区的 tm 结构体——但这个“本地”取决于程序启动时的环境变量(如 TZ),运行中修改 TZ 不会自动生效。
基础示例(仅作演示,非线程安全):
time_t now;
time(&now);
tm* lt = localtime(&now);
printf("%d-%02d-%02d", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday);
关键细节:
-
tm_year是距 1900 年的偏移,不是年份本身 -
tm_mon是 0–11,不是 1–12 -
tm_wday周日为 0,周一为 1 - 所有字段都需手动加偏移或补零,容易出错
C++20 推荐替代方案:不用 time() 和 localtime()
std::chrono + std::format 更类型安全、无静态缓冲区、支持时区感知:
auto now = std::chrono::system_clock::now();
auto zoned = std::chrono::zoned_time{std::chrono::current_zone(), now};
auto local = zoned.get_local_time();
std::cout << std::format("{:%Y-%m-%d}", local);
若编译器不支持 C++20,可用 std::put_time(C++11 起)配合 localtime 的拷贝:
time_t t = time(nullptr); tm lt = *localtime(&t); // 注意这里是拷贝 std::cout << std::put_time(<, "%Y-%m-%d");
注意:std::put_time 仍依赖 localtime 的行为,且格式字符串语法和 strftime 一致,但不处理时区切换。
真正麻烦的从来不是“怎么取日期”,而是“怎么保证它在多线程、跨时区、长期运行时不悄悄错掉”。别让 localtime 的静态缓冲区成为你线上服务里那个查不出的偶发 bug。










