Refactor: Remove stale benchmark outputs

This commit is contained in:
github-actions[bot]
2026-02-06 21:01:23 +00:00
parent 3a53d655cd
commit 3385fbc925
99 changed files with 0 additions and 2663 deletions

View File

@@ -1,87 +0,0 @@
async function findAvailableSlots(calendar1, calendar2, constraints) {
const { DateTime, Interval } = await import("https://cdn.jsdelivr.net/npm/luxon@3/+esm");
const { durationMinutes, searchRange, workHours } = constraints;
const dur = durationMinutes;
const rangeStart = DateTime.fromISO(searchRange.start, { zone: "utc" });
const rangeEnd = DateTime.fromISO(searchRange.end, { zone: "utc" });
const [whStartH, whStartM] = workHours.start.split(":").map(Number);
const [whEndH, whEndM] = workHours.end.split(":").map(Number);
const allBusy = [...calendar1, ...calendar2]
.map(b => ({
s: DateTime.fromISO(b.start, { zone: "utc" }),
e: DateTime.fromISO(b.end, { zone: "utc" })
}))
.sort((a, b) => a.s - b.s);
const merged = [];
for (const slot of allBusy) {
if (merged.length && slot.s <= merged[merged.length - 1].e) {
merged[merged.length - 1].e = DateTime.max(merged[merged.length - 1].e, slot.e);
} else {
merged.push({ s: slot.s, e: slot.e });
}
}
const freePeriods = [];
let cursor = rangeStart;
for (const busy of merged) {
if (busy.s > cursor) {
freePeriods.push({ s: cursor, e: DateTime.min(busy.s, rangeEnd) });
}
cursor = DateTime.max(cursor, busy.e);
if (cursor >= rangeEnd) break;
}
if (cursor < rangeEnd) {
freePeriods.push({ s: cursor, e: rangeEnd });
}
const workConstrained = [];
for (const free of freePeriods) {
let day = free.s.startOf("day");
const lastDay = free.e.startOf("day");
while (day <= lastDay) {
const whS = day.set({ hour: whStartH, minute: whStartM, second: 0, millisecond: 0 });
const whE = day.set({ hour: whEndH, minute: whEndM, second: 0, millisecond: 0 });
const overlapStart = DateTime.max(free.s, whS, rangeStart);
const overlapEnd = DateTime.min(free.e, whE, rangeEnd);
if (overlapStart < overlapEnd) {
workConstrained.push({ s: overlapStart, e: overlapEnd });
}
day = day.plus({ days: 1 });
}
}
const results = [];
for (const period of workConstrained) {
let slotStart = period.s;
while (true) {
const slotEnd = slotStart.plus({ minutes: dur });
if (slotEnd > period.e) break;
results.push({
start: slotStart.toISO(),
end: slotEnd.toISO()
});
slotStart = slotEnd;
}
}
results.sort((a, b) => a.start.localeCompare(b.start));
return results;
}
export default findAvailableSlots;
// Generation time: 11.229s
// Result: PASS

View File

