应使用 std::chrono::high_resolution_clock 获取真正微秒级时间戳,因其精度高且跨平台;再通过 duration_cast 截断到微秒粒度并拆分为秒与微秒两部分,避免假精度和负数取模问题。

为什么 std::chrono::system_clock 默认不满足微秒精度需求
直接用 std::chrono::system_clock::now().time_since_epoch().count() 得到的是纳秒级数值,但实际分辨率取决于系统——Linux 上通常能到微秒或更好,Windows 上 system_clock 在旧版本(如 VS2015)可能只到毫秒级,且 system_clock 还受系统时钟调整影响,不适合做高精度日志对齐。
真正稳定输出微秒时间戳,得用 std::chrono::steady_clock 或更高精度的 std::chrono::high_resolution_clock(后者通常是前两者的别名,但语义更明确),再手动截断或格式化到微秒单位。
-
steady_clock单调、无跳变,适合间隔测量;high_resolution_clock优先选最高可用精度,日志时间戳推荐用它 - 不要依赖
time_t+gettimeofday或clock_gettime(CLOCK_REALTIME, ...)—— 虽然能到微秒,但跨平台封装成本高,C++11+ 有更统一的方案 - 微秒不是“显示小数点后6位”就行:得确保底层计时器真能分辨微秒,否则补零只是假精度
如何用 std::chrono 提取微秒级时间戳(含秒和微秒两部分)
最实用的做法是把时间点拆成“自纪元起的秒数”和“该秒内的微秒偏移”,这样既可读又易格式化。关键不是靠 duration_cast 强转,而是用 floor 截断到微秒粒度再分解。
auto now = std::chrono::high_resolution_clock::now(); auto us = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()); auto seconds = std::chrono::duration_cast<std::chrono::seconds>(us); auto micros = us - seconds; // 0–999999
-
seconds.count()给出 Unix 时间戳整数部分(秒) -
micros.count()是该秒内第多少微秒(0–999999),不是纳秒除1000——避免整数除法截断误差 - 别用
std::chrono::duration_cast<:chrono::microseconds>(...).count() % 1000000</:chrono::microseconds>,在负时间(极少见)下取模行为不可靠
格式化输出时避免 std::put_time 的精度陷阱
std::put_time 只支持秒级和毫秒级(%S 和 %f),没有原生微秒占位符。硬塞 %f 输出的是毫秒(3位),不是微秒(6位)。
立即学习“C++免费学习笔记(深入)”;
正确做法是先用 std::gmtime 或 std::localtime 解析秒数,再拼接微秒字段:
std::time_t t = seconds.count();
std::tm tm_buf;
#ifdef _WIN32
localtime_s(&tm_buf, &t);
#else
localtime_r(&t, &tm_buf);
#endif
std::ostringstream oss;
oss << std::put_time(&tm_buf, "%Y-%m-%d %H:%M:%S.")
<< std::setfill('0') << std::setw(6) << micros.count();
// 输出形如:2024-05-22 14:32:18.123456
- 务必检查
localtime_r/localtime_s的线程安全性,多线程日志必须用可重入版本 -
std::setw(6)+std::setfill('0')确保微秒恒为6位,避免123输出成.123而非.000123 - 如果只要 UTC 时间,用
std::gmtime替代localtime,避免时区转换开销
性能敏感场景下怎么减少日志时间戳开销
每次打日志都调用 high_resolution_clock::now() + 时间分解 + 格式化,对高频日志(如每毫秒一条)是明显瓶颈。优化核心是:延迟格式化、复用缓冲、避免动态内存分配。
- 把时间戳结构体(秒+微秒)作为日志 entry 的一部分,在真正写入前才格式化,而非每次生成字符串
- 用
char buf[32]栈缓冲 +snprintf替代std::ostringstream,实测快 3–5 倍 - 若日志库支持,传入
std::chrono::time_point原始值,由后台线程统一格式化(如 spdlog 的spdlog::stopwatch模式) - 注意:
steady_clock无法直接转本地时间,如需带时区的日志,必须用system_clock,但要接受其精度波动
微秒级时间戳真正的难点不在“怎么算”,而在“怎么算得准、写得快、跨平台一致”。尤其在容器或嵌入式环境里,high_resolution_clock 的底层实现可能退化为毫秒级,上线前务必用 std::chrono::high_resolution_clock::period::num / std::chrono::high_resolution_clock::period::den 实际验证分辨率。


















