用std::chrono计算日期差需先转time_point:c++20前用tm+mktime→time_t→system_clock::from_time_t;注意tm_year=123表示2023、tm_mon=9为10月、tm_isdst=-1;时区是最大陷阱,推荐统一utc或用c++20原生日期类。

用 std::chrono 计算两个日期差值,必须先转成时间点(time_point)
单纯用 std::chrono::system_clock::now() 只能获取当前时刻,没法直接表示“2023-10-05”这种日期。C++20 之前没有原生的 year_month_day 类型(C++20 起才有),所以得靠 std::tm + std::mktime 构造 time_t,再转成 system_clock::time_point。
常见错误是直接对两个 tm 结构做减法——这毫无意义,tm 不是时间量纲;也有人误以为 duration_cast 能直接套在 tm 上,结果编译失败。
- 构造
tm时注意:年份字段是距 1900 的偏移(tm_year = 123表示 2023),月份从 0 开始(tm_mon = 9是 10 月) -
tm_isdst = -1让系统自动判断夏令时,避免因 DST 导致 1 小时误差 -
std::mktime返回time_t,需用system_clock::from_time_t转为time_point
计算天数差要用 duration_cast<hours></hours> 再除以 24?别这么干
直接除以 24 容易因浮点或截断出错,且忽略闰秒、DST 切换等边界情况。正确做法是统一转成 std::chrono::hours 或 std::chrono::days(C++20),再用 duration_cast 提取整数量级。
例如想得到完整天数差(忽略时间部分),推荐先归一化到当天 00:00:00,再相减:
立即学习“C++免费学习笔记(深入)”;
auto tp1 = floor<days>(tp1_full); // C++20 auto tp2 = floor<days>(tp2_full); auto diff_days = duration_cast<days>(tp2 - tp1).count();
若仍在用 C++17 或更早:
- 用
time_t相减后除以60 * 60 * 24,但要注意负数截断问题(建议用std::div或先转int64_t) - 避免用
double做中间类型,time_t在 Windows 上可能是 64 位有符号整数,用double会丢失精度 - 跨年/跨月计算时,
mktime自动处理了闰年和各月天数,比手写逻辑可靠得多
时区是最大陷阱:std::mktime 总是按本地时区解析
如果你输入 “2023-10-05”,mktime 解释成你本机时区的午夜,不是 UTC。如果两台机器时区不同,同一串日期字符串算出来的 time_point 差值可能差出几小时甚至一天。
解决方法只有两个:
- 所有日期统一按 UTC 处理:用
std::gmtime+std::timegm(POSIX,非标准但广泛支持),或 C++20 的utc_clock - 明确约定输入格式带时区信息(如 ISO 8601 “2023-10-05T00:00:00Z”),再用第三方库(如 Howard Hinnant 的 date 库)解析
- Windows 下
_mkgmtime是timegm的等价替代,不要用mktime硬凑 UTC
C++20 的 <chrono></chrono> 原生日期类让这事简单很多,但别默认开启
C++20 引入了 year_month_day、sys_days、floor<days></days> 等,可以直接写:
using namespace std::chrono;
sys_days sd1 = year{2023}/oct/5;
sys_days sd2 = year{2024}/mar/10;
days diff = sd2 - sd1;
但注意:MSVC 2022 v17.5+、GCC 11+、Clang 14+ 才完整支持;且需显式开启 -std=c++20(GCC/Clang)或 /std:c++20(MSVC)。很多项目仍卡在 C++17,盲目用 C++20 日期特性会导致构建失败。
真正容易被忽略的是:C++20 的 year_month_day 默认构造的日期是“日历日期”,不带时区也不含时间,它和 system_clock::time_point 之间转换仍隐含本地时区语义——除非你显式用 utc_days 或 zoned_time。