@@ -1,88 +0,0 @@
async function findAvailableSlots(calendar1, calendar2, constraints) {
const { DateTime, Interval } = await import("https://cdn.jsdelivr.net/npm/luxon@3/+esm");
const { durationMinutes, searchRange, workHours } = constraints;
const rangeStart = DateTime.fromISO(searchRange.start, { zone: "utc" });
const rangeEnd = DateTime.fromISO(searchRange.end, { zone: "utc" });
const [whStartH, whStartM] = workHours.start.split(":").map(Number);
const [whEndH, whEndM] = workHours.end.split(":").map(Number);
const allBusy = [...calendar1, ...calendar2]
.map(b => ({
s: DateTime.fromISO(b.start, { zone: "utc" }),
e: DateTime.fromISO(b.end, { zone: "utc" })
}))
.sort((a, b) => a.s - b.s);
const merged = [];
for (const slot of allBusy) {
if (merged.length && slot.s <= merged[merged.length - 1].e) {
merged[merged.length - 1].e = DateTime.max(merged[merged.length - 1].e, slot.e);
} else {
merged.push({ s: slot.s, e: slot.e });
}
}
const freePeriods = [];
let cursor = rangeStart;
for (const busy of merged) {
if (busy.s > cursor) {
freePeriods.push({ s: cursor, e: DateTime.min(busy.s, rangeEnd) });
}
cursor = DateTime.max(cursor, busy.e);
}
if (cursor < rangeEnd) {
freePeriods.push({ s: cursor, e: rangeEnd });
}
const workWindows = [];
let day = rangeStart.startOf("day");
const lastDay = rangeEnd.startOf("day").plus({ days: 1 });
while (day < lastDay) {
const ws = day.set({ hour: whStartH, minute: whStartM, second: 0, millisecond: 0 });
const we = day.set({ hour: whEndH, minute: whEndM, second: 0, millisecond: 0 });
if (we > ws) {
const clampedStart = DateTime.max(ws, rangeStart);
const clampedEnd = DateTime.min(we, rangeEnd);
if (clampedStart < clampedEnd) {
workWindows.push({ s: clampedStart, e: clampedEnd });
}
}
day = day.plus({ days: 1 });
}
const effectiveFree = [];
for (const free of freePeriods) {
for (const ww of workWindows) {
const s = DateTime.max(free.s, ww.s);
const e = DateTime.min(free.e, ww.e);
if (s < e) {
effectiveFree.push({ s, e });
}
}
}
effectiveFree.sort((a, b) => a.s - b.s);
const results = [];
const dur = { minutes: durationMinutes };
for (const period of effectiveFree) {
let slotStart = period.s;
while (true) {
const slotEnd = slotStart.plus(dur);
if (slotEnd > period.e) break;
results.push({
start: slotStart.toISO(),
end: slotEnd.toISO()
});
slotStart = slotEnd;
}
}
return results;
}
export default findAvailableSlots;
// Generation time: 12.695s
// Result: PASS

View File

@@ -1,56 +0,0 @@
export async function findAvailableSlots(cal1, cal2, { durationMinutes, searchRange, workHours }) {
const dayjs = (await import('https://cdn.skypack.dev/dayjs')).default;
const utc = (await import('https://cdn.skypack.dev/dayjs/plugin/utc')).default;
dayjs.extend(utc);
const u = (v) => dayjs.utc(v);
const sR = u(searchRange.start);
const eR = u(searchRange.end);
const [sh, sm] = workHours.start.split(':');
const [eh, em] = workHours.end.split(':');
let blocked = [...cal1, ...cal2].map((b) => ({
s: u(b.start),
e: u(b.end)
}));
for (let c = sR.startOf('d'); c <= eR; c = c.add(1, 'd')) {
blocked.push(
{ s: c, e: c.set('h', sh).set('m', sm) },
{ s: c.set('h', eh).set('m', em), e: c.add(1, 'd') }
);
}
const merged = blocked
.map((b) => ({
s: b.s < sR ? sR : b.s,
e: b.e > eR ? eR : b.e
}))
.filter((b) => b.s < b.e)
.sort((a, b) => a.s - b.s)
.reduce((acc, b) => {
const p = acc[acc.length - 1];
p && b.s <= p.e ? (p.e = b.e > p.e ? b.e : p.e) : acc.push(b);
return acc;
}, []);
const avail = [];
let cursor = sR;
[...merged, { s: eR, e: eR }].forEach((b) => {
while (cursor.add(durationMinutes, 'm') <= b.s) {
const next = cursor.add(durationMinutes, 'm');
avail.push({
start: cursor.toISOString(),
end: next.toISOString()
});
cursor = next;
}
if (b.e > cursor) cursor = b.e;
});
return avail;
}
export default findAvailableSlots;
// Generation time: 35.904s
// Result: PASS

View File

