mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 16:47:55 +00:00
78 lines
2.8 KiB
JavaScript
78 lines
2.8 KiB
JavaScript
async function findAvailableSlots(cal1, cal2, constraints) {
|
|
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
|
const [{ default: utc }, { default: isBetween }, { default: customParseFormat }] = await Promise.all([
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/plugin/utc/+esm'),
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/plugin/isBetween/+esm'),
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/plugin/customParseFormat/+esm')
|
|
]);
|
|
|
|
dayjs.extend(utc);
|
|
dayjs.extend(isBetween);
|
|
dayjs.extend(customParseFormat);
|
|
|
|
const { durationMinutes, searchRange, workHours } = constraints;
|
|
const allBusy = [...cal1, ...cal2]
|
|
.map(s => ({ start: dayjs(s.start), end: dayjs(s.end) }))
|
|
.sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
|
|
|
const merged = allBusy.reduce((acc, curr) => {
|
|
if (!acc.length || acc[acc.length - 1].end.isBefore(curr.start)) {
|
|
acc.push(curr);
|
|
} else {
|
|
acc[acc.length - 1].end = dayjs.max(acc[acc.length - 1].end, curr.end);
|
|
}
|
|
return acc;
|
|
}, []);
|
|
|
|
const slots = [];
|
|
const rangeStart = dayjs(searchRange.start);
|
|
const rangeEnd = dayjs(searchRange.end);
|
|
const [workStart, workEnd] = [workHours.start, workHours.end];
|
|
|
|
const getWorkBounds = (date) => {
|
|
const [sh, sm] = workStart.split(':').map(Number);
|
|
const [eh, em] = workEnd.split(':').map(Number);
|
|
return {
|
|
start: date.hour(sh).minute(sm).second(0).millisecond(0),
|
|
end: date.hour(eh).minute(em).second(0).millisecond(0)
|
|
};
|
|
};
|
|
|
|
let currentDay = rangeStart.startOf('day');
|
|
while (currentDay.isBefore(rangeEnd) || currentDay.isSame(rangeEnd, 'day')) {
|
|
const { start: dayWorkStart, end: dayWorkEnd } = getWorkBounds(currentDay);
|
|
const dayStart = dayjs.max(dayWorkStart, rangeStart);
|
|
const dayEnd = dayjs.min(dayWorkEnd, rangeEnd);
|
|
|
|
if (dayStart.isBefore(dayEnd)) {
|
|
let cursor = dayStart;
|
|
|
|
for (const busy of merged) {
|
|
if (busy.end.isBefore(dayStart) || busy.start.isAfter(dayEnd)) continue;
|
|
|
|
const gapEnd = dayjs.min(busy.start, dayEnd);
|
|
while (cursor.add(durationMinutes, 'minute').isSameOrBefore(gapEnd)) {
|
|
slots.push({
|
|
start: cursor.toISOString(),
|
|
end: cursor.add(durationMinutes, 'minute').toISOString()
|
|
});
|
|
cursor = cursor.add(durationMinutes, 'minute');
|
|
}
|
|
cursor = dayjs.max(cursor, busy.end);
|
|
}
|
|
|
|
while (cursor.add(durationMinutes, 'minute').isSameOrBefore(dayEnd)) {
|
|
slots.push({
|
|
start: cursor.toISOString(),
|
|
end: cursor.add(durationMinutes, 'minute').toISOString()
|
|
});
|
|
cursor = cursor.add(durationMinutes, 'minute');
|
|
}
|
|
}
|
|
|
|
currentDay = currentDay.add(1, 'day');
|
|
}
|
|
|
|
return slots;
|
|
}
|
|
export default findAvailableSlots; |