mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 00:27:55 +00:00
72 lines
2.3 KiB
JavaScript
72 lines
2.3 KiB
JavaScript
const findAvailableSlots = async (calendar1, calendar2, constraints) => {
|
|
const [{ default: dayjs }, { default: utc }] = await Promise.all([
|
|
import('https://cdn.skypack.dev/dayjs'),
|
|
import('https://cdn.skypack.dev/dayjs/plugin/utc.js'),
|
|
]);
|
|
dayjs.extend(utc);
|
|
|
|
const { durationMinutes, searchRange, workHours } = constraints;
|
|
|
|
const toDayjs = (t) => dayjs.utc(t);
|
|
const toDayjsRange = ({ start, end }) => ({ start: toDayjs(start), end: toDayjs(end) });
|
|
|
|
const search = toDayjsRange(searchRange);
|
|
const allBusy = [...calendar1, ...calendar2]
|
|
.map(toDayjsRange)
|
|
.sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
|
|
|
const mergedBusy = allBusy.reduce((acc, current) => {
|
|
const last = acc[acc.length - 1];
|
|
if (last && current.start.valueOf() < last.end.valueOf()) {
|
|
if (current.end.isAfter(last.end)) {
|
|
last.end = current.end;
|
|
}
|
|
} else {
|
|
acc.push({ ...current });
|
|
}
|
|
return acc;
|
|
}, []);
|
|
|
|
const boundaryPoints = [
|
|
search.start,
|
|
...mergedBusy.flatMap(b => [b.start, b.end]),
|
|
search.end,
|
|
];
|
|
|
|
const freeGaps = [];
|
|
for (let i = 0; i < boundaryPoints.length - 1; i += 2) {
|
|
const start = boundaryPoints[i];
|
|
const end = boundaryPoints[i + 1];
|
|
if (end.isAfter(start)) {
|
|
freeGaps.push({ start, end });
|
|
}
|
|
}
|
|
|
|
const availableSlots = [];
|
|
const [workStartH, workStartM] = workHours.start.split(':').map(Number);
|
|
const [workEndH, workEndM] = workHours.end.split(':').map(Number);
|
|
|
|
for (const gap of freeGaps) {
|
|
let cursor = gap.start.startOf('day');
|
|
while (cursor.isBefore(gap.end)) {
|
|
const workWindowStart = cursor.hour(workStartH).minute(workStartM);
|
|
const workWindowEnd = cursor.hour(workEndH).minute(workEndM);
|
|
|
|
let effectiveStart = dayjs.max(gap.start, workWindowStart);
|
|
const effectiveEnd = dayjs.min(gap.end, workWindowEnd);
|
|
|
|
while (effectiveStart.add(durationMinutes, 'minute').valueOf() <= effectiveEnd.valueOf()) {
|
|
const slotEnd = effectiveStart.add(durationMinutes, 'minute');
|
|
availableSlots.push({ start: effectiveStart, end: slotEnd });
|
|
effectiveStart = slotEnd;
|
|
}
|
|
cursor = cursor.add(1, 'day');
|
|
}
|
|
}
|
|
|
|
return availableSlots.map(({ start, end }) => ({
|
|
start: start.toISOString(),
|
|
end: end.toISOString(),
|
|
}));
|
|
};
|
|
export default findAvailableSlots; |