@@ -1,94 +0,0 @@
export async function findAvailableSlots(calendarA, calendarB, constraints) {
const { durationMinutes, searchRange, workHours } = constraints;
const { parseISO, addMinutes, differenceInMinutes, min, max, formatISO, isAfter, isBefore, isEqual, parse } = await import('https://cdn.jsdelivr.net/npm/date-fns@3.6.0/+esm');
const searchStart = parseISO(searchRange.start);
const searchEnd = parseISO(searchRange.end);
const workStart = parse(workHours.start, 'HH:mm', searchStart);
const workEnd = parse(workHours.end, 'HH:mm', searchStart);
const allBusy = [...calendarA, ...calendarB].map(slot => ({
start: parseISO(slot.start),
end: parseISO(slot.end)
})).sort((a, b) => a.start - b.start);
const mergedBusy = [];
for (const slot of allBusy) {
if (!mergedBusy.length || isAfter(slot.start, mergedBusy[mergedBusy.length - 1].end)) {
mergedBusy.push({ start: slot.start, end: slot.end });
} else {
mergedBusy[mergedBusy.length - 1].end = max([mergedBusy[mergedBusy.length - 1].end, slot.end]);
}
}
const freePeriods = [];
let currentStart = searchStart;
for (const busy of mergedBusy) {
if (isBefore(currentStart, busy.start)) {
freePeriods.push({ start: currentStart, end: busy.start });
}
currentStart = max([currentStart, busy.end]);
}
if (isBefore(currentStart, searchEnd)) {
freePeriods.push({ start: currentStart, end: searchEnd });
}
const slots = [];
for (const period of freePeriods) {
const periodStart = max([period.start, searchStart]);
const periodEnd = min([period.end, searchEnd]);
let slotStart = periodStart;
while (true) {
const slotEnd = addMinutes(slotStart, durationMinutes);
if (isAfter(slotEnd, periodEnd) || isAfter(slotEnd, searchEnd)) {
break;
}
const slotWorkStart = new Date(slotStart);
const slotWorkEnd = new Date(slotStart);
slotWorkStart.setUTCHours(workStart.getUTCHours(), workStart.getUTCMinutes(), 0, 0);
slotWorkEnd.setUTCHours(workEnd.getUTCHours(), workEnd.getUTCMinutes(), 0, 0);
const adjustedSlotStart = max([slotStart, slotWorkStart]);
if (differenceInMinutes(slotEnd, adjustedSlotStart) >= durationMinutes) {
const finalSlotStart = adjustedSlotStart;
const finalSlotEnd = addMinutes(finalSlotStart, durationMinutes);
if (!isAfter(finalSlotEnd, periodEnd) && !isAfter(finalSlotEnd, searchEnd) && !isAfter(finalSlotEnd, slotWorkEnd)) {
slots.push({ start: formatISO(finalSlotStart), end: formatISO(finalSlotEnd) });
}
}
slotStart = addMinutes(slotStart, durationMinutes);
}
}
const sortedSlots = slots.sort((a, b) => parseISO(a.start) - parseISO(b.start));
const nonOverlappingSlots = [];
let lastEnd = null;
for (const slot of sortedSlots) {
const slotStart = parseISO(slot.start);
const slotEnd = parseISO(slot.end);
if (!lastEnd || !isBefore(slotStart, lastEnd)) {
if (!lastEnd || isAfter(slotStart, lastEnd)) {
nonOverlappingSlots.push(slot);
lastEnd = slotEnd;
}
}
}
return nonOverlappingSlots;
}
export default findAvailableSlots;
// Generation time: 14.885s
// Result: FAIL

View File

