Files
lynchmark/tests/7_scheduler/outputs_gemini/gemini-3-pro-preview TEMP_1.7.js
2025-11-18 22:04:41 +00:00

43 lines
1.6 KiB
JavaScript

export const findAvailableSlots = async (calA, calB, { durationMinutes, searchRange, workHours }) => {
const { DateTime, Interval } = await import('https://cdn.jsdelivr.net/npm/luxon@3.4.4/+esm');
const zone = 'utc';
const toDt = (iso) => DateTime.fromISO(iso, { zone });
const totalRange = Interval.fromDateTimes(toDt(searchRange.start), toDt(searchRange.end));
const busy = Interval.merge([...calA, ...calB].map(({ start, end }) => Interval.fromDateTimes(toDt(start), toDt(end))));
const results = [];
let cursor = totalRange.start.startOf('day');
while (cursor < totalRange.end) {
const [startH, startM] = workHours.start.split(':');
const [endH, endM] = workHours.end.split(':');
const dailyWork = Interval.fromDateTimes(
cursor.set({ hour: +startH, minute: +startM }),
cursor.set({ hour: +endH, minute: +endM })
);
const effective = dailyWork.intersection(totalRange);
if (effective?.isValid && !effective.isEmpty()) {
let available = [effective];
for (const b of busy) {
if (b.overlaps(effective)) {
available = available.flatMap(a => a.difference(b));
}
}
for (const period of available) {
period.splitBy({ minutes: durationMinutes }).forEach(slot => {
if (slot.isValid && slot.toDuration('minutes').minutes === durationMinutes) {
results.push({ start: slot.start.toISO(), end: slot.end.toISO() });
}
});
}
}
cursor = cursor.plus({ days: 1 });
}
return results;
};
export default findAvailableSlots;