如何识别并标记日历中“同日进出”的冲突日期

本文介绍如何高效识别日期数组中存在“同日入住与退房”(即某日期同时作为多个预约的起始或结束日)的场景,并自动将其标记为不可用,适用于酒店、日程等时间资源管理场景。

在日历系统或预订管理中,一个常见但易被忽略的业务规则是:若某一天既是某个预约的结束日(checkout),又是另一个预约的开始日(checkin),则该天通常不可对外开放预订——因为实际运营中需预留清洁/交接时间,不允许“无缝衔接”。例如 2025-07-05 同时出现在 {start: "2025-07-03", end: "2025-07-05"} 的 end 字段和 {start: "2025-07-05", end: "2025-07-08"} 的 start 字段中,此时应将 dates 数组中对应 '2025-07-05' 的条目设为 available: false。

实现这一逻辑的关键在于:识别出在 reserved 预约列表中被重复使用的日期(即至少出现两次)。注意,这里“重复”指该日期作为 start 或 end 值出现 ≥2 次,而非连续或成对出现——无论它是两次 start、两次 end,还是一次 start + 一次 end,只要总频次 ≥2,就触发不可用标记。

以下是清晰、高效的实现方案:

✅ 核心思路

  1. 扁平化提取所有关键日期:遍历 reserved 数组,收集每个对象的 start 和 end 值,合并为一个一维字符串数组 reservedDatesArr;
  2. 判断重复性:对 dates 中每个日期 obj.date,使用 indexOf() 与 lastIndexOf() 比较——若两者索引不相等,说明该日期在 reservedDatesArr 中至少出现两次;
  3. 原地更新可用状态:满足条件则设置 obj.available = false。

? 完整可运行代

const dates = [
  { date: '2025-07-01', available: true },
  { date: '2025-07-02', available: true },
  // ...(其余日期省略,见原始数据)
  { date: '2025-07-31', available: true }
];

const reserved = [
  { start: "2025-07-03", end: "2025-07-05" },
  { start: "2025-07-05", end: "2025-07-08" },
  { start: "2025-07-08", end: "2025-07-10" },
  { start: "2025-07-18", end: "2025-07-20" },
  { start: "2025-07-22", end: "2025-07-24" },
  { start: "2025-07-24", end: "2025-07-26" }
];

// 步骤1:提取所有 start/end 日期到扁平数组
const reservedDatesArr = [];
reserved.forEach(({ start, end }) => {
  reservedDatesArr.push(start, end);
});

// 步骤2:标记重复出现的日期为不可用
const finalArr = dates.map(obj => {
  const idx = reservedDatesArr.indexOf(obj.date);
  const lastIdx = reservedDatesArr.lastIndexOf(obj.date);
  if (idx !== -1 && idx !== lastIdx) { // 确保存在且重复
    obj.available = false;
  }
  return obj;
});

console.log(finalArr);
// 输出中:'2025-07-05'、'2025-07-08'、'2025-07-24' 的 available 均为 false

⚠️ 注意事项与优化建议

  • 边界安全:添加 idx !== -1 判断,避免对未匹配日期执行无效比较;
  • 性能考量:该方案时间复杂度为 O(n×m)(n 为 dates 长度,m 为 reservedDatesArr 长度),适用于中小型数据集(如单月日历)。若需处理海量预约,建议改用 Map 统计频次(O(n+m)):
    const countMap = new Map();
    reserved.forEach(({ start, end }) => {
      countMap.set(start, (countMap.get(start) || 0) + 1);
      countMap.set(end, (countMap.get(end) || 0) + 1);
    });
    const finalArr = dates.map(obj => ({
      ...obj,
      available: (countMap.get(obj.date) || 0) < 2
    }));
  • 业务语义强化:可根据需求区分“仅 end 重复”或“start/end 同时存在”,此时需分别建两个 Set 再交集判断;
  • 日期格式一致性:确保所有日期字符串均为 YYYY-MM-DD 格式(ISO 8601),避免因格式差异导致匹配失败。

通过该方法,你不仅能精准捕获“同日进出”的冲突点,还能以声明式、易维护的方式集成到现有日历逻辑中,显著提升资源调度的准确性与用户体验。