time.h 日期运算必须通过 time_t 加减秒数再转回 tm,而非直接修改 tm 字段;因 mktime 仅单次归一化且受夏令时、字段范围等影响易出错。

time.h 在 C++ 中不支持直接加减天数或月份,所有日期运算必须手动转换为 time_t,再通过 mktime 归一化——这是最容易出错的环节。
为什么不能直接修改 tm_mday 或 tm_year 后就用 mktime?
mktime 会把 struct tm 当作“本地时间未归一化的原始输入”,它只在内部做一次标准化(比如把 tm_mday=32 转成下个月第1天),但不会递归处理跨月/跨年溢出。如果你先加 30 天再改 tm_mday,而没重置 tm_hour 到 0,mktime 可能因夏令时切换返回错误时间。
实操建议:
- 每次日期增减后,必须把
tm_sec、tm_min、tm_hour显式设为0(若只需日期精度) - 修改
tm_mday后不要假设月份自动进位,应依赖mktime的一次性归一化 - 调用
mktime前确保tm_isdst = -1,让系统自动判断夏令时
加减 N 天的可靠写法(避开 tm 字段直改陷阱)
最稳的方式是:转 time_t → 加减秒数 → 转回 tm。因为 time_t 是线性秒数,无歧义。
立即学习“C++免费学习笔记(深入)”;
示例:计算今天加 10 天的日期
time_t now; time(&now); time_t later = now + 10 * 24 * 60 * 60; // 粗略加 10 天(忽略闰秒和 DST 跳变) struct tm *t = localtime(&later); // 注意:localtime 返回静态缓冲区,多线程需用 localtime_r // t->tm_mday, t->tm_mon 等即为结果
注意点:
- 这个方法对“天”级运算是够用的,但遇到 DST 切换日(如春秋季凌晨2点跳变),
localtime可能返回重复或跳过的时间;若需绝对精确,应使用gmtime+ UTC 时间线 - 不要用
86400代替24*60*60—— 可读性差且易被误认为 magic number
mktime 归一化失败的典型错误现象
常见报错:mktime 返回 (time_t)(-1),或返回时间明显错乱(如 2024 年变成 1970 年)。
原因多为:
-
tm_year没减去 1900(例如填了2024而不是124) -
tm_mon超出[0,11]且未设tm_isdst = -1,导致归一化失败 -
tm_wday或tm_yday被手动赋值(这两个字段会被mktime忽略并重算,填错反而干扰逻辑)
调试技巧:打印原始 struct tm 各字段后再调 mktime,确认 tm_year 和 tm_mon 是否合规。
真正麻烦的是跨月计算(比如“上个月的今天”),time.h 没有内置支持,必须先减月再用 mktime 归一,且要处理 1月→12月、31号→下月溢出等边界。这时候与其硬刚 time.h,不如考虑 <chrono> + 第三方库(如 date.h),但那是另一个问题了。


















