js 获取多个时间段中的断档日期

需求

如下图,保存的时候对表格中存在的断档日期进行提醒在这里插入图片描述

预期效果

有准确的提醒
在这里插入图片描述

实现

数据瞎造的,跟贴图不一致,不用纠结哈

// 默认日期全部合法,即开始日期小于结束日期
let tableData = [
      {
        dateStart: "2015-02-01",
        dateEnd: "2016-07-08",
      },
      {
        dateStart: "2013-02-01",
        dateEnd: "2014-07-08",
      },
      {
        dateStart: "2016-07-09",
        dateEnd: "2018-04-05",
      },
      {
        dateStart: "2015-05-09",
        dateEnd: "2019-01-02",
      },
      {
        dateStart: "2019-05-09",
        dateEnd: "2021-01-02",
      },
    ]; // 初始数据

    // 数组从小到大排序
    // arr: 排序数组
    // minToMax:是否从小到大排序,默认true
    function getSort(arr, minToMax = true) {
      for (var i = 0; i < arr.length; i++) {
        for (var j = i + 1; j < arr.length; j++) {
          let changeNum = () => {
            let current = arr[i];
            arr[i] = arr[j];
            arr[j] = current;
          };
          if (minToMax) {
            // 从小到大排序
            if (arr[i] > arr[j]) {
              changeNum();
            }
          } else {
            // 从大到小排序
            if (arr[i] < arr[j]) {
              changeNum();
            }
          }
        }
      }
      return arr;
    }

    let 
      minDate = getSort(tableData.map((it) => it.dateStart))[0], // 获取日期最小值
      maxDate = getSort(
        tableData.map((it) => it.dateEnd),
        false
      )[0], // 获取日期最大值
      initDates = [[minDate, maxDate]]; // 初始化表格最大范围时间区间,即均未被使用的时间区间
	
    // 使用排除法,只保留未使用的时间区间,区间如果被使用则移除该段区间;
    // 如果是完整的不断档的日期,最后initDates应该为空数组;否则为断档日期数组;
    tableData.forEach((it, index) => {
      let newDates = [];
      initDates.forEach((date) => {
        // 如果判定日期时间段不在当前未使用日期内
        if (it.dateEnd <= date[0] || it.dateStart >= date[1]) {
          newDates.push(date);
        }

        // 如果判定日期时间段在当前未使用日期内
        if (date[0] < it.dateStart && it.dateEnd < date[1]) {
          newDates.push([date[0], it.dateStart]);
          newDates.push([it.dateEnd, date[1]]);
        }

        // 如果判定只有日期时间段开始时间在当前未使用日期内
        if (
          date[0] < it.dateStart &&
          it.dateStart < date[1] &&
          it.dateEnd >= date[1]
        ) {
          newDates.push([date[0], it.dateStart]);
        }

        // 如果判定只有日期时间段结束时间在当前未使用日期内
        if (
          date[0] < it.dateEnd &&
          it.dateEnd < date[1] &&
          it.dateStart <= date[0]
        ) {
          newDates.push([it.dateEnd, date[1]]);
        }
      });
      initDates = newDates;
    });
    console.log(initDates, 'initDates') 
    //  [
    //     [
    //         "2014-07-08",
    //         "2015-02-01"
    //     ],
    //     [
    //         "2019-01-02",
    //         "2019-05-09"
    //     ]
    // ]
    // 即 2014-07-08 到 2015-02-01 有断档,2019-01-02 到 2019-05-09 有断档;

百度了下没找到现成的,自己动手,记录一下,留爪~

把一个大佬的方法也记录在这里,随意自取

function toDays(date) {
    return Math.floor((new Date(date).getTime() - new Date(2000, 1, 1).getTime()) / (1000 * 3600 * 24))
}
function toDate(day) {
    return new Date(day * 1000 * 3600 * 24 + new Date(2000, 1, 1).getTime()) 
}
function format(date) {
    return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
}


function solution(ranges) {
    // 格式化
    ranges = ranges.map(item => [toDays(item.dateStart), toDays(item.dateEnd)])
    // 获取初始区间
    const [min, max] = ranges.reduce((prev, curr) => {
        return [Math.min(prev[0], curr[0]), Math.max(prev[1], curr[1])]
    }, [Infinity, -Infinity])
    // 开始计算
    return _solution([[min, max, 0]], ranges)
}

function _solution(arr, ranges) {
    ranges.forEach(([min, max]) => {
        // 获取 min 落在的区间 index
        let minIndex = arr.findIndex(item => min >= item[0] && min <= item[1])
        // 获取 max 落在的区间 index
        let maxIndex = arr.findIndex(item => max >= item[0] && max <= item[1])
        // min 落在的区间
        let minRange = arr[minIndex]
        // max 落在的区间
        let maxRange = arr[maxIndex]

        if (minIndex === maxIndex) { // 左右端点落到同一个未使用区间
            if (minRange[2] === 0) { // 该区间未使用,则拆分成 3 个区间
                arr.splice(minIndex, 1, [minRange[0], min - 1, 0], [min, max, 1], [max + 1, minRange[1], 0])
            }
        } else { // 左右端点落到两个区间
            // 如果左侧区间已经使用,则 min 可认为是左侧区间的左端点
            if (minRange[2] === 1) {
                min = minRange[0]
            }
            // 如果右侧区间已经使用,则 max 可以认为是右侧区间的右端点
            if (maxRange[2] === 1) {
                max = maxRange[1]
            }
            // 左侧区间保留未使用的
            arr.splice(minIndex, 1, [minRange[0], min - 1, 0])
            // 右侧区间保留未使用的
            arr.splice(maxIndex, 1, [max + 1, maxRange[1], 0])
            // 中间的区间做合并处理
            arr.splice(minIndex, maxIndex - minIndex - 1, [min, max, 1])

        }
    })
    // 最终的结果需要过滤
    // 过滤出未使用的区间 => item[2] === 0
    // 过滤出合法区间 => item[0] <= item[1]
    return arr.filter(item => item[2] === 0 && item[0] <= item[1])
}


const input = [
    {
        dateStart: "2015-02-01",
        dateEnd: "2016-07-08",
    },
    {
        dateStart: "2013-02-01",
        dateEnd: "2014-07-08",
    },
    {
        dateStart: "2016-07-09",
        dateEnd: "2018-04-05",
    },
    {
        dateStart: "2015-05-09",
        dateEnd: "2019-01-02",
    },
    {
        dateStart: "2019-05-09",
        dateEnd: "2021-01-02",
    },
]
const output = solution(input)
output.forEach(item => {
    console.log(`${format(toDate(item[0]))} - ${format(toDate(item[1]))}`)
})
// 这个方法过滤了边界,所以输出不一样
// 2014-7-9 - 2015-1-31
// 2019-1-3 - 2019-5-8
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值