mirror of
https://github.com/multipleof4/lynchmark.git
synced 2026-01-14 08:37:56 +00:00
Docs: Update benchmark results
This commit is contained in:
@@ -1,74 +1,78 @@
|
||||
const findAvailableSlots = async (cal1, cal2, constraints) => {
|
||||
const { DateTime, Interval } = await import('https://cdn.skypack.dev/luxon');
|
||||
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')
|
||||
]);
|
||||
|
||||
const { durationMinutes: dur, searchRange: sr, workHours: wh } = constraints;
|
||||
const [srStart, srEnd] = [DateTime.fromISO(sr.start), DateTime.fromISO(sr.end)];
|
||||
const [whStart, whEnd] = wh.start.split(':').map(Number);
|
||||
|
||||
const toInterval = ({ start, end }) =>
|
||||
Interval.fromDateTimes(DateTime.fromISO(start), DateTime.fromISO(end));
|
||||
|
||||
const busySlots = [...cal1, ...cal2]
|
||||
.map(toInterval)
|
||||
.sort((a, b) => a.start - b.start);
|
||||
|
||||
const merged = busySlots.reduce((acc, curr) => {
|
||||
if (!acc.length) return [curr];
|
||||
const last = acc[acc.length - 1];
|
||||
return last.overlaps(curr) || last.abutsStart(curr)
|
||||
? [...acc.slice(0, -1), last.union(curr)]
|
||||
: [...acc, curr];
|
||||
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 isWorkHours = dt => {
|
||||
const h = dt.hour, m = dt.minute;
|
||||
const mins = h * 60 + m;
|
||||
const whStartMins = whStart[0] * 60 + whStart[1];
|
||||
const whEndMins = whEnd[0] * 60 + whEnd[1];
|
||||
return mins >= whStartMins && mins < whEndMins;
|
||||
};
|
||||
|
||||
|
||||
const slots = [];
|
||||
let curr = srStart;
|
||||
|
||||
while (curr < srEnd) {
|
||||
const dayStart = curr.set({ hour: whStart[0], minute: whStart[1], second: 0 });
|
||||
const dayEnd = curr.set({ hour: whEnd[0], minute: whEnd[1], second: 0 });
|
||||
|
||||
if (dayStart >= srEnd) break;
|
||||
|
||||
const effectiveStart = dayStart > srStart ? dayStart : srStart;
|
||||
const effectiveEnd = dayEnd < srEnd ? dayEnd : srEnd;
|
||||
|
||||
let pointer = effectiveStart;
|
||||
|
||||
while (pointer.plus({ minutes: dur }) <= effectiveEnd) {
|
||||
const slotEnd = pointer.plus({ minutes: dur });
|
||||
const slotInterval = Interval.fromDateTimes(pointer, slotEnd);
|
||||
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;
|
||||
|
||||
const isFree = !merged.some(busy =>
|
||||
busy.overlaps(slotInterval) || slotInterval.overlaps(busy)
|
||||
);
|
||||
|
||||
if (isFree && isWorkHours(pointer) && isWorkHours(slotEnd.minus({ minutes: 1 }))) {
|
||||
slots.push({
|
||||
start: pointer.toISO(),
|
||||
end: slotEnd.toISO()
|
||||
});
|
||||
pointer = slotEnd;
|
||||
} else {
|
||||
const nextBusy = merged.find(busy => busy.start > pointer);
|
||||
if (nextBusy && nextBusy.start < effectiveEnd) {
|
||||
pointer = nextBusy.end > pointer ? nextBusy.end : pointer.plus({ minutes: 1 });
|
||||
} else {
|
||||
pointer = pointer.plus({ minutes: 1 });
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
curr = curr.plus({ days: 1 }).startOf('day');
|
||||
currentDay = currentDay.add(1, 'day');
|
||||
}
|
||||
|
||||
|
||||
return slots;
|
||||
};
|
||||
}
|
||||
export default findAvailableSlots;
|
||||
Reference in New Issue
Block a user