C++20 推荐用 std::chrono::floor + std::chrono::utc_time 配合 std::format 安全格式化;兼容 C++11 则用 std::time + 线程安全的 localtime_r/gmtime_r + std::strftime,并显式指定 UTC 时区避免偏差。

用 std::chrono + std::format(C++20)最简洁安全
如果你的编译器支持 C++20 且启用了 std::format(如 MSVC 19.32+、GCC 13+、Clang 15+),这是目前最推荐的方式:类型安全、无缓冲溢出风险、格式清晰。
常见错误是直接传 std::chrono::system_clock::now() 给 std::format —— 它不接受时间点,必须先转成 std::chrono::time_point 对应的 std::chrono::seconds 或用 std::chrono::utc_time 配合 std::chrono::floor。
- 先用
std::chrono::system_clock::now()获取当前时间点 - 用
std::chrono::floor<:chrono::seconds>(tp)</:chrono::seconds>截断到秒级(避免毫秒导致格式化失败) - 再通过
std::chrono::zoned_time{"UTC", tp}或本地时区转换为可格式化的std::chrono::sys_time - 示例:
auto now = std::chrono::system_clock::now(); auto sec = std::chrono::floor<std::chrono::seconds>(now); std::string s = std::format("{:%Y-%m-%d %H:%M:%S}", std::chrono::utc_time{sec});
兼容 C++11 的方案:std::time + std::strftime
绝大多数项目仍需兼容旧标准,这时得靠 std::time 和 std::strftime。关键陷阱在于:忘记初始化 std::tm 结构体,或忽略 std::localtime_r/std::gmtime_r 的线程安全性问题。
Windows 下没有 _r 版本函数,必须用 localtime_s(带下划线前缀),而 Linux/macOS 用 localtime_r;混用会导致编译失败或运行时崩溃。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 始终用
std::memset(&tm, 0, sizeof(tm))清零std::tm,防止未定义行为 - 优先用线程安全版本:
localtime_r(&t, &tm)(POSIX)或localtime_s(&tm, &t)(MSVC) - 格式字符串中
%Y是 4 位年份,%y是 2 位;%H是 24 小时制,%I是 12 小时制 - 示例(POSIX):
std::time_t t; std::time(&t); std::tm tm{}; std::localtime_r(&t, &tm); char buf[64]; std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
避免 std::asctime 和 std::ctime 的硬编码格式
这两个函数返回固定格式字符串(如 "Sun Sep 15 16:20:32 2024\n"),不可控、难解析、含换行符,几乎无法用于日志或 API 输出。
尤其注意 std::ctime 返回的是指向静态缓冲区的指针,多次调用会覆盖内容;在多线程中直接使用极易引发竞态。
- 永远不要把
std::ctime结果赋给多个变量后比较内容——它们可能指向同一块内存 - 如果只是临时调试打印,可以接受;但任何生产代码都应绕过它
- 替代方案:哪怕只用
std::strftime写一行"%F %T"也比它强
时区处理是最大盲区,别默认用本地时间
几乎所有上述方法默认用本地时区(由系统环境决定),但服务端日志、跨时区协作、数据库写入通常需要 UTC。不显式指定时区,上线后可能发现时间比实际快/慢几小时,排查成本极高。
C++20 前没有标准 UTC 转换,只能靠 std::gmtime(即 UTC 时间),但要注意它返回的是非线程安全的静态结构体指针。
- 用
std::gmtime_r(&t, &tm)(POSIX)或gmtime_s(&tm, &t)(MSVC)代替std::gmtime - C++20 中优先用
std::chrono::utc_time而不是std::chrono::zoned_time{"UTC", ...},后者涉及时区数据库加载开销 - 日志记录务必注明时区,比如写成
"2024-09-15T08:30:45Z"(末尾Z表示 UTC)
真正麻烦的不是怎么取日期,而是取完之后没意识到它带有时区含义,也没在上下游约定好统一时区。这点比格式本身更值得花时间确认。

















