Time slots, handled.
Zero-dependency TypeScript library for generating time slots with timezone support, buffers, and exclusions.
Declarative scheduling
Define single-day or multi-day schedules with one function call.
Single Day
1import { generateTimeslots } from 'timeslottr';23const slots = generateTimeslots({4 day: '2024-01-01',5 timezone: 'America/New_York',6 range: { start: '09:00', end: '17:00' },7 slotDurationMinutes: 30,8 excludedWindows: [9 { start: '12:00', end: '13:00' }10 ]11});
Per-Weekday
1import { generateDailyTimeslots, Weekday } from 'timeslottr';23const slots = generateDailyTimeslots(4 { start: '2024-01-01', end: '2024-01-14' },5 {6 range: new Map([7 [Weekday.MON, { start: '09:00', end: '17:00' }],8 [Weekday.TUE, { start: '09:00', end: '17:00' }],9 [Weekday.WED, { start: '09:00', end: '12:00' }],10 [Weekday.THU, { start: '09:00', end: '17:00' }],11 [Weekday.FRI, { start: '10:00', end: '16:00' }],12 ]),13 slotDurationMinutes: 60,14 timezone: 'America/New_York'15 }16);
Try it live
Configure schedule rules and see the generated slots instantly.
Configuration
Adjust the settings to customize slot generation.
No excluded windows defined.
Generated Slots
Preview of the time slots based on your configuration.
JSON Output
The raw data generated by the library.
[]
API Reference
Every exported function, config option, and type.
Core Generation Functions
generateTimeslots(config: TimeslotGenerationConfig): Timeslot[]Generates time slots for a single day (or custom date range). This is the primary function for most use cases.
import { generateTimeslots } from 'timeslottr';const slots = generateTimeslots({day: '2024-03-15',timezone: 'America/New_York',range: { start: '09:00', end: '17:00' },slotDurationMinutes: 30,slotIntervalMinutes: 15, // overlapping 30-min slots every 15 minbufferBeforeMinutes: 10, // 10 min buffer at startbufferAfterMinutes: 10, // 10 min buffer at endexcludedWindows: [{ start: '12:00', end: '13:00' } // lunch break],alignment: 'start',includeEdge: true,maxSlots: 50,labelFormatter: (slot, index) => `Slot #${index + 1}`,});
See the Configuration section for full details on each option.
generateDailyTimeslots(period: TimeslotRangeInput, config: DailyTimeslotConfig): Timeslot[]Generates time slots across multiple days. Supports per-weekday schedules using a Map of Weekday to time ranges, or a single range applied to every day.
import { generateDailyTimeslots, Weekday } from 'timeslottr';// Per-weekday schedule over a 2-week spanconst slots = generateDailyTimeslots({ start: '2024-03-01', end: '2024-03-14' },{range: new Map([[Weekday.MON, { start: '09:00', end: '17:00' }],[Weekday.TUE, { start: '09:00', end: '17:00' }],[Weekday.WED, { start: '09:00', end: '12:00' }],[Weekday.THU, { start: '09:00', end: '17:00' }],[Weekday.FRI, { start: '10:00', end: '16:00' }],// SAT and SUN omitted = no slots generated]),slotDurationMinutes: 60,timezone: 'America/New_York',excludedWindows: [{ start: '12:00', end: '13:00' }],});
The first argument defines the overall date range. Days whose weekday is not in the Map (or mapped to null) are skipped. You can also pass a single TimeslotRangeInput instead of a Map to apply the same hours to every day.
Utility Functions
createTimeslot(start: Date, end: Date): TimeslotCreates a validated Timeslot. Throws TypeError if either date is invalid, or RangeError if end is not after start.
import { createTimeslot } from 'timeslottr';const slot = createTimeslot(new Date('2024-03-15T09:00:00Z'),new Date('2024-03-15T09:30:00Z'));// { start: Date, end: Date }
overlaps(a: Timeslot, b: Timeslot): booleanReturns true if two timeslots overlap in time. Uses half-open interval comparison (start inclusive, end exclusive).
import { overlaps, createTimeslot } from 'timeslottr';const a = createTimeslot(new Date('2024-03-15T09:00:00Z'), new Date('2024-03-15T10:00:00Z'));const b = createTimeslot(new Date('2024-03-15T09:30:00Z'), new Date('2024-03-15T10:30:00Z'));overlaps(a, b); // true
contains(slot: Timeslot, date: Date): booleanReturns true if a date falls within the timeslot. The start is inclusive and the end is exclusive.
import { contains, createTimeslot } from 'timeslottr';const slot = createTimeslot(new Date('2024-03-15T09:00:00Z'),new Date('2024-03-15T10:00:00Z'));contains(slot, new Date('2024-03-15T09:30:00Z')); // truecontains(slot, new Date('2024-03-15T10:00:00Z')); // false (end is exclusive)
mergeSlots(slots: Timeslot[]): Timeslot[]Sorts slots by start time and merges any overlapping or adjacent slots into continuous blocks. Merged slots do not carry metadata from the originals.
import { mergeSlots, createTimeslot } from 'timeslottr';const slots = [createTimeslot(new Date('2024-03-15T09:00:00Z'), new Date('2024-03-15T10:00:00Z')),createTimeslot(new Date('2024-03-15T09:30:00Z'), new Date('2024-03-15T11:00:00Z')),createTimeslot(new Date('2024-03-15T13:00:00Z'), new Date('2024-03-15T14:00:00Z')),];const merged = mergeSlots(slots);// [// { start: 09:00, end: 11:00 }, // first two merged// { start: 13:00, end: 14:00 }, // standalone// ]
findGaps(slots: Timeslot[], range: { start: Date; end: Date }): Timeslot[]Finds free/unbooked time periods within a range, given a list of booked slots. Useful for discovering available scheduling windows.
import { findGaps, createTimeslot } from 'timeslottr';const booked = [createTimeslot(new Date('2024-03-15T09:00:00Z'), new Date('2024-03-15T10:00:00Z')),createTimeslot(new Date('2024-03-15T11:00:00Z'), new Date('2024-03-15T12:00:00Z')),];const gaps = findGaps(booked, {start: new Date('2024-03-15T08:00:00Z'),end: new Date('2024-03-15T13:00:00Z'),});// [// { start: 08:00, end: 09:00 },// { start: 10:00, end: 11:00 },// { start: 12:00, end: 13:00 },// ]
timeslotToJSON(slot: Timeslot): TimeslotJSONConverts a Timeslot to a JSON-safe object with ISO 8601 string dates. Preserves metadata if present.
import { timeslotToJSON } from 'timeslottr';const json = timeslotToJSON(slot);// { start: "2024-03-15T09:00:00.000Z", end: "2024-03-15T09:30:00.000Z", metadata: { ... } }
timeslotFromJSON(json: TimeslotJSON): TimeslotParses a TimeslotJSON object back into a Timeslot with proper Date instances. Validates dates and the start < end constraint.
import { timeslotFromJSON } from 'timeslottr';const slot = timeslotFromJSON({start: "2024-03-15T09:00:00.000Z",end: "2024-03-15T09:30:00.000Z",metadata: { index: 0, durationMinutes: 30 }});// { start: Date, end: Date, metadata: { index: 0, durationMinutes: 30 } }
Configuration
Options for generateTimeslots(). All options except range and slotDurationMinutes are optional.
| Option | Type | Default | Description |
|---|---|---|---|
range | TimeslotRangeInput | required | Start and end boundaries for slot generation. Accepts Date objects, ISO strings, or time-only strings like "09:00". |
slotDurationMinutes | number | required | Length of each slot in minutes. Must be a positive number. |
day | Date | string | - | Calendar date to anchor time-only boundaries. For example, day: '2024-03-15' with range: { start: '09:00', end: '17:00' }. |
slotIntervalMinutes | number | slotDurationMinutes | Step size (in minutes) between the start of consecutive slots. Set lower than slotDurationMinutes to create overlapping slots. |
bufferBeforeMinutes | number | 0 | Minutes trimmed from the start of the usable window before generating slots. |
bufferAfterMinutes | number | 0 | Minutes trimmed from the end of the usable window before generating slots. |
excludedWindows | TimeslotRangeInput[] | - | Sub-ranges to exclude from slot generation (e.g., lunch breaks, blackout periods). Overlapping exclusions are automatically merged. |
timezone | string | - | IANA timezone identifier (e.g., "America/New_York", "Europe/London", "UTC"). Controls how time-only strings are interpreted. |
alignment | 'start' | 'end' | 'center' | 'start' | How to handle leftover time that doesn't fill a complete slot. 'start' discards at the end, 'end' aligns slots backward from the range end, 'center' distributes leftover evenly on both sides. |
minimumSlotDurationMinutes | number | slotDurationMinutes | Minimum duration (in minutes) for partial/edge slots. Only relevant when includeEdge is true. |
includeEdge | boolean | true | Whether to include truncated edge slots (those cut short by the range boundary or exclusions) if they meet the minimum duration. |
maxSlots | number | - | Hard limit on the number of slots generated. Generation stops once this count is reached. |
labelFormatter | (slot, index, durationMinutes) => string | - | Callback to attach a custom label string to each slot's metadata. Return undefined to skip. |
Additional DailyTimeslotConfig options
generateDailyTimeslots() accepts all of the above options (except day, which is set automatically per day), plus:
| Option | Type | Default | Description |
|---|---|---|---|
range | TimeslotRangeInput | Map<Weekday, ...> | required | Either a single time range applied to every day, or a Map<Weekday, TimeslotRangeInput | null> for per-weekday schedules. Weekdays not in the Map are skipped. |
maxDays | number | 10000 | Safety limit on the number of calendar days to iterate. Prevents runaway loops for large date ranges. |
Types
Timeslot
The core output type. Start is inclusive, end is exclusive (half-open interval).
interface Timeslot {start: Date; // inclusiveend: Date; // exclusivemetadata?: {index: number; // 0-based slot indexdurationMinutes: number;label?: string; // from labelFormatter};}
TimeslotJSON
JSON-safe version with ISO string dates. Used with timeslotToJSON() and timeslotFromJSON().
interface TimeslotJSON {start: string; // ISO 8601end: string; // ISO 8601metadata?: {index: number;durationMinutes: number;label?: string;};}
TimeslotBoundaryInput
Flexible input type for time boundaries. Supports multiple formats:
type TimeslotBoundaryInput =| Date // JavaScript Date object| string // ISO string or time-only ("09:00", "17:30")| { date?: Date | string; // optional date anchortime: string | { // time componenthour: number;minute?: number;second?: number;};};
TimeslotRangeInput
A start/end pair of boundaries:
interface TimeslotRangeInput {start: TimeslotBoundaryInput;end: TimeslotBoundaryInput;}
AlignmentStrategy
type AlignmentStrategy = 'start' | 'end' | 'center';
Weekday Enum
Maps to JavaScript's Date.getDay() values (Sunday = 0).
import { Weekday } from 'timeslottr';Weekday.SUN // 0Weekday.MON // 1Weekday.TUE // 2Weekday.WED // 3Weekday.THU // 4Weekday.FRI // 5Weekday.SAT // 6