最稳妥方案是用 std::strftime 配合线程安全的 std::localtime_r(Linux/macOS)或 std::localtime_s(Windows),先将 long 时间戳 static_cast 为 time_t,再转 struct tm 并格式化;需注意缓冲区大小、时区逻辑、负时间戳兼容性及返回值检查。

用 std::strftime 配合 std::localtime_r(或 std::localtime_s)最稳妥
直接用 std::to_string 只能拿到原始秒数,不是可读时间字符串。必须先转成 struct tm,再格式化。Linux/macOS 用线程安全的 std::localtime_r,Windows 用 std::localtime_s,别用已被弃用的 std::localtime(有静态缓冲区风险)。
常见错误是传入未初始化的 struct tm 或忽略时区转换逻辑——long 是 UTC 秒数,localtime_* 才会按本地时区调整。
-
long值需先转为time_t(通常就是long,但显式强转更安全:static_cast<time_t>(ts)</time_t>) - 目标缓冲区至少 64 字节,避免
strftime截断 - 格式字符串如
"%Y-%m-%d %H:%M:%S",注意%z在某些平台不输出时区偏移
char buf[64];
time_t t = static_cast<time_t>(unix_ts);
struct tm tm_buf;
#ifdef _WIN32
localtime_s(&tm_buf, &t);
#else
localtime_r(&t, &tm_buf);
#endif
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm_buf);
std::string result(buf);
想严格 UTC 不带本地时区偏移?用 std::gmtime_r / std::gmtime_s
如果输入 long 是标准 Unix 时间戳(即 UTC),且你希望输出也保持 UTC(比如日志统一用 UTC),就不能用 localtime_*,否则在夏令时或跨时区机器上结果会漂移。
gmtime_* 系列函数不查系统时区,直接解析为 UTC 时间,行为确定。Windows 下用 std::gmtime_s,其他平台用 std::gmtime_r。
立即学习“C++免费学习笔记(深入)”;
- UTC 时间字符串末尾加
"Z"要手动拼接,strftime的%z输出的是 +0000 这种,不是标准 Z -
gmtime_*返回的struct tm中tm_isdst恒为 0,别依赖它判断夏令时 - 若需 ISO 8601 格式(如
2024-05-20T13:45:30Z),建议用 C++20 的<chrono>,但老项目仍得靠strftime拼
C++20 <chrono> 方案:类型安全但需注意精度和时区
如果你的编译器支持 C++20 且能用 <chrono>,推荐走 std::chrono::system_clock::time_point 路线,它天然适配 Unix 时间戳,还能自动处理纳秒级精度(只要输入 long 是秒数,就用 std::chrono::seconds 构造)。
关键点在于:C++20 的 std::format 目前对时间格式支持有限(GCC 13+、Clang 17+ 才较完整),多数场景仍要回退到 std::put_time + std::ostringstream,而后者底层还是调 strftime。
- 构造 time_point 时别错用
std::chrono::milliseconds——除非你的long是毫秒单位 -
std::put_time默认用本地时区;要 UTC,得先用std::chrono::utc_clock转换(C++20),或继续用gmtime_* - Windows 上
std::put_time对宽字符支持不稳定,坚持用窄字符和char缓冲区更省心
容易被忽略的边界情况:负时间戳、32 位溢出、空指针
Unix 时间戳为负值(1970 年前)时,localtime_r/gmtime_r 在大多数 POSIX 系统上仍能工作,但 Windows 的 localtime_s 对负值行为未明确保证,测试时务必覆盖 -1、-86400 等值。
32 位 long 在 2038 年问题下会溢出,虽然现在少见,但如果接收外部输入,应检查是否超出 time_t 范围(可用 std::numeric_limits<time_t>::max() 判断)。
- 所有
_r/_s函数第二个参数不能为nullptr,否则未定义行为 -
strftime返回 0 表示缓冲区不够或格式非法,必须检查返回值,不能只看buf内容 - 跨平台代码中,
time_t在 32 位系统上可能是 32 位整型,long却可能是 64 位(如 Windows LLP64),强制转换前确认位宽匹配
真正麻烦的从来不是“怎么转”,而是“转完是不是我想要的那个时刻”——尤其在部署到不同时区、不同架构的机器上时,本地时区、time_t 位宽、负时间戳处理这三点,漏一个就可能线上出错。


















