Skip to content

Schedule Picker

Date and TimeSince 0.6.0
import { SchedulePicker } from '@absoluteui/components/schedule-picker';
import '@absoluteui/components/date-input/style.css';

Using Angular? See Schedule Picker for Angular.

Chooses when something happens, in one form control: start and end, all-day or timed, in a timezone, optionally repeating. Timed schedules use two DateTime Pickers (instants whose wall-clock times are read in the schedule’s timezone); all-day schedules use two Date Pickers (inclusive days). An optional timezone select re-reads the entered times in another zone; an optional Recurrence Picker (recurrence: true) edits the repetition with the schedule start as its series start.

Moving the start keeps the duration. The value is a ScheduleValue; the form value is iCalendar text (DTSTART, DTEND, RRULE, EXDATE), with scheduleToString and parseSchedule to convert.

  • Event, meeting, booking, or maintenance-window editors.
  • Anything with a start, an end, and optionally a repetition, where timezones matter.
  • A single moment: use DateTime Picker.
  • A range of dates without times: Date Range Picker is lighter.
  • A duration without a start: use Duration Picker.

A meeting with timezone choice and repetition

Section titled “A meeting with timezone choice and repetition”
import { parseInstant } from '@absoluteui/core';
import { SchedulePicker, scheduleToString, type SchedulePickerChangeDetail } from '@absoluteui/components/schedule-picker';
const start = parseInstant('2026-09-29T08:00:00Z');
const end = parseInstant('2026-09-29T09:00:00Z');
const host = document.createElement('div');
document.body.append(host);
if (start && end) {
SchedulePicker.create(host, {
label: 'When',
name: 'when',
value: { allDay: false, start, end, timeZone: 'Europe/London', recurrence: null },
timeZones: ['Europe/London', 'America/New_York', 'Asia/Kolkata'],
recurrence: true,
});
}
host.addEventListener('aui-change', (event) => {
const { value } = (event as CustomEvent<SchedulePickerChangeDetail>).detail;
host.dataset.ical = value ? scheduleToString(value) : '';
});
import { parseSchedule, SchedulePicker } from '@absoluteui/components/schedule-picker';
const saved = 'DTSTART;VALUE=DATE:20261224\nDTEND;VALUE=DATE:20261227';
const host = document.createElement('div');
document.body.append(host);
const picker = SchedulePicker.create(host, { label: 'Office closed', value: parseSchedule(saved), required: true });
export const text = picker.getFormValue();
import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { schedulePicker: { defaultDuration: 30 } } });
import { parseInstant } from '@absoluteui/core';
import { SchedulePicker, scheduleToString, type SchedulePickerChangeDetail } from '@absoluteui/components/schedule-picker';
import '@absoluteui/components/date-input/style.css';
import '@absoluteui/components/calendar/style.css';
import '@absoluteui/components/date-picker/style.css';
import '@absoluteui/components/date-time-picker/style.css';
import '@absoluteui/components/recurrence-picker/style.css';
import '@absoluteui/components/schedule-picker/style.css';
const start = parseInstant('2026-09-29T08:00:00Z');
const end = parseInstant('2026-09-29T09:00:00Z');
const host = document.querySelector<HTMLElement>('#when');
if (host && start && end) {
SchedulePicker.create(host, { label: 'When', name: 'when', recurrence: true, timeZones: ['Europe/London', 'America/New_York'], value: { allDay: false, start, end, timeZone: 'Europe/London', recurrence: null } });
host.addEventListener('aui-change', (event) => {
const { value } = (event as CustomEvent<SchedulePickerChangeDetail>).detail;
host.dataset.ical = value ? scheduleToString(value) : '';
});
}

See section 33 and metadata/schedule-picker/examples.json: meeting with timezone and recurrence, iCalendar round trip, global configuration.

<div class="aui-field aui-schedule-picker [--all-day] [--invalid] [--disabled]"> (consumer host)
<fieldset class="aui-schedule-picker__fieldset" aria-describedby="{help} {error} {duration}">
<legend class="aui-field__label">When</legend>
<div class="aui-schedule-picker__range">
<div class="aui-schedule-picker__part --start"> DateTime Picker "Starts"
<div class="aui-schedule-picker__part --start-date" hidden> Date Picker "Start date"
<div class="aui-schedule-picker__part --end"> DateTime Picker "Ends"
<div class="aui-schedule-picker__part --end-date" hidden> Date Picker "End date"
<p class="aui-schedule-picker__duration">Duration: 1 hr, 30 min</p>
<div class="aui-schedule-picker__row"><label class="…__check"><input type="checkbox" class="…__checkbox"> All day</label>
<div class="aui-schedule-picker__row --zone"><label>Time zone</label><select class="…__select">
<div class="aui-schedule-picker__recurrence"> Recurrence Picker "Repeat" (with recurrence)
<div class="aui-field__description"> <div class="aui-field__error"> <input type="hidden" name="...">
SchedulePicker.create(host: HTMLElement, options: SchedulePickerOptions): SchedulePicker

