日期计算
计算两日期相差的天/周/月/年,以及指定日期加减天、月、年的结果。
—
日期加减
—
日期计算:天数为什么精确、月份为什么只能估算
天数精确,月年不精确
两个日期之间的天数可以精确算出——它就是两个时间点之间的绝对间隔,没有任何歧义(前提是明确了是否包含端点)。
但"相差几个月"没有唯一答案,因为月份长度不同:
| 区间 | 天数 | "几个月" |
|---|---|---|
| 1 月 1 日 → 2 月 1 日 | 31 | 1 个月 |
| 2 月 1 日 → 3 月 1 日 | 28 或 29 | 1 个月 |
| 1 月 1 日 → 1 月 31 日 | 30 | 不满 1 个月 |
同样是"1 个月",天数可以是 28 到 31。所以工具的做法是:天数给出精确值,月和年按平均长度换算并明确标注为估算(月按 30.44 天、年按 365.25 天)。这样你不会看到 30 和 31 都被说成"1 个月"却不知道依据。
需要精确的"几个月"时,不要用换算,而应该按日历规则算:逐月推进,遇到月末做相应处理(1 月 31 日加一个月是 2 月 28 日还是 3 月 2 日,各系统实现不同)。工龄、租期、账单周期这类场景应该用日历法,差额通常是几天。
加减月份的月末溢出
日期加减里最容易出错的是月份处理:
| 操作 | 结果 | 说明 |
|---|---|---|
| 3 月 31 日 + 1 个月 | 4 月 30 日 | 目标月没有 31 日,退回月末 |
| 1 月 31 日 + 1 个月 | 2 月 28 日(闰年 29 日) | 同上 |
| 2 月 29 日 + 1 年 | 2 月 28 日 | 非闰年没有 2 月 29 日 |
工具有按日历规则处理这类溢出。注意这不是唯一正确的实现:有的系统选择"溢出到下个月"(3 月 31 日 + 1 个月 = 5 月 1 日)。做合同到期日、账单日、订阅续费日期计算时,必须先确认业务规则要求哪一种。
需要明确的四个约定
- 是否包含边界日期。"1 日到 3 日有几天",可能是 2 天(间隔)也可能是 3 天(含首尾)。请假天数、住宿晚数、项目工期,各自的口径不同。这是日期计算出错的头号原因。
- 时区。纯日期(不带时间)通常没有时区问题,但一旦涉及"今天""现在"就会受影响。跨时区的服务器按 UTC 计算"今天",可能与你本地日期差一天。日期选择器返回的是本地日期时尤其要注意。
- 工作日与自然日。"相差 30 天"和"30 个工作日"完全不同。工作日计算需要排除周末和法定节假日,而节假日因地区和年份而异,必须用对应的日历数据,不能靠规则推算。
- 夏令时。在有夏令时的地区,某些日期实际是 23 或 25 小时。纯日期运算不受影响,但涉及"几点到几点"的时长计算会差一小时。中国不实行夏令时,所以国内业务可以忽略这一条。
实际应用
- 项目工期核算。用天数,并明确是否含首尾。跨月项目建议直接列出起止日期而不是"X 个月"。
- 账期与到期日。按日历加月份,月末溢出规则要与业务约定一致。
- 年龄与周年。"满 18 周岁"要求具体到日,且要处理 2 月 29 日出生者的非闰年情况。
- 倒计时。目标日期减今天,注意包含性约定。
- 数据统计区间。做同比环比时,"上月的同期"应该按日历对齐,而不是按 30 天平移。
工具怎么用
上半部分选起始和结束日期,得到精确相差天数以及周/月/年的估算值;下半部分选基准日期并填要加减的天数、月数、年数,得到目标日期。两部分都实时计算,日期运算在浏览器本地完成。
常见问题
月和年是怎么算出来的?
天数是精确值;周=天/7;月按平均 30.44 天、年按 365.25 天估算,便于快速参考,非日历精确月。
加减月份会跨年吗?
会。工具直接用 Date 的 setMonth/setFullYear,遵循日历规则自动跨年、处理月末溢出。