最稳方法是用std::chrono结合std::tm转换:先设tm_isdst=-1,再mktime转time_t,最后整除86400;需校验mktime返回值,避免非法日期导致溢出或偏差。

用 std::chrono 算两个日期差最稳
直接用 std::chrono + std::tm 转换,别碰 time_t 手动算天数——跨闰年、时区、夏令时全会翻车。C++20 的 std::chrono::year_month_day 更好,但老项目大概率还在用 C++11/14,所以重点讲兼容写法。
核心思路:把两个日期转成 std::time_t(秒级时间点),再相减除以 86400。但必须注意:两个 std::tm 都得设好 tm_isdst = -1,否则本地时区规则可能让结果偏一天。
-
std::mktime()会修改传入的std::tm,比如把tm_mon = 12自动进位,所以别复用同一结构体 - 月份要减 1(
tm_mon是 0–11),年份要加 1900(tm_year是距 1900 年的偏移) - 如果只关心“日历天数差”,忽略时分秒,建议统一设成中午 12:00,避免某天凌晨跨时区导致
mktime返回前一天
std::mktime 返回 -1 就是输入非法
常见错误:传了 tm_mday = 0 或 tm_mon = 13,mktime 不报错也不修正,直接返回 -1,接着 std::difftime 算出来就是负的巨大值。不是 bug,是标准行为。
- 务必检查
mktime(&tm1) == -1 || mktime(&tm2) == -1 -
tm_wday和tm_yday不用填,mktime会自动算;但tm_isdst必须设为-1让系统推断 - Windows 下
mktime对超大年份(如 10000 年)支持弱,Linux 一般撑到 2100 年左右没问题
只要日差?别用浮点除法
用 static_cast<int>(std::difftime(t2, t1) / 86400)</int> 看似简单,但 std::difftime 返回 double,对长跨度(比如 100 年)可能丢失精度——double 在 2^53 后无法精确表示每个整数秒,日差误差可能达 ±1 天。
立即学习“C++免费学习笔记(深入)”;
- 改用整数截断:
(t2 - t1) / 86400,前提是确认t2 >= t1且差值不溢出time_t有符号范围 - 更安全的做法:用
std::lldiv做整除,或直接用 C++20 的std::chrono::duration_cast<:chrono::days></:chrono::days> - 如果两个日期在同一年且月份已知,手动按月累加天数反而更准(避开
mktime的时区陷阱)
跨年/跨月计算容易漏掉边界
比如算 2023-01-31 到 2023-02-28,有人按“2 月有 28 天”直接减,得出 28 天,实际是 28 天没错;但若算 2024-01-31 到 2024-02-29,手动硬算就容易错——2 月有 29 天,但起始日 31 日在 2 月不存在,mktime 会自动折成 3 月 2 日,结果变成 33 天而非 29 天。
- 这就是为什么不能跳过
mktime直接数学算:它处理了所有“不存在的日期”的归约逻辑 - 测试时至少覆盖:1 月 31 日→2 月 28/29 日、12 月 31 日→次年 1 月 1 日、闰年前后各一年
- 如果业务明确只要“日历上看起来的天数差”(比如合同有效期),那得另写逻辑,和系统时间无关
真正麻烦的不是怎么算,而是搞清你要的到底是“经过多少个 24 小时”,还是“日历上翻了多少页”。前者靠 mktime,后者得自己建日期表或用第三方库比如 date.h。