Global configuration key: schedulePicker (see Configuration).

Option Type Default Description
label string Legend of the fieldset (required accessible name). Required.
hideLabel boolean false Visually hide the legend.
value ScheduleValue | null Timed ({ allDay: false, start: Instant, end: Instant, timeZone, recurrence }) or all-day ({ allDay: true, start: PlainDate, end: PlainDate (inclusive), timeZone, recurrence }).
allowAllDay boolean true Offer the all-day checkbox.
timeZones readonly string[] Timezones offered in a select; omitted: no select.
recurrence boolean false Embed a Recurrence Picker (read at creation). Set at creation only.
name string Form field name of the hidden input (iCalendar text).
description string Help text referenced by the fieldset.
required boolean false An empty schedule is invalid.
disabled boolean false Disables every part; not submitted.
firstDayOfWeek IsoWeekday First column of the calendars and weekday toggles.
validate (value: ScheduleValue | null) => string | null Consumer validation after built-in checks.
icons Partial<{ error: IconRef }> Icon slot overrides.
validationTiming 'blur' | 'input' | 'submit' "blur" When validation messages first appear. Globally configurable.
defaultDuration number 60 Minutes of a new timed schedule after switching off all-day. Globally configurable.
locale string "en-US" Month and weekday names, dates, numbers, the first day of the week, and messages. Globally configurable. Inherited from common configuration.
timezone string "UTC" Timezone of the wall-clock times when the value has none and no select is shown. Globally configurable. Inherited from common configuration.
disambiguation 'compatible' | 'earlier' | 'later' | 'reject' "compatible" DST policy for wall-clock times in a gap or overlap. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" Reading direction; Left/Right arrow keys and chevrons follow it. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement Consumer host.
fieldset HTMLFieldSetElement The labelled fieldset.
startPicker / endPicker DateTimePicker Timed pickers (hidden while all-day).
startDatePicker / endDatePicker DatePicker All-day pickers (hidden while timed).
recurrencePicker RecurrencePicker | null The embedded Recurrence Picker.
value ScheduleValue | null Current schedule; null when empty or invalid.
validity SchedulePickerValidity { valid, error, message }.
timeZone string The timezone of the wall-clock times.
name string | null Form field name.
label string Legend text.
disabled boolean Disabled state.
dirty boolean The iCalendar text differs from the initial value.
touched boolean Focus has left the control.
options Readonly<SchedulePickerOptions> Current options.
Method Description
setValue(value: ScheduleValue | null): void Programmatic value (no events).
update(options: Partial<SchedulePickerOptions>): void Merge and apply options (recurrence cannot change).
reportValidity(): boolean Show messages now and return validity.
setCustomError(message: string | null): void External error; cleared on the next edit.
getFormValue(): string | null iCalendar text, or null.
focus(): void Focus the start.
reset(): void Restore the initial value.
destroy(): void Remove rendering, inner pickers, listeners, and registration.
scheduleToString(value: ScheduleValue): string; parseSchedule(text: string, disambiguation?: Disambiguation): ScheduleValue | null Module functions: iCalendar text of a schedule and back.
Event Detail Bubbles Cancelable Description
aui-change { value: ScheduleValue | null; validity: SchedulePickerValidity } Yes No The user changed a part of the schedule or its validity error.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-field aui-schedule-picker | --invalid | --disabled | --all-day Host and state modifiers.
aui-schedule-picker__fieldset | __range | __part | __part--start | --end | --start-date | --end-date Fieldset and picker hosts.
aui-schedule-picker__duration Duration text (referenced by the fieldset).
aui-schedule-picker__row | __row--zone | __check | __checkbox | __label | __select All-day checkbox and timezone select.
aui-schedule-picker__recurrence Host of the embedded Recurrence Picker.
Slot Default icon
error alert-circle

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-disabled-bg | --aui-color-error-fg | --aui-color-focus-ring | --aui-color-primary | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-control-height-md | --aui-control-padding-x | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-medium | --aui-icon-size-sm | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-md | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-touch-target-min