@@ -1,142 +0,0 @@
'use strict';
export async function findAvailableSlots(busyA, busyB, constraints) {
const {
addMinutes,
compareAsc,
eachMinuteOfInterval,
isBefore,
isEqual,
isValid,
parseISO,
startOfDay,
} = await import('https://cdn.skypack.dev/date-fns@2.30.0');
const c = constraints || {};
const durationMinutes = (c && c.durationMinutes) || 30;
if (!Number.isInteger(durationMinutes) || durationMinutes <= 0) throw new Error('durationMinutes must be a positive integer');
const range = c && c.searchRange || {};
const rangeStart = parseISO(range.start);
const rangeEnd = parseISO(range.end);
if (!isValid(rangeStart) || !isValid(rangeEnd) || !isBefore(rangeStart, rangeEnd)) throw new Error('Invalid searchRange');
const wh = c && c.workHours || {};
const [whStartH, whStartM] = String(wh.start || '09:00').split(':').map(n => parseInt(n, 10));
const [whEndH, whEndM] = String(wh.end || '17:00').split(':').split(':').map(n => parseInt(n, 10));
const WH_ST = whStartH * 60 + (isNaN(whStartM) ? 0 : whStartM);
const WH_ET = whEndH * 60 + (isNaN(whEndM) ? 0 : whEndM);
if (!(0 <= WH_ST && WH_ST < WH_ET && WH_ET <= 1440)) throw new Error('Invalid workHours');
const toEpoch = d => d.getTime();
const fmtTime = d => {
const t = d.getUTCHours().toString().padStart(2, '0') + ':' + d.getUTCMinutes().toString().padStart(2, '0');
return t;
};
const parseBusy = slot => {
const s = parseISO(slot.start);
const e = parseISO(slot.end);
if (!isValid(s) || !isValid(e) || !isBefore(s, e) && !isEqual(s, e)) throw new Error('Invalid busy slot');
return { s, e };
};
const busy = [...busyA, ...busyB].map(parseBusy)
.filter(({ s, e }) => isBefore(rangeStart, e) && !isBefore(s, rangeEnd))
.map(({ s, e }) => ({ s: isBefore(s, rangeStart) ? rangeStart : s, e: isBefore(rangeEnd, e) ? rangeEnd : e }))
.sort((a, b) => compareAsc(a.s, b.s));
const merged = [];
for (const cur of busy) {
if (merged.length === 0) {
merged.push({ ...cur });
continue;
}
const last = merged[merged.length - 1];
if (isBefore(last.e, cur.s) || isEqual(last.e, cur.s)) {
merged.push({ ...cur });
} else {
if (isBefore(cur.e, last.e)) continue;
last.e = cur.e;
}
}
const findNext = (t, arr, lo, hi) => {
while (lo <= hi) {
const mid = (lo + hi) >> 1;
if (isBefore(arr[mid].e, t) || isEqual(arr[mid].e, t)) lo = mid + 1;
else hi = mid - 1;
}
return lo < arr.length ? lo : -1;
};
const available = [];
let cursor = rangeStart;
let idx = 0;
while (idx < merged.length && !isBefore(rangeEnd, merged[idx].s)) {
const blockStart = merged[idx].s;
const blockEnd = merged[idx].e;
if (isBefore(cursor, blockStart)) {
available.push({ start: cursor, end: blockStart });
}
if (isBefore(blockEnd, cursor)) {
idx = findNext(cursor, merged, idx + 1, merged.length - 1);
if (idx === -1) break;
continue;
}
cursor = blockEnd;
idx = idx + 1;
}
if (isBefore(cursor, rangeEnd)) available.push({ start: cursor, end: rangeEnd });
const isSameUTCDay = (a, b) =>
a.getUTCFullYear() === b.getUTCFullYear() &&
a.getUTCMonth() === b.getUTCMonth() &&
a.getUTCDate() === b.getUTCDate();
const startOfNextDayUTC = d => addMinutes(startOfDay(d), 24 * 60);
const clipSegmentToWorkHours = (segStart, segEnd) => {
const out = [];
let day = startOfDay(segStart);
while (isBefore(day, segEnd)) {
const dayStart = isBefore(day, segStart) ? segStart : day;
const dayNext = startOfNextDayUTC(day);
const dayEnd = isBefore(segEnd, dayNext) ? segEnd : dayNext;
const segST = (dayStart.getUTCHours() * 60 + dayStart.getUTCMinutes()) | 0;
const segET = (dayEnd.getUTCHours() * 60 + dayEnd.getUTCMinutes()) | 0;
const st = Math.max(segST, WH_ST);
const et = Math.min(segET, WH_ET);
if (st < et) {
const clipStart = addMinutes(day, st);
const clipEnd = addMinutes(day, et);
out.push({ start: clipStart, end: clipEnd });
}
day = dayNext;
}
return out;
};
const slots = [];
for (const seg of available) {
const clips = clipSegmentToWorkHours(seg.start, seg.end);
for (const clip of clips) {
const clipLen = Math.floor((toEpoch(clip.end) - toEpoch(clip.start)) / 60000);
const count = Math.floor(clipLen / durationMinutes);
if (count <= 0) continue;
let cur = clip.start;
for (let i = 0; i < count; i++) {
const s = cur;
const e = addMinutes(s, durationMinutes);
if (isBefore(e, clip.end) || isEqual(e, clip.end)) {
if (isBefore(rangeStart, s) && !isBefore(rangeEnd, e)) {
slots.push({ start: s.toISOString(), end: e.toISOString() });
}
}
cur = e;
}
}
}
return slots;
}
export default findAvailableSlots;
// Generation time: 36.069s
// Result: FAIL

