Node.js中如何排除用户选定期限内不在指定区间的日期?
问题:排除用户日期范围内不在场馆可用区间的日期
我有一个包含多个场馆可用日期区间的数组venueTimings,以及用户选择的一组起止日期范围。需要从用户选择的日期范围中,排除所有不属于venueTimings中任意一个区间的日期。
场馆可用日期区间数组:
const venueTimings = [ { start_date: '2024-07-01T00:00:00.000Z', end_date: '2024-07-31T00:00:00.000Z' }, { start_date: '2024-08-01T00:00:00.000Z', end_date: '2024-08-31T00:00:00.000Z' }, { start_date: '2024-10-01T00:00:00.000Z', end_date: '2024-10-31T00:00:00.000Z' } ];
用户选择的日期范围:
const userStart = '2024-07-01T00:00:00.000Z'; const userEnd = '2024-10-31T00:00:00.000Z';
当前尝试的代码(未达预期):
if (moment(start_date) < moment(venueTiming.start_date)) { for (let d = moment(start_date); d.diff(venueTiming.start_date, 'days') <= 0; d.add(1, 'days')) { let date = d.format('YYYY-MM-DD'); excluded_dates.push({ date, reason: 'missing-venue-time' }); blocked_dates.push(date); } } if (moment(venueTiming.end_date) < moment(end_date)) { for (let d = moment(venueTiming.end_date); d.isBefore(moment(end_date)); d.add(1, 'days')) { const date = d.format('YYYY-MM-DD'); if (d.format('dddd') === day) { excluded_dates.push({ date, reason: 'missing-venue-time' }); blocked_dates.push(date); } } }
存在的问题:上述代码仅能处理单个场馆区间与用户区间的前后差异,无法覆盖多个场馆区间之间的空白(比如示例中的9月份),也未遍历所有场馆区间做完整校验,导致无法准确排除所有不符合条件的日期。
解决方案
方法1:遍历用户日期范围逐天判断
逻辑直观,适合日期范围较小的场景:
const moment = require('moment'); const venueTimings = [ { start_date: '2024-07-01T00:00:00.000Z', end_date: '2024-07-31T00:00:00.000Z' }, { start_date: '2024-08-01T00:00:00.000Z', end_date: '2024-08-31T00:00:00.000Z' }, { start_date: '2024-10-01T00:00:00.000Z', end_date: '2024-10-31T00:00:00.000Z' } ]; const userStart = moment('2024-07-01T00:00:00.000Z'); const userEnd = moment('2024-10-31T00:00:00.000Z'); const excludedDates = []; const blockedDates = []; // 遍历用户选择的每一天 for (let currentDate = moment(userStart); currentDate.isSameOrBefore(userEnd); currentDate.add(1, 'days')) { const formattedDate = currentDate.format('YYYY-MM-DD'); // 判断当前日期是否在任意场馆可用区间内 const isInVenueRange = venueTimings.some(timing => { const venueStart = moment(timing.start_date); const venueEnd = moment(timing.end_date); return currentDate.isBetween(venueStart, venueEnd, 'day', '[]'); // 包含起止日期 }); if (!isInVenueRange) { excludedDates.push({ date: formattedDate, reason: 'missing-venue-time' }); blockedDates.push(formattedDate); } } console.log('排除的日期:', excludedDates); console.log('被阻止的日期列表:', blockedDates);
方法2:先计算差集区间再生成日期
通过计算空白区间减少遍历次数,效率更高,适合大范围日期场景:
const moment = require('moment'); const venueTimings = [ { start_date: '2024-07-01T00:00:00.000Z', end_date: '2024-07-31T00:00:00.000Z' }, { start_date: '2024-08-01T00:00:00.000Z', end_date: '2024-08-31T00:00:00.000Z' }, { start_date: '2024-10-01T00:00:00.000Z', end_date: '2024-10-31T00:00:00.000Z' } ]; const userStart = moment('2024-07-01T00:00:00.000Z'); const userEnd = moment('2024-10-31T00:00:00.000Z'); // 先将场馆区间按开始日期排序,避免顺序混乱导致漏判 const sortedVenueTimings = [...venueTimings].sort((a, b) => moment(a.start_date).valueOf() - moment(b.start_date).valueOf()); const excludedIntervals = []; let prevEnd = moment(userStart); // 遍历场馆区间,找出中间的空白区间 for (const timing of sortedVenueTimings) { const venueStart = moment(timing.start_date); const venueEnd = moment(timing.end_date); // 当前场馆区间开始晚于上一个区间结束,说明存在空白 if (venueStart.isAfter(prevEnd)) { excludedIntervals.push({ start: moment(prevEnd), end: moment(venueStart).subtract(1, 'days') }); } // 更新上一个区间的结束日期为当前场馆区间的结束日期(取较大值,兼容重叠区间) if (venueEnd.isAfter(prevEnd)) { prevEnd = venueEnd; } } // 检查用户区间结束日期是否晚于最后一个场馆区间的结束日期 if (userEnd.isAfter(prevEnd)) { excludedIntervals.push({ start: moment(prevEnd).add(1, 'days'), end: moment(userEnd) }); } // 生成所有排除区间内的日期 const excludedDates = []; const blockedDates = []; for (const interval of excludedIntervals) { for (let d = moment(interval.start); d.isSameOrBefore(interval.end); d.add(1, 'days')) { const formattedDate = d.format('YYYY-MM-DD'); excludedDates.push({ date: formattedDate, reason: 'missing-venue-time' }); blockedDates.push(formattedDate); } } console.log('排除的日期:', excludedDates); console.log('被阻止的日期列表:', blockedDates);
关键说明
- 方法1逻辑简单,适合小范围日期;方法2通过计算空白区间减少遍历次数,效率更优。
moment.isBetween的第四个参数'[]'表示包含起止日期,确保边界日期被正确判断。- 对场馆区间排序是必要步骤,避免因区间顺序混乱导致空白区间漏判。
内容的提问来源于stack exchange,提问作者Jaskaran
相关产品推荐
相关产品推荐