type ScheduleValue =
| { allDay: false; start: Instant; end: Instant; timeZone: string; recurrence: RecurrenceRule | null }
| { allDay: true; start: PlainDate; end: PlainDate; timeZone: string; recurrence: RecurrenceRule | null };
type SchedulePickerError = 'value-missing' | 'incomplete' | 'invalid-start' | 'invalid-end' | 'end-before-start' | 'recurrence' | 'custom';
interface SchedulePickerValidity { valid: boolean; error: SchedulePickerError | null; message: string }
interface SchedulePickerChangeDetail { value: ScheduleValue | null; validity: SchedulePickerValidity }
declare function scheduleToString(value: ScheduleValue): string;
declare function parseSchedule(text: string, disambiguation?: Disambiguation): ScheduleValue | null;

Pattern: A fieldset composing labelled pickers (APG date picker dialogs), a native checkbox and select, and an optional Recurrence Picker fieldset.

Key Context Action
Tab / Shift+Tab Picker Start, end (with their calendar buttons), all day, timezone, then the recurrence controls.
Picker keys Start / end As DateTime Picker and Date Picker (segments, ArrowUp/ArrowDown, calendar popup).
Space All day Toggles all-day; the pickers switch between times and dates.
Element Role
fieldset group (native)
start/end DateTime Picker or Date Picker groups
all day checkbox (native)
timezone combobox (native select)
Attribute Element Value
aria-describedby fieldset Help text, error message, and the duration text
label inner pickers “Starts” / “Ends” (timed), “Start date” / “End date” (all-day)

Hidden pickers (timed or all-day) are not focusable. Toggling all-day keeps focus on the checkbox.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.3 Contrast (Minimum) AA
1.4.11 Non-text Contrast AA
2.1.1 Keyboard A
2.4.3 Focus Order A
2.4.7 Focus Visible AA
3.2.2 On Input A
3.3.1 Error Identification A
3.3.2 Labels or Instructions A
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • Moving the start moves the end by the same amount, so the duration is kept; the duration text is part of the description.
  • Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.

Timed, all-day, with timezone select, with recurrence, incomplete, end before start, disabled, required, invalid.

From Trigger To
timed start changed end moves by the same amount
timed end changed new duration (kept for later start changes)
timed all day checked days of the start and of the last moment
all-day all day unchecked previous start time (09:00 by default) and duration
any timezone select same wall-clock times in the new zone
any one part empty, end not after start, inner or recurrence error invalid (value null)
  • Keeping the duration when the start moves avoids an extra correction step.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.

Start and end sit side by side when there is room (auto-fit, 14rem minimum) and stack otherwise; reflows at 320 px.

All parts follow direction.

Inner pickers keep their styles; the timezone select uses the control surface; the recurrence section has a top border. Forced colors follow the inner pickers.

configureAbsoluteUI({ components: { schedulePicker: { defaultDuration: 30 } } });

Global options: validationTiming, defaultDuration, plus inherited locale, timezone, disambiguation, direction.

Instance options override global and theme defaults; recurrence is init-only.

Timed values are exact Instants plus timeZone: the inner pickers show and parse wall-clock times in that zone (the value’s zone, else the select, else the timezone option; UTC unless configured) with the DST policy (disambiguation, compatible by default, section 8.5). A typed time in a DST gap is reported by the inner Date Input and resolved by the policy. Changing the zone keeps the wall-clock times (09:00 in London becomes 09:00 in New York). All-day values are dates. The recurrence expands in the schedule’s zone. In iCalendar text, timed values use TZID= and local times; all-day DTEND is the day after the last day (RFC 5545), while ScheduleValue.end is the last day.

error (alert-circle); inner pickers use their calendar icons.

Text through textContent; timezone names from Intl. parseSchedule accepts a strict subset and returns null otherwise.

No persistence or network access. One hidden input with the iCalendar text when name is set; inner pickers are not form controls.

Creates four inner pickers (two hidden) and, with recurrence, a Recurrence Picker. Budget (e2e): create with recurrence < 30 ms per instance.

One schedule per instance.

  • An end equal to the start is invalid for timed schedules; for all-day schedules start and end may be the same day.
  • A timed end at midnight belongs to the previous day when switching to all-day.
  • A value whose timeZone is not in timeZones is added to the select.
  • parseSchedule rejects floating times (no TZID or Z) and all-day DTEND on or before DTSTART.
Code Category Cause
SCHEDULE_PICKER_OPTIONS configuration Empty label; invalid value (kinds, end, timeZone); invalid timeZones, booleans, callbacks, config; changing recurrence after creation.

UI validation errors: value-missing, incomplete, invalid-start, invalid-end, end-before-start, recurrence, custom.

  • One timezone for start and end.
  • No separate duration field.
  • Manual screen-reader verification is pending.
  • Floating times; several ranges in one value.