View File

@@ -1,56 +0,0 @@
export async function findAvailableSlots(cal1, cal2, { durationMinutes, searchRange, workHours }) {
const { parseISO, compareAsc, addMinutes } = await import('https://cdn.skypack.dev/date-fns');
const s = parseISO(searchRange.start);
const e = parseISO(searchRange.end);
const [whS, whE] = [workHours.start, workHours.end].map(t => {
const [h, m] = t.split(':').map(Number);
return h * 60 + m;
});
const inWH = (d, de) => {
const g = t => t.getUTCHours() * 60 + t.getUTCMinutes();
return g(d) >= whS && g(de) <= whE;
};
const busy = [...cal1, ...cal2]
.map(({ start, end }) => ({ s: parseISO(start), e: parseISO(end) }))
.filter(({ s: bs, e: be }) => compareAsc(be, s) > 0 && compareAsc(bs, e) < 0)
.map(({ s: bs, e: be }) => ({
s: compareAsc(bs, s) < 0 ? s : bs,
e: compareAsc(be, e) > 0 ? e : be
}))
.sort((a, b) => compareAsc(a.s, b.s));
const merged = [];
for (const { s: bs, e: be } of busy) {
const last = merged[merged.length - 1];
if (!last || compareAsc(bs, last.e) >= 0) merged.push({ s: bs, e: be });
else if (compareAsc(be, last.e) > 0) last.e = be;
}
const free = [];
let cur = s;
for (const { s: ms, e: me } of merged) {
if (compareAsc(cur, ms) < 0) free.push({ s: cur, e: ms });
cur = compareAsc(me, cur) > 0 ? me : cur;
}
if (compareAsc(cur, e) < 0) free.push({ s: cur, e });
const slots = [];
for (const { s: fs, e: fe } of free) {
let cs = fs;
while (true) {
const ce = addMinutes(cs, durationMinutes);
if (compareAsc(ce, fe) > 0) break;
if (inWH(cs, ce)) slots.push({ start: cs.toISOString(), end: ce.toISOString() });
cs = addMinutes(cs, durationMinutes);
}
}
return slots;
}
export default findAvailableSlots;
// Generation time: 289.823s
// Result: PASS

View File

@@ -1,38 +0,0 @@
async function findAvailableSlots(a,b,o){
const {default:d}=await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/index.js')
const {default:u}=await import('https://cdn.jsdelivr.net/npm/dayjs@1.11.10/esm/plugin/utc.js')
d.extend(u)
const s=d.utc(o.searchRange.start),e=d.utc(o.searchRange.end)
const S=s.valueOf(),E=e.valueOf(),t=o.durationMinutes*6e4
const busy=[...a,...b].map(v=>[d.utc(v.start).valueOf(),d.utc(v.end).valueOf()]).filter(v=>v[1]>S&&v[0]<E).sort((i,j)=>i[0]-j[0])
const m=[]
for(const [b0,b1]of busy){
const x=Math.max(b0,S),y=Math.min(b1,E)
if(!m.length||x>m[m.length-1][1])m.push([x,y])
else m[m.length-1][1]=Math.max(m[m.length-1][1],y)
}
const r=[],ws=o.workHours.start,we=o.workHours.end
let j=0
for(let day=s.startOf('day');day.isBefore(e);day=day.add(1,'day')){
let w0=d.utc(`${day.format('YYYY-MM-DD')}T${ws}Z`).valueOf(),w1=d.utc(`${day.format('YYYY-MM-DD')}T${we}Z`).valueOf()
if(w1<=S||w0>=E)continue
if(w0<S)w0=S
if(w1>E)w1=E
if(w0>=w1)continue
while(j<m.length&&m[j][1]<=w0)j++
let p=w0
for(let k=j;k<m.length&&m[k][0]<w1;k++){
if(m[k][0]>p){
for(let a=p;a+t<=Math.min(m[k][0],w1);a+=t)r.push({start:new Date(a).toISOString(),end:new Date(a+t).toISOString()})
}
p=Math.max(p,m[k][1])
}
if(p<w1){
for(let a=p;a+t<=w1;a+=t)r.push({start:new Date(a).toISOString(),end:new Date(a+t).toISOString()})
}
}
return r
}
export default findAvailableSlots;
// Generation time: 62.189s
// Result: FAIL

View File

@@ -1,93 +0,0 @@
const loadDay = (() => {
let memo
return () => memo ?? (memo = (async () => {
const [core, utc] = await Promise.all([
import('https://esm.sh/dayjs@1.11.13?target=esnext'),
import('https://esm.sh/dayjs@1.11.13/plugin/utc?target=esnext')
])
const d = core.default
d.extend(utc.default)
return d
})())
})()
const findAvailableSlots = async (c1 = [], c2 = [], cfg = {}) => {
const d = await loadDay()
const u = v => d.utc(v)
const {
durationMinutes: dm,
searchRange: sr = {},
workHours: wh = {}
} = cfg
const dur = Number(dm)
if(!Number.isFinite(dur) || dur <= 0 || !sr.start || !sr.end || !wh.start || !wh.end) return []
const s = u(sr.start)
const e = u(sr.end)
if(!s.isValid() || !e.isValid() || !s.isBefore(e)) return []
const toMin = v => {
const [h, m = 0] = String(v).split(':').map(Number)
if(!Number.isFinite(h) || !Number.isFinite(m)) return null
if(h < 0 || h > 24 || m < 0 || m > 59) return null
if(h === 24 && m > 0) return null
return h * 60 + m
}
const ws = toMin(wh.start)
const we = toMin(wh.end)
if(ws == null || we == null || ws >= we) return []
const list = [
...(Array.isArray(c1) ? c1 : []),
...(Array.isArray(c2) ? c2 : [])
]
const busy = list.map(v => {
if(!v?.start || !v?.end) return null
const st = u(v.start)
const en = u(v.end)
if(!st.isValid() || !en.isValid() || !st.isBefore(en)) return null
if(!en.isAfter(s) || !st.isBefore(e)) return null
return {
start: st.isBefore(s) ? s : st,
end: en.isAfter(e) ? e : en
}
}).filter(Boolean).sort((a, b) => a.start.valueOf() - b.start.valueOf())
const merged = []
for(const slot of busy){
if(!merged.length){
merged.push({...slot})
continue
}
const last = merged[merged.length - 1]
if(slot.start.isAfter(last.end)) merged.push({...slot})
else if(slot.end.isAfter(last.end)) last.end = slot.end
}
const free = []
let cur = s
for(const slot of merged){
if(slot.start.isAfter(cur)) free.push({start: cur, end: slot.start})
if(slot.end.isAfter(cur)) cur = slot.end
}
if(cur.isBefore(e)) free.push({start: cur, end: e})
if(!free.length) return []
const step = dur * 60000
if(!Number.isFinite(step) || step <= 0) return []
const out = []
for(const seg of free){
for(let day = seg.start.startOf('day'); day.isBefore(seg.end); day = day.add(1, 'day')){
const wsStart = day.add(ws, 'minute')
const wsEnd = day.add(we, 'minute')
const start = wsStart.isAfter(seg.start) ? wsStart : seg.start
const end = wsEnd.isBefore(seg.end) ? wsEnd : seg.end
if(!start.isBefore(end)) continue
const limit = end.valueOf()
for(let t = start.valueOf(); t + step <= limit; t += step){
out.push({
start: d.utc(t).toISOString(),
end: d.utc(t + step).toISOString()
})
}
}
}
return out
}
export default findAvailableSlots;
// Generation time: 211.798s
// Result: PASS

View File

@@ -1,96 +0,0 @@
let _lx;
const _loadLuxon = () =>
_lx || (_lx = import("https://cdn.jsdelivr.net/npm/luxon@3.4.4/build/es6/luxon.js"));
async function findAvailableSlots(calendarA, calendarB, constraints) {
const { DateTime } = await _loadLuxon();
const M = 6e4, DAY = 1440, Z = { zone: "utc" };
const a = Array.isArray(calendarA) ? calendarA : [];
const b = Array.isArray(calendarB) ? calendarB : [];
const o = constraints && typeof constraints === "object" ? constraints : {};
const durMin = Number(o.durationMinutes);
if (!(durMin > 0)) return [];
const dur = durMin * M;
const sr = o.searchRange || {};
const rs = DateTime.fromISO(sr.start || "", Z);
const re = DateTime.fromISO(sr.end || "", Z);
if (!rs.isValid || !re.isValid) return [];
const r0 = rs.toMillis(), r1 = re.toMillis();
if (r0 >= r1) return [];
const wh = o.workHours || {};
const toMin = t => {
const [h, m] = String(t).split(":").map(Number);
if (!Number.isFinite(h) || !Number.isFinite(m)) return NaN;
if (h === 24 && m === 0) return DAY;
if (h < 0 || h > 23 || m < 0 || m > 59) return NaN;
return h * 60 + m;
};
const ws = toMin(wh.start), we = toMin(wh.end);
if (!Number.isFinite(ws) || !Number.isFinite(we)) return [];
const clamp = (s, e) => {
s = Math.max(s, r0);
e = Math.min(e, r1);
return s < e ? [s, e] : null;
};
const busy = [];
const add = (s, e) => {
const c = clamp(s, e);
if (c) busy.push(c);
};
const addEvent = ev => {
const s = DateTime.fromISO(ev?.start || "", Z);
const e = DateTime.fromISO(ev?.end || "", Z);
if (!s.isValid || !e.isValid) return;
const x = s.toMillis(), y = e.toMillis();
if (x < y) add(x, y);
};
a.forEach(addEvent);
b.forEach(addEvent);
for (let d = rs.startOf("day"); d.toMillis() < r1; d = d.plus({ days: 1 })) {
const base = d.toMillis();
const off = (x, y) => add(base + x * M, base + y * M);
if (ws === we) off(0, DAY);
else if (ws < we) {
if (ws) off(0, ws);
if (we < DAY) off(we, DAY);
} else off(we, ws);
}
busy.sort((x, y) => x[0] - y[0] || x[1] - y[1]);
const merged = [];
for (const [s, e] of busy) {
const last = merged[merged.length - 1];
if (!last || s > last[1]) merged.push([s, e]);
else last[1] = Math.max(last[1], e);
}
const out = [];
const iso = ms => new Date(ms).toISOString();
const pushSlot = (s, e) => out.push({ start: iso(s), end: iso(e) });
let cur = r0;
for (const [s, e] of merged) {
if (cur < s) {
for (let t = cur; t + dur <= s; t += dur) pushSlot(t, t + dur);
}
if (e > cur) cur = e;
if (cur >= r1) return out;
}
for (let t = cur; t + dur <= r1; t += dur) pushSlot(t, t + dur);
return out;
}
export default findAvailableSlots;
// Generation time: 822.838s
// Result: PASS