如何高效获取摩托车分类下全量预订的连续日期范围以适配FullCalendar背景事件
我正在使用FullCalendar.js库来可视化租赁公司的预订信息。我把摩托车按引擎排量分类存储为数组,每个分类下的children数组包含该类别的所有具体车辆,示例如下:
// The resources array contains information about the motorbikes in a rental store. // The parent array contains the information about the motorbike engine, // while the children array contains the information about the unique motorbikes belonging to each engine category. resources = [{ id: "scooter50", title: "Scooter 50", children: [{ id: "aa123ab", title: "AA 123 AB" }, { id: "aa456cd", title: "AA 456 CD" }, { id: "aa789ef", title: "AA 789 EF" }, { id: "aa101gh", title: "AA 101 GH" } ] }, { id: "scooter125", title: "Scooter 125", children: [{ id: "bb123ab", title: "BB 123 AB" }, { id: "bb456cd", title: "BB 456 CD" }, { id: "bb789ef", title: "BB 789 EF" }, { id: "bb101gh", title: "BB 101 GH" } ] }, { id: "scooter250", title: "Scooter 250", children: [{ id: "cc123ab", title: "CC 123 AB" }, { id: "cc456cd", title: "CC 456 CD" }, { id: "cc789ef", title: "CC 789 EF" }, { id: "cc101gh", title: "CC 101 GH" } ] } ];
另外,我有一些关联到具体车辆的预订事件:
// The events array contains information about the motorbikes that are rented, including the start and end date of the rental. events = [{ start: new Date(2025, 0, 6, 0, 0), end: new Date(2025, 0, 9, 23, 59, 59), resourceId: "bb101gh", title: "Event 0" }, { start: new Date(2025, 0, 11, 0, 0), end: new Date(2025, 0, 14, 23, 59, 59), resourceId: "bb101gh", title: "Event 1" }, { start: new Date(2025, 0, 7, 0, 0), end: new Date(2025, 0, 11, 23, 59, 59), resourceId: "bb123ab", title: "Event 2" }, { start: new Date(2025, 0, 9, 0, 0), end: new Date(2025, 0, 14, 23, 59, 59), resourceId: "bb456cd", title: "Event 3" }, { start: new Date(2025, 0, 7, 0, 0), end: new Date(2025, 0, 10, 23, 59, 59), resourceId: "bb789ef", title: "Event 4" }, { start: new Date(2025, 0, 11, 0, 0), end: new Date(2025, 0, 15, 23, 59, 59), resourceId: "bb789ef", title: "Event 5" }, { start: new Date(2025, 0, 6, 0, 0), end: new Date(2025, 0, 15, 23, 59, 59), resourceId: "cc101gh", title: "Event 6" }, { start: new Date(2025, 0, 8, 0, 0), end: new Date(2025, 0, 11, 23, 59, 59), resourceId: "cc123ab", title: "Event 7" } ]
这些事件可以在FullCalendar时间线图表的截图中查看(截图)。
我想要实现的效果是:当某一分类下的所有摩托车在某个时间段内都被完全预订时,在该分类的资源行上显示红色背景(示例参考这里)。这可以通过FullCalendar的背景事件(官方文档:背景事件)轻松实现,但问题在于我还没找到一种可靠且高效的方法,来计算出某个分类下所有车辆都被预订的连续日期范围。
目前我已经实现了一个半可行的方案:先获取所有事件的最早和最晚日期,然后以30分钟为间隔遍历这些时间点,当某一分类下的所有车辆在该时间点都被占用时,就为该分类添加一个30分钟的背景事件。代码如下:
// Get the minimum and maximum date in the events array minDate = events.map(event => event.start).reduce((a, b) => a < b ? a : b); maxDate = events.map(event => event.end).reduce((a, b) => a > b ? a : b); // Iterate between the dates using 30 minutes intervals. unavailableDateRanges = []; for (let i = minDate; i <= maxDate; i.setMinutes(i.getMinutes() + 30)) { // Filter the events that are happening at this time and get the resources that are being used. let usedResources = events.filter(event => event.start <= i && event.end >= i).map(event => event.resourceId); // If all resources from a certain category are used, store a 30 minutes unavailable range for that category. let unavailableCategories = resources.filter(resource => resource.children.every(child => usedResources.includes(child.id))).map(resource => resource.id); for (category of unavailableCategories) { let start = new Date(i); let end = new Date(i); end.setMinutes(end.getMinutes() + 30); unavailableDateRanges.push({ start: start, end: end, resourceId: category, title: "Unavailable", display: "background" }); } }
但这个方案并不够可靠,因为它只检查30分钟的时间间隔,如果有预订在比如9:45开始,那么9:45到10:00这段时间就不会被标记为不可用。而这个红色背景需要在不同的时间粒度下都准确显示,所以这会成为问题。
由于日期是连续的时间变量(UNIX时间戳),我觉得应该可以按分类筛选事件,然后找出该分类下有n个(n等于该分类的车辆总数)预订重叠的连续时间段——前提是同一辆车的预订不会重叠(这点我已经确保了)。我尝试编写这个逻辑但没有成功。
我知道有一些处理日期范围的库,比如date-fns,但看起来它也无法直接解决这个问题。
有没有有经验的开发者能帮我解决这个问题?
备注:内容来源于stack exchange,提问作者Matteo Berra

