time()线程安全,但localtime()/gmtime()因共用静态缓冲区而不安全;应改用localtime_s、localtime_r或C++11的std::chrono::system_clock::now()。

time() 本身是线程安全的
time() 函数只读取系统时钟并写入你传入的 time_t* 指针(或返回值),不依赖任何共享静态缓冲区。POSIX 和 C++ 标准都明确要求它为异步信号安全且线程安全。你在多个线程里并发调用 time(nullptr) 不会崩溃、不会覆盖、也不会返回错乱值。
但紧跟着用 localtime() 就立刻不安全了
问题几乎总出在后续操作上:一旦你拿到 time_t 值,立刻调用 localtime() 或 gmtime(),就踩进雷区了。这两个函数返回指向内部静态 struct tm 的指针,所有线程共用同一块内存:
- 线程 A 调用
localtime(&t1),结果存进静态缓冲区 - 线程 B 紧接着调用
localtime(&t2),把 A 的结果直接覆盖掉 - 线程 A 再去读
tm->tm_hour,实际拿到的是 B 的时间字段
这种竞态不是偶尔发生——只要并发调用频率够高,必现。Visual Studio 会报 C4996 警告,Linux 下用 localtime_r() 才能避开。
替代方案要分场景选
不升级 C++ 标准?用平台安全版:
立即学习“C++免费学习笔记(深入)”;
- Windows:改用
localtime_s(&tm_out, &t),显式传入你自己的struct tm变量地址 - Linux/macOS:用
localtime_r(&t, &tm_out),第二个参数是你提供的缓冲区
能用 C++11 及以上?直接切 std::chrono::system_clock::now():
- 返回
time_point,类型安全,无静态缓冲区 - 转换本地时间需配合
std::put_time(C++11)或std::format(C++20) - 注意:
std::chrono::system_clock通常对应 UTC,转本地需手动处理时区偏移(std::chrono::current_zone()是 C++20 才有)
容易被忽略的细节
即使你用了 localtime_s 或 localtime_r,仍要小心两件事:
-
tm_year是距 1900 年的偏移,tm_mon是 0–11,这些字段没变,该加的还得加,别以为换了函数就自动修正 - “本地时区”由进程启动时的
TZ环境变量决定,运行中改TZ不生效,localtime_s同样不感知


















