Availability primitives
Two functions over half-open [start, end) intervals. Both work on resolved
Interval objects ({ start: Date; end: Date }), and they are what
generateAvailableTimeslots uses internally.
subtract
subtract(source: Interval[], busy: Interval[]): Interval[]Removes the busy intervals from the source (available) intervals and returns
the free time that is left. The busy list can be unsorted, overlapping, or
touching boundaries. A busy window in the middle of a source splits it in two.
Touching boundaries do not count as overlap. It runs in O((n + m) log m) with a
sort and a sweep, not a nested scan.
import { subtract } from 'timeslottr'
const free = subtract(
[{ start: new Date('2024-01-01T09:00:00Z'), end: new Date('2024-01-01T17:00:00Z') }],
[{ start: new Date('2024-01-01T12:00:00Z'), end: new Date('2024-01-01T13:00:00Z') }],
)
// result: [09:00 to 12:00, 13:00 to 17:00]intersect
intersect(intervalSets: Interval[][]): Interval[]Given one set of free intervals per person, it returns the windows where all of
them overlap. With no sets it returns []. With one set it returns a merged copy.
If any set is empty the result is [], since a person with no availability blocks
everyone. It runs in O(total intervals · log) with a sweep, not pairwise nested
loops.
import { intersect } from 'timeslottr'
const team = intersect([
[{ start: new Date('2024-01-01T09:00:00Z'), end: new Date('2024-01-01T15:00:00Z') }],
[{ start: new Date('2024-01-01T11:00:00Z'), end: new Date('2024-01-01T17:00:00Z') }],
])
// result: [11:00 to 15:00]Both return new Interval objects with fresh Date instances, so your inputs are
not mutated. See the Interval type.