推荐用 std::chrono 获取毫秒级时间戳:auto now = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast(now.time_since_epoch()).count();

用 std::chrono 获取毫秒级时间戳(推荐)
C++11 起,std::chrono 是最可靠、跨平台的方式。它不依赖系统时区设置,精度高,且避免了 C 风格时间函数的线程安全和可重入问题。
- 直接获取自纪元以来的毫秒数:
auto now = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<:chrono::milliseconds>(now.time_since_epoch()).count();</:chrono::milliseconds>
-
std::chrono::system_clock 对应系统实时时钟,但注意:它不一定单调(可能因 NTP 调整跳变)
- 如果需要单调时钟(比如测间隔),改用
std::chrono::steady_clock,但它不能转成日历时间
- 别用
time(nullptr) + localtime_r 去“反推”毫秒——time_t 通常只精确到秒,毫秒信息已丢失
把时间戳转成可读日期字符串(带毫秒)std::chrono 本身不直接格式化,得先转成 std::time_t,再借助 std::strftime;毫秒部分需单独提取。
- 正确步骤:
auto now = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<:chrono::milliseconds>(now.time_since_epoch()).count();
auto sec = ms / 1000;
auto ms_part = ms % 1000;</:chrono::milliseconds>
auto now = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast<:chrono::milliseconds>(now.time_since_epoch()).count();</:chrono::milliseconds>
std::chrono::system_clock 对应系统实时时钟,但注意:它不一定单调(可能因 NTP 调整跳变)std::chrono::steady_clock,但它不能转成日历时间time(nullptr) + localtime_r 去“反推”毫秒——time_t 通常只精确到秒,毫秒信息已丢失std::chrono 本身不直接格式化,得先转成 std::time_t,再借助 std::strftime;毫秒部分需单独提取。
- 正确步骤:
auto now = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast<:chrono::milliseconds>(now.time_since_epoch()).count(); auto sec = ms / 1000; auto ms_part = ms % 1000;</:chrono::milliseconds>
std::time_t t = sec; std::tm tm_buf{};
ifdef _WIN32
localtime_s(&tm_buf, &t);
else
localtime_r(&t, &tm_buf);
endif
char buf[64]; std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm_buf); std::sprintf(buf + std::strlen(buf), ".%03ld", ms_part); // 注意是 %03ld,不是 %03d
- 常见错误:直接对
std::chrono::system_clock::to_time_t(now)调用localtime,会丢掉毫秒;或忽略 Windows 下localtime_s和 POSIX 下localtime_r的参数差异 -
std::strftime不支持毫秒占位符(如%f),必须手动拼接 -
tm_buf必须显式初始化为零(尤其 Windows),否则tm_isdst等字段可能为垃圾值,导致夏令时判断出错
用 std::format(C++20)简化格式化(如果编译器支持)
C++20 引入了 std::format,能更安全地拼接,但目前主流项目仍需兼容旧标准,且它仍不原生支持毫秒格式化。
- 示例(需开启
-std=c++20):auto now = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<:chrono::milliseconds>(now.time_since_epoch()).count();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm = *std::localtime(&t);
std::string s = std::format("{:%Y-%m-%d %H:%M:%S}.{:03}", tm, ms % 1000);</:chrono::milliseconds>
- 注意:
std::format 的时间格式化依赖 std::locale,默认 locale 下 {:%Y...} 才有效;若 locale 被设为非 C,可能输出非 ASCII 字符(如中文月份)
- GCC 13+、Clang 15+ 支持较完整,MSVC 2019 v16.10+ 开始支持,但
std::format 对 std::tm 的支持在早期版本中仍有 bug(比如年份偏移)
时区陷阱:为什么本地时间总差 8 小时?std::localtime 依赖运行时环境的 TZ 环境变量或系统默认时区。硬编码逻辑里没做时区适配,就容易在容器、CI 或跨地域部署时出错。
- 检查当前时区:
std::cout << std::put_time(std::localtime(&t), "%Z %z") << "\n";
- 如果必须固定 UTC,用
std::gmtime_r(POSIX)或 gmtime_s(Windows)替代 localtime_r/localtime_s
- 更健壮的做法:用第三方库如
date.h(Howard Hinnant’s date library),它支持 zoned_time 和任意时区转换,但引入外部依赖需权衡
- 容器镜像(如 alpine)默认无时区数据,
localtime 可能回退到 UTC,光改代码没用,得挂载 /usr/share/zoneinfo 或设 TZ=Asia/Shanghai
-std=c++20):auto now = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<:chrono::milliseconds>(now.time_since_epoch()).count();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm = *std::localtime(&t);
std::string s = std::format("{:%Y-%m-%d %H:%M:%S}.{:03}", tm, ms % 1000);</:chrono::milliseconds>std::format 的时间格式化依赖 std::locale,默认 locale 下 {:%Y...} 才有效;若 locale 被设为非 C,可能输出非 ASCII 字符(如中文月份)std::format 对 std::tm 的支持在早期版本中仍有 bug(比如年份偏移)std::localtime 依赖运行时环境的 TZ 环境变量或系统默认时区。硬编码逻辑里没做时区适配,就容易在容器、CI 或跨地域部署时出错。
- 检查当前时区:
std::cout << std::put_time(std::localtime(&t), "%Z %z") << "\n";
- 如果必须固定 UTC,用
std::gmtime_r(POSIX)或gmtime_s(Windows)替代localtime_r/localtime_s - 更健壮的做法:用第三方库如
date.h(Howard Hinnant’s date library),它支持zoned_time和任意时区转换,但引入外部依赖需权衡 - 容器镜像(如 alpine)默认无时区数据,
localtime可能回退到 UTC,光改代码没用,得挂载/usr/share/zoneinfo或设TZ=Asia/Shanghai
时间戳和日期转换看着简单,真正上线后出问题的,八成卡在时区、毫秒截断、或跨平台函数调用差异上。别信“一次写完到处跑”,多在目标环境里跑一遍 std::strftime 输出和 std::format 结果对比。










