mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 08:37:56 +00:00
108 lines
3.4 KiB
JavaScript
108 lines
3.4 KiB
JavaScript
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
|
const { default: dayjs } = await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/+esm');
|
|
const [{ default: utc }, { default: customParseFormat }, { default: isBetween }] = await Promise.all([
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/utc.js'),
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/customParseFormat.js'),
|
|
import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/plugin/isBetween.js')
|
|
]);
|
|
|
|
dayjs.extend(utc);
|
|
dayjs.extend(customParseFormat);
|
|
dayjs.extend(isBetween);
|
|
|
|
const { durationMinutes, searchRange, workHours } = constraints;
|
|
const allBusy = [...cal1, ...cal2].map(s => ({
|
|
start: dayjs(s.start),
|
|
end: dayjs(s.end)
|
|
})).sort((a, b) => a.start - b.start);
|
|
|
|
const merged = allBusy.reduce((acc, curr) => {
|
|
if (!acc.length || acc[acc.length - 1].end < 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 searchStart = dayjs(searchRange.start);
|
|
const searchEnd = dayjs(searchRange.end);
|
|
const [whStart, whEnd] = [
|
|
dayjs(workHours.start, 'HH:mm'),
|
|
dayjs(workHours.end, 'HH:mm')
|
|
];
|
|
|
|
const isInWorkHours = (dt) => {
|
|
const time = dayjs().hour(dt.hour()).minute(dt.minute());
|
|
return time.isBetween(whStart, whEnd, null, '[)');
|
|
};
|
|
|
|
const addSlot = (start, end) => {
|
|
let curr = start;
|
|
while (curr.add(durationMinutes, 'minute') <= end) {
|
|
if (isInWorkHours(curr) && isInWorkHours(curr.add(durationMinutes, 'minute').subtract(1, 'second'))) {
|
|
const slotEnd = curr.add(durationMinutes, 'minute');
|
|
const dayEnd = curr.hour(whEnd.hour()).minute(whEnd.minute());
|
|
if (slotEnd <= dayEnd) {
|
|
slots.push({
|
|
start: curr.toISOString(),
|
|
end: slotEnd.toISOString()
|
|
});
|
|
}
|
|
}
|
|
curr = curr.add(15, 'minute');
|
|
}
|
|
};
|
|
|
|
let prevEnd = searchStart.hour(whStart.hour()).minute(whStart.minute());
|
|
if (prevEnd < searchStart) prevEnd = searchStart;
|
|
|
|
merged.forEach(busy => {
|
|
if (busy.start > prevEnd) {
|
|
let gapStart = prevEnd;
|
|
let gapEnd = busy.start;
|
|
|
|
let day = gapStart.startOf('day');
|
|
while (day <= gapEnd) {
|
|
const dayStart = dayjs.max(
|
|
gapStart,
|
|
day.hour(whStart.hour()).minute(whStart.minute())
|
|
);
|
|
const dayEnd = dayjs.min(
|
|
gapEnd,
|
|
day.hour(whEnd.hour()).minute(whEnd.minute())
|
|
);
|
|
|
|
if (dayStart < dayEnd && dayStart >= searchStart && dayEnd <= searchEnd) {
|
|
addSlot(dayStart, dayEnd);
|
|
}
|
|
day = day.add(1, 'day');
|
|
}
|
|
}
|
|
prevEnd = dayjs.max(prevEnd, busy.end);
|
|
});
|
|
|
|
const finalEnd = searchEnd.hour(whEnd.hour()).minute(whEnd.minute());
|
|
if (prevEnd < finalEnd) {
|
|
let day = prevEnd.startOf('day');
|
|
while (day <= finalEnd) {
|
|
const dayStart = dayjs.max(
|
|
prevEnd,
|
|
day.hour(whStart.hour()).minute(whStart.minute())
|
|
);
|
|
const dayEnd = dayjs.min(
|
|
finalEnd,
|
|
day.hour(whEnd.hour()).minute(whEnd.minute())
|
|
);
|
|
|
|
if (dayStart < dayEnd && dayStart >= searchStart && dayEnd <= searchEnd) {
|
|
addSlot(dayStart, dayEnd);
|
|
}
|
|
day = day.add(1, 'day');
|
|
}
|
|
}
|
|
|
|
return slots;
|
|
};
|
|
export default findAvailableSlots; |