mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 00:27:55 +00:00
68 lines
2.1 KiB
JavaScript
68 lines
2.1 KiB
JavaScript
async function findAvailableSlots(calendar1, calendar2, constraints) {
|
|
const [dayjsModule, durationModule] = await Promise.all([
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js'),
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1/plugin/duration.js')
|
|
]);
|
|
const dayjs = dayjsModule.default;
|
|
dayjs.extend(durationModule.default);
|
|
|
|
const { durationMinutes: duration, searchRange, workHours } = constraints;
|
|
const searchStart = dayjs(searchRange.start);
|
|
const searchEnd = dayjs(searchRange.end);
|
|
const [workStartH, workStartM] = workHours.start.split(':').map(Number);
|
|
const [workEndH, workEndM] = workHours.end.split(':').map(Number);
|
|
|
|
const toDayjs = ({ start, end }) => ({ start: dayjs(start), end: dayjs(end) });
|
|
const allBusy = [...calendar1, ...calendar2].map(toDayjs);
|
|
|
|
for (let day = searchStart.clone().startOf('day'); day.isBefore(searchEnd); day = day.add(1, 'day')) {
|
|
allBusy.push({
|
|
start: day,
|
|
end: day.hour(workStartH).minute(workStartM)
|
|
});
|
|
allBusy.push({
|
|
start: day.hour(workEndH).minute(workEndM),
|
|
end: day.endOf('day')
|
|
});
|
|
}
|
|
|
|
const mergedBusy = allBusy
|
|
.sort((a, b) => a.start - b.start)
|
|
.reduce((acc, slot) => {
|
|
const last = acc.at(-1);
|
|
if (last && slot.start <= last.end) {
|
|
if (slot.end > last.end) last.end = slot.end;
|
|
} else {
|
|
acc.push({ ...slot });
|
|
}
|
|
return acc;
|
|
}, []);
|
|
|
|
const availableSlots = [];
|
|
let nextFreeStart = searchStart;
|
|
|
|
const findSlotsInGap = (start, end) => {
|
|
let slotStart = start;
|
|
while (slotStart.add(duration, 'minute') <= end) {
|
|
const slotEnd = slotStart.add(duration, 'minute');
|
|
availableSlots.push({ start: slotStart.toISOString(), end: slotEnd.toISOString() });
|
|
slotStart = slotEnd;
|
|
}
|
|
};
|
|
|
|
mergedBusy.forEach(busySlot => {
|
|
if (busySlot.start > nextFreeStart) {
|
|
findSlotsInGap(nextFreeStart, busySlot.start);
|
|
}
|
|
if (busySlot.end > nextFreeStart) {
|
|
nextFreeStart = busySlot.end;
|
|
}
|
|
});
|
|
|
|
if (searchEnd > nextFreeStart) {
|
|
findSlotsInGap(nextFreeStart, searchEnd);
|
|
}
|
|
|
|
return availableSlots;
|
|
}
|
|
export default findAvailableSlots; |