
本文介绍一种健壮的日期重叠计算方法,可精确统计用户选择的日期区间(如 10/04/2024–17/04/2024)与任意数量预定义日期范围集合之间的逐段重叠天数,支持跨区间、部分覆盖及边界对齐场景。
本文介绍一种健壮的日期重叠计算方法,可精确统计用户选择的日期区间(如 10/04/2024–17/04/2024)与任意数量预定义日期范围集合之间的逐段重叠天数,支持跨区间、部分覆盖及边界对齐场景。
在实际业务中(如酒店预订、保险计费、资源占用分析),常需判断用户输入的一段连续日期(startDate → endDate)与多个预设时间区间集合(如不同价格策略周期、服务可用时段)的交集天数。原始代码仅支持“起始日是否落在某区间内”,无法处理跨区间覆盖(如 10/04–17/04 同时横跨 rangesInputRange1 的末尾和 rangesInputRange2 的开头)或部分重叠(如 28/08–03/09 仅与 rangesInputRange2 的 01/07–31/08 重叠 3 天),导致计费/校验逻辑错误。
核心思路是:对每个预定义区间集合(如 ranges[0] 对应 range1,ranges[1] 对应 range2),遍历其所有子区间,计算该子区间与用户输入区间 [ustart, uend] 的交集长度(以毫秒为单位),再统一转换为整数天数。
以下为优化后的完整实现(兼容 ISO 格式 ,无需手动解析 dd/mm/yyyy):
// 辅助函数:将 Date 对象向前推进一天(含月份/年份自动进位)
function advanceDay(d) {
d.setDate(d.getDate() + 1);
return d;
}
// 预定义多组日期范围(每组为一个数组,支持任意数量)
const ranges = [
// Range Set 1(对应 checkbox1 / number1)
[
{ startDate: new Date(2024, 3, 15), endDate: new Date(2024, 4, 1) }, // 15/04–01/05
{ startDate: new Date(2024, 5, 1), endDate: new Date(2024, 5, 30) } // 01/06–30/06
],
// Range Set 2(对应 checkbox2 / number2)
[
{ startDate: new Date(2024, 3, 1), endDate: new Date(2024, 3, 14) }, // 01/04–14/04
{ startDate: new Date(2024, 6, 1), endDate: new Date(2024, 7, 31) } // 01/07–31/08
]
];
// 绑定日期输入框事件
const dateInputs = [...document.querySelectorAll('input[type="date"]')];
dateInputs.forEach(inp => inp.addEventListener('change', calculateOverlap));
function calculateOverlap() {
// 获取用户输入的起止日期(注意:type="date" 返回 YYYY-MM-DD 字符串,new Date() 可直接解析)
const [userStart, userEnd] = dateInputs.map(inp => new Date(inp.value));
// 关键:将 userEnd 推进一天,使区间变为 [start, end) 左闭右开,避免重复计数边界日
advanceDay(userEnd);
// 对每一组预定义范围,计算总重叠天数
const overlapDays = ranges.map(rangeSet =>
Math.round(
rangeSet.reduce((totalMs, interval) => {
// 计算交集区间:max(start) ~ min(end)
const overlapStart = new Date(Math.max(interval.startDate.getTime(), userStart.getTime()));
const overlapEnd = new Date(Math.min(interval.endDate.getTime(), userEnd.getTime()));
// 若有有效交集(overlapStart 0).trigger('change');
$number1.val(overlapDays[0]).trigger('change');
$checkbox2.prop('checked', overlapDays[1] > 0).trigger('change');
$number2.val(overlapDays[1]).trigger('change');
console.log('重叠天数分布:', overlapDays); // 例如 [3, 4] 表示 range1 重叠 3 天,range2 重叠 4 天
}
// 初始化计算(页面加载后立即执行)
calculateOverlap();
✅ 关键改进点说明:
-
数学严谨性:使用
Math.max(startDate, userStart)与Math.min(endDate, userEnd)精确求交集端点,天然支持四种重叠关系(完全包含、左交、右交、内嵌)。 -
边界安全:将
userEnd推进一天并采用[start, end)半开区间模型,确保2024-04-10至2024-04-17精确返回 7 天(而非易错的getDate()差值)。 -
可扩展性强:
ranges是二维数组,可轻松添加第 3、第 4 组规则集,overlapDays自动扩容。 -
输入友好:直接使用原生
,规避了dd/mm/yyyy手动解析风险(如parseDate("15/04/2024")在某些浏览器可能误判为mm/dd/yyyy)。
⚠️ 注意事项:
- 确保 HTML 中
input[type="date"]的value属性为YYYY-MM-DD格式(如value="2024-04-10"),这是浏览器标准行为;若后端返回dd/mm/yyyy,请在 JS 中统一转换后再赋值。 -
new Date("2024-04-10")在所有现代浏览器中解析可靠,而new Date("10/04/2024")行为不一致,强烈建议避免。 - 如需支持时区敏感场景,请统一使用 UTC 时间或明确指定时区(如
new Date(Date.UTC(2024,3,15)))。
通过此方案,无论是 10/04–17/04(横跨 range2 末尾 + range1 开头),还是 28/08–03/09(仅与 range2 的 01/07–31/08 重叠 3 天),均可被精准拆解、独立计数,彻底解决原始逻辑的覆盖盲区。










