最可靠方案是用 Howard Hinnant 的 date 库解析 ISO8601 日期并计算小时差,避免手动处理闰年、时区、夏令时等复杂逻辑;纯标准库需用 std::get_time + mktime + difftime,但必须统一时区、注意 tm_year/tm_mon 偏移、检查流状态、用 double 保存秒差再除以 3600.0。

用 std::chrono 解析日期字符串再计算小时差最可靠
直接手撕年月日时分秒的进位逻辑极易出错(比如闰年、大小月、夏令时),C++11 起推荐用 std::chrono 搭配 std::get_time 或第三方库(如 Howard Hinnant 的 date 库)处理。标准库虽不直接支持 ISO 格式解析,但可借助 std::istringstream + std::get_time 读取固定格式字符串,再转为 std::time_t 计算秒差。
关键点:必须确保两个日期都转换到同一时区(通常用本地时间或 UTC),否则跨夏令时会导致结果偏差 1 小时。
- 只支持
%Y-%m-%d %H:%M:%S这类明确格式,2023-04-01T12:00:00中的T需手动替换或跳过 -
std::get_time不抛异常,失败时流状态置为 failbit,务必检查 - 计算完秒差后除以 3600 得到总小时数,用
double或long long存储,避免整除截断
手写解析函数前先确认是否真需要“纯标准库”
如果项目允许引入头文件-only 库,强烈建议用 date.h(https://www.php.cn/link/abe26c6ace59013c7c366a84290c2aac)。它让 ISO8601 解析变成一行:auto tp = date::parse("%Y-%m-%d %H:%M", ss);,且默认按 UTC 处理,无时区歧义。
纯标准库方案的问题在于:std::tm 的 tm_year 是从 1900 起算,tm_mon 是 0~11,mktime 会自动归一化但可能掩盖输入错误;而 mktime 返回的 time_t 是本地时间戳,若两日期跨 DST 切换点,差值会少或多 3600 秒。
立即学习“C++免费学习笔记(深入)”;
- 若必须用标准库,统一转成 UTC:用
timegm(POSIX)或手动减去timezone偏移(不推荐) - Windows 下无
timegm,可用_mkgmtime替代 - 输入格式不固定(如有的带毫秒、有的缺时分)时,正则预处理比硬编码
get_time更健壮
difftime 返回秒数,除以 3600 时注意类型截断
difftime 返回 double,但若直接赋给 int 再除,会丢失小数部分。例如 3601 秒本该是 1.000277... 小时,强转 int 后变 1 小时。
time_t t1 = mktime(&tm1); time_t t2 = mktime(&tm2); double seconds = difftime(t2, t1); // 正确:保持 double double hours = seconds / 3600.0; // 注意 .0,避免整数除法
- 若只要整数小时(向下取整),用
std::floor(hours);若四舍五入,用std::round(hours) - 差值可能为负,
difftime本身不保证符号,取决于传参顺序 - 32 位系统上
time_t可能溢出(2038 年问题),生产环境建议用 64 位time_t或std::chrono::system_clock::time_point
测试时别忽略 2 月 29 日和时区切换日
常见错误不是算法错,而是测试用例太“干净”。比如用 2023-01-01 和 2023-01-02 测出 24 小时就认为 OK,但遇到 2024-02-28 到 2024-03-01(闰年)或美国 3 月第二个周日(DST 开始)时,实际小时数可能是 23 或 25。
- 必测边界:2000-02-29(闰年)、2100-02-28(非闰年)、2023-11-05(美国 DST 结束日)
- 打印中间值:输出两个
time_t值及对应本地时间字符串,确认没被mktime错误归一化 - 跨年计算时,
tm_year忘加 1900 是高频 bug,调试时直接打印tm.tm_year + 1900
真正麻烦的从来不是算减法,而是让两个日期在同一个时空坐标系里对齐。时区、历法、系统 API 行为差异,任何一个没控住,结果就偏了。


















