C STL 之 chrono 时间库详解为什么需要 chronoC 标准库的ctime提供time_t、clock_t、struct tm和clock()/time()等接口但存在三个硬伤精度不可控time_t只能到秒clock()虽返回 ticks 但单位由CLOCKS_PER_SEC定义不同平台不一致类型不安全秒和毫秒都是算术类型编译器不会阻止你把time_t直接赋值给毫秒变量单调性无保证time()返回墙上时间系统时间被 NTP 同步或用户手动修改后可能倒退测性能时会出现负数间隔chronoC11 引入C20/23 大幅扩展用强类型系统和编译期有理数运算解决了上述所有问题。三种时钟时钟是 chrono 的基石。C 标准定义了三种时钟各有用途。chrono 时钟体系system_clock墙上时间挂钟时间steady_clock单调递增时间high_resolution_clock最高精度时钟特点可转 time_t会被 NTP 调整用途日志时间戳文件修改时间特点严格单调永不回调用途性能测量函数耗时特点通常是对steady_clock 的类型别名用途微基准测试system_clock表示系统范围的实时挂钟wall clock对应time_t。可调用to_time_t()转成传统time_t也支持from_time_t()反向转换。#includechrono#includeiostream#includeiomanipintmain(){autotpstd::chrono::system_clock::now();std::time_t tstd::chrono::system_clock::to_time_t(tp);std::cout当前时间: std::put_time(std::gmtime(t),%F %T)\n;}steady_clock单调时钟保证了now()的返回值永远不会减小。适合测量代码段耗时#includechrono#includeiostream#includethreadintmain(){autostartstd::chrono::steady_clock::now();std::this_thread::sleep_for(std::chrono::milliseconds(100));autoendstd::chrono::steady_clock::now();autodiffend-start;automsstd::chrono::duration_caststd::chrono::milliseconds(diff);std::cout耗时: ms.count() ms\n;}high_resolution_clock字面意思是最高精度时钟。在主流实现中MSVC、libstdc、libc它通常是steady_clock的类型别名而非独立时钟。不要假定它与steady_clock不同。duration编译期单位转换duration是 chrono 最精巧的设计把数值和单位打包成一个类型。模板签名templateclassRep,classPeriodratio1classduration;Rep计数值类型int64_t、double等Period编译期有理数表示每个 tick 对应多少秒。ratio1, 1000表示毫秒ratio1, 1000000表示微秒标准库预定义了常用单位类型定义nanosecondsdurationint64_t, nanomicrosecondsdurationint64_t, micromillisecondsdurationint64_t, millisecondsdurationint64_tminutesdurationint64_t, ratio60hoursdurationint64_t, ratio3600单位转换链如下hoursratio3600minutesratio60secondsratio1millisecondsratio1,1000microsecondsratio1,1000000nanosecondsratio1,1000000000duration_cast / floor可编译期转换降精度需要显式隐式转换 vs 显式转换从低精度到高精度无损可以隐式转换automsstd::chrono::milliseconds(1500);std::chrono::microseconds usms;// OK1500000 us从高精度到低精度可能截断必须用duration_castautousstd::chrono::microseconds(2500);automsstd::chrono::duration_caststd::chrono::milliseconds(us);// 2 ms截断C17 新增floor、ceil、roundusingnamespacestd::chrono;automsduration_castmilliseconds(microseconds(2500));// 2automs2floormilliseconds(microseconds(2500));// 2automs3ceilmilliseconds(microseconds(2500));// 3automs4roundmilliseconds(microseconds(2499));// 2time_point时间轴上的一个点time_point绑定到特定时钟表示从该时钟的 epoch 起经过的 duration。templateclassClock,classDurationtypenameClock::durationclasstime_point;关键成员time_since_epoch()返回 epoch 到该点之间的durationoperator /operator -与duration做算术autotpstd::chrono::system_clock::now();autodtp.time_since_epoch();automsstd::chrono::duration_caststd::chrono::milliseconds(d);std::coutUnix 时间戳毫秒: ms.count()\n;不同时钟的time_point不能混用。system_clock::time_point和steady_clock::time_point是不同的类型编译器禁止直接运算。C20 日历扩展C20 为 chrono 增加了日历类型彻底终结了tmmktime的繁琐模式。year_month_day#includechronointmain(){usingnamespacestd::chrono;autotodayfloordays(system_clock::now());year_month_day ymd{today};// 构造指定日期year_month_day ymd2{2026y,June,25d};autodsys_days{ymd2};autotpsystem_clock::time_point{d};}工作日计算#includechrono#includeiostreamintmain(){usingnamespacestd::chrono;// 2026-06-25 是星期几year_month_day ymd{2026y,June,25d};autowdweekday{sys_days{ymd}};std::coutweekday: wd\n;// 下一个周一autodays_until_mon(Monday-wd).count();if(days_until_mon0)days_until_mon7;autonext_monsys_days{ymd}days{days_until_mon};}日期加减autodsys_days{2026y,January,1d}months{3}-days{5};// 约 2026-03-27注意事项year_month_day存储在days精度通过sys_days{}与system_clock::time_point互转C20 日期支持在 GCC 12、Clang 16、MSVC 2022 17.0 中可用需要链接-latomic某些平台性能high_resolution_clock::now() 开销now()的调用成本因平台/时钟而异时钟近似耗时说明system_clock::now()~25-50 ns通常调用GetSystemTimePreciseAsFileTimeWin或clock_gettime(CLOCK_REALTIME)Linuxsteady_clock::now()~25-50 ns与 system_clock 接近同样基于QueryPerformanceCounter或CLOCK_MONOTONIChigh_resolution_clock::now()~同样本质上是上述之一的别名结论单次now()开销约 50 ns在宏观性能测量中可忽略但在微基准测试中测量耗时 1 us 的操作需要重复执行并取平均值以稀释 overhead。// 测量高精度小函数的正确姿势autoinvoke[](){/* 被测函数 */};autostartstd::chrono::steady_clock::now();for(inti0;i100000;i)invoke();autoendstd::chrono::steady_clock::now();autoavg(end-start)/100000;std::cout平均耗时: std::chrono::duration_caststd::chrono::nanoseconds(avg).count() ns\n;面试题1.steady_clock和system_clock的根本区别是什么分别用于什么场景steady_clock保证单调递增不受系统时间调整影响用于性能测量。system_clock表示墙上时间可转time_t用于时间戳和日志记录。2. 为什么不能用steady_clock打印当前时间steady_clock的 epoch 通常是系统启动时间而非 Unix 纪元无法映射到人类可读的日历时间。要打印当前时间必须用system_clock。3.duration_cast和floor/ceil/round有什么区别duration_cast向零截断truncate toward zerofloor向下取整、ceil向上取整、round四舍五入到最近的整数。对正数duration_cast和floor结果相同但对负数不同。4. 为什么high_resolution_clock::now()不能保证高精度它是实现定义的别名在主流实现中就是steady_clock或system_clock之一并未提供额外精度。steady_clock::now()实际精度受操作系统定时器分辨率限制Windows 默认 ~15.6 ms除非使用多媒体定时器。5. 如何将system_clock::time_point转为 C20 的year_month_dayautotpstd::chrono::system_clock::now();autodaysstd::chrono::floorstd::chrono::days(tp);std::chrono::year_month_day ymd{days};6.duration的Period参数是运行时指定的还是编译期指定的它如何影响代码生成编译期指定。Period是std::ratio的特化所有单位转换通过有理数算术在编译期完成。编译器能在常数时间内完成乘除优化运行时零开销。7. C20 之前如何计算两个日期之间的天数差C20 有什么改进C20 之前需要用mktime转time_t再相除86400但受时区影响可能有偏差。C20 的sys_days将日期映射到 days 精度的时间点直接相减就是精确天数差且不涉及时区。autod1sys_days{2026y,January,1d};autod2sys_days{2026y,December,31d};autodiff(d2-d1).count();// 3648. 什么是 epochsystem_clock和steady_clock的 epoch 分别是什么epoch 是时间轴的零点。system_clock的 epoch 是 1970-01-01 00:00:00 UTCUnix 纪元。steady_clock的 epoch 是平台相关的通常是系统启动时间且不保证不同进程间一致。