DateTime Picker
import { DateTimePicker } from '@absoluteui/components/date-time-picker';import '@absoluteui/components/date-input/style.css';Using Angular? See DateTime Picker for Angular.
What it does
Section titled “What it does”A field for an exact moment (date and time of day) with a picker popup. DateTimePicker composes a Date Input (minute granularity) with a trigger button inside the field boundary. The button opens a non-modal role="dialog" popup in the top layer (WAI-ARIA APG date picker dialog) with a Calendar, a listbox of suggested times, and a Done button. The value is an Instant. Typed and picked wall-clock times are resolved the same way: in the configured timezone, using the disambiguation policy for daylight-saving transitions (section 8.5). The picker, not its inner Date Input, is the form control that Form discovers.
When to use
Section titled “When to use”- Exact moments users may type or look up: appointments, departures, deadlines with a time.
- Scheduling forms that need min/max instants, unavailable days, and explicit timezone handling.
When not to use
Section titled “When not to use”- A date without a time: use Date Picker.
- A time of day without a date or timezone (opening hours): use Time Picker.
- Moments whose timezone the user chooses per value: pair this picker with a timezone select and update
timezone. - Date-time ranges: not supported yet (use a Date Range Picker with Time Pickers).
Examples
Section titled “Examples”Required departure in New York with bounds
Section titled “Required departure in New York with bounds”import { instantToString, parseInstant } from '@absoluteui/core';import { DateTimePicker, type DateTimePickerChangeDetail } from '@absoluteui/components/date-time-picker';
const host = document.createElement('div');document.body.append(host);DateTimePicker.create(host, { label: 'Departure', name: 'departure', required: true, timezone: 'America/New_York', min: parseInstant('2026-01-01T05:00:00Z'), max: parseInstant('2027-01-01T05:00:00Z'),});host.addEventListener('aui-change', (event) => { const { value, validity } = (event as CustomEvent<DateTimePickerChangeDetail>).detail; host.dataset.value = value && validity.valid ? instantToString(value) : '';});Reject nonexistent and repeated local times
Section titled “Reject nonexistent and repeated local times”import { DateTimePicker } from '@absoluteui/components/date-time-picker';
const host = document.createElement('div');document.body.append(host);const picker = DateTimePicker.create(host, { label: 'Maintenance window', timezone: 'Europe/Berlin', disambiguation: 'reject', step: 15, hourCycle: 'h23' });host.addEventListener('aui-change', () => { if (picker.validity.error === 'nonexistent-time' || picker.validity.error === 'ambiguous-time') picker.reportValidity();});Hourly suggestions that keep the popup open
Section titled “Hourly suggestions that keep the popup open”import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ common: { timezone: 'Asia/Kolkata' }, components: { dateTimePicker: { step: 60, closeOnSelect: false } } });import { instantToString } from '@absoluteui/core';import { DateTimePicker } from '@absoluteui/components/date-time-picker';import '@absoluteui/components/date-input/style.css';import '@absoluteui/components/calendar/style.css';import '@absoluteui/components/date-time-picker/style.css';
const host = document.querySelector<HTMLElement>('#pickup');if (host) { const picker = DateTimePicker.create(host, { label: 'Pickup', name: 'pickup', timezone: 'Europe/Berlin', step: 15 }); host.addEventListener('aui-change', () => { host.dataset.utc = picker.value ? instantToString(picker.value) : ''; });}More usage
Section titled “More usage”import { parseInstant } from '@absoluteui/core';import { DateTimePicker } from '@absoluteui/components/date-time-picker';
const host = document.createElement('div');document.body.append(host);DateTimePicker.create(host, { label: 'Departure', name: 'departure', required: true, timezone: 'America/New_York', min: parseInstant('2026-01-01T05:00:00Z') });Anatomy
Section titled “Anatomy”<div class="aui-date-time-picker" data-aui-control> (consumer host; registered with Form) <div class="aui-date-time-picker__field aui-date-input"> (inner Date Input host; not registered) <div class="aui-date-input__label"> <div class="aui-date-input__group" role="group"> <div class="aui-date-input__fields"> date and time segments ... </div> <div class="aui-date-time-picker__trailing"> (Date Input `trailing` option) <button class="aui-date-time-picker__button" aria-haspopup="dialog" aria-expanded aria-controls aria-label="Choose date and time"> <div class="aui-date-time-picker__popup" role="dialog" aria-modal="false" aria-label="Choose date and time" popover="manual"> <div class="aui-date-time-picker__body"> <div class="aui-date-time-picker__calendar aui-calendar"> ... </div> <div class="aui-date-time-picker__time"> <div class="aui-date-time-picker__time-label">Time</div> <div class="aui-date-time-picker__listbox" role="listbox" tabindex="0" aria-labelledby aria-activedescendant> <div class="aui-date-time-picker__option" role="option" aria-selected [aria-disabled]>9:00 AM</div> ... <div class="aui-date-time-picker__footer"><button class="aui-date-time-picker__done">Done</button></div> <div class="aui-date-input__description"> <div class="aui-date-input__hint"> <div class="aui-date-input__error"> <input type="hidden" name>The popup sits inside the field group in the DOM (it renders in the top layer). Moving focus between the segments, the button, and the popup therefore does not mark the field touched, and a picker inside a modal Dialog stays inside the dialog’s subtree.
API reference
Section titled “API reference”Factory
Section titled “Factory”DateTimePicker.create(host: HTMLElement, options: DateTimePickerOptions): DateTimePickerGlobal configuration key: dateTimePicker (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Field label and accessible name of the segment group. Required. | |
value |
Instant | null |
null |
Initial value; also the value restored by reset(). |
min |
Instant | null |
Inclusive lower bound (validation, calendar days, and suggested times). | |
max |
Instant | null |
Inclusive upper bound (validation, calendar days, and suggested times). | |
name |
string |
Form field name for the hidden input carrying the ISO 8601 instant (Z). | |
description |
string |
Help text referenced by aria-describedby. | |
required |
boolean |
false |
Empty value is invalid (value-missing). |
disabled |
boolean |
false |
Disables the segments and the trigger button. |
readOnly |
boolean |
false |
Prevents editing; the trigger button is disabled. |
isDateDisabled |
(date: PlainDate) => boolean |
Disable days in the calendar; a typed disabled day is invalid (‘This date is not available.’). | |
businessCalendar |
BusinessCalendarEngine | null |
Business calendar (since 0.9.0): non-working days and holidays are marked and named in the calendar. | |
disableNonWorking |
boolean |
"false" |
With businessCalendar: non-working days cannot be picked or typed (since 0.9.0). |
firstDayOfWeek |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
First calendar column (ISO weekday). Default: the locale’s first day of week. | |
icons |
Partial<{ calendar: IconRef }> |
Icon slot overrides. | |
formControl |
boolean |
true |
Advanced, read at creation: false skips registration with Form, for composite controls such as Schedule Picker that register themselves (since 0.6.0). Set at creation only. |
closeOnSelect |
boolean |
true |
Close the popup after a time is picked (picking a day keeps it open). Globally configurable. |
step |
number (integer 1..720, minutes) |
30 |
Minutes between suggested times, from midnight. Globally configurable. |
hourCycle |
'auto' | 'h12' | 'h23' |
"auto" |
Clock of the segments and the suggested times; ‘auto’ follows the locale. Globally configurable. |
locale |
string |
"en-US" |
Segment order, calendar, time labels, messages, formatting; forwarded to both children. Globally configurable. Inherited from common configuration. |
timezone |
string (IANA) |
"UTC" |
Zone in which wall-clock input is resolved and values are displayed; also today in the calendar. Globally configurable. Inherited from common configuration. |
disambiguation |
'compatible' | 'earlier' | 'later' | 'reject' |
"compatible" |
DST policy for nonexistent and repeated wall-clock times (typed or picked). Globally configurable. Inherited from common configuration. |
direction |
'ltr' | 'rtl' |
"ltr" |
Arrow-key mirroring, calendar direction, and popup alignment. Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
host |
HTMLElement |
Consumer host; its children are owned by the component. Registered with Form. |
input |
DateInput |
The inner Date Input (minute granularity); not registered with Form. |
calendar |
Calendar |
The Calendar inside the popup. |
button |
HTMLButtonElement |
Trigger button inside the field boundary. |
popup |
HTMLDivElement |
role=dialog aria-modal=false popup (popover=manual). |
listbox |
HTMLDivElement |
role=listbox of suggested times (aria-activedescendant). |
doneButton |
HTMLButtonElement |
Done button that closes the popup. |
value |
Instant | null |
Current valid value, or null when empty or invalid. |
validity |
DateInputValidity |
{ valid, error, message, localTimeStatus }. |
isOpen |
boolean |
Whether the popup is open. |
name |
string | null |
Form field name. |
label |
string |
Label text. |
disabled |
boolean |
Disabled state. |
dirty |
boolean |
Value differs from the initial value. |
touched |
boolean |
Focus has left the field (segments, button, and popup) at least once. |
options |
Readonly<DateTimePickerOptions> |
Current instance options. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
open(): void |
Open the popup and focus the selected day (or today). No-op when disabled or read-only. |
close(): void |
Close the popup; focus inside the popup returns to the trigger button. |
setValue(value: Instant | null): void |
Programmatic value (no aui-change). |
update(options: Partial<DateTimePickerOptions>): void |
Merge and apply options to the field, calendar, and listbox. |
reportValidity(): boolean |
Show messages now and return validity. |
setCustomError(message: string | null): void |
External error (server, cross-field rule); cleared when the user changes the value. |
getFormValue(): string | null |
ISO 8601 instant with Z, or null when empty or invalid. |
focus(): void |
Focus the first segment. |
reset(): void |
Close, restore the initial value, and clear touched/submitted state (no aui-change). |
destroy(): void |
Close and remove rendering, listeners, registration, and subscriptions of the picker and its children. |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-change |
{ value: Instant | null; validity: DateInputValidity } |
Yes | No | User typing or picking changed the value, validity, or DST status. Dispatched from the picker host. |
aui-navigate |
{ month: { year: number; month: number } } |
Yes | No | Bubbles from the inner Calendar when the user changes the visible month. |
aui-open |
null |
No | No | The popup opened. |
aui-close |
null |
No | No | The popup closed. |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-date-time-picker |
Host; –disabled modifier. |
aui-date-time-picker__field |
Host of the inner Date Input (aui-date-input classes apply inside). |
aui-date-time-picker__trailing |
Button and popup container inside the Date Input group. |
aui-date-time-picker__button |
Trigger button. |
aui-date-time-picker__popup |
Top-layer dialog popup. |
aui-date-time-picker__body |
Calendar and time column (wraps on narrow viewports). |
aui-date-time-picker__calendar |
Calendar host (aui-calendar classes apply inside). |
aui-date-time-picker__time |
Time column. |
aui-date-time-picker__time-label |
Visible ‘Time’ label naming the listbox. |
aui-date-time-picker__listbox |
Scrollable listbox of suggested times. |
aui-date-time-picker__option |
Suggested time; –active modifier for the active descendant; aria-selected and aria-disabled states. |
aui-date-time-picker__footer |
Footer holding the Done button. |
aui-date-time-picker__done |
Done button. |
Icon slots
Section titled “Icon slots”| Slot | Default icon |
|---|---|
calendar |
calendar |
Design tokens
Section titled “Design tokens”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-focus-ring | --aui-color-hover | --aui-color-selected | --aui-color-selected-text | --aui-color-surface | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-control-height-md | --aui-control-height-sm | --aui-duration-fast | --aui-easing-standard | --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-radius-md | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-space-3
TypeScript definitions
Section titled “TypeScript definitions”interface DateTimePickerConfig { closeOnSelect: boolean; step: number; hourCycle: HourCycle }type HourCycle = 'auto' | 'h12' | 'h23';interface DateTimePickerIcons { calendar: IconRef }interface DateTimePickerChangeDetail { value: Instant | null; validity: DateInputValidity }type Weekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;Accessibility
Section titled “Accessibility”Pattern: WAI-ARIA APG Date Picker Dialog, non-modal variant, extended with an APG listbox of times: a labelled segmented date-time field (Date Input), a button with aria-haspopup=dialog, and a role=dialog popup containing the Calendar grid, a role=listbox of suggested times (aria-activedescendant), and a Done button.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Enter / Space | Trigger button | Open (or close) the popup; focus moves to the selected day, else today. |
| Alt+ArrowDown | Segment focused | Open the popup (the segment value does not change). |
| Calendar keys | Day focused | Arrows, Home/End, PageUp/PageDown, Shift+PageUp/PageDown as in Calendar. |
| Enter / Space | Day focused | Pick the day, keeping the time of day (09:00 or the first available time when empty). The popup stays open. |
| ArrowUp / ArrowDown / Home / End / PageUp / PageDown | Time listbox | Move the active time. |
| Printable characters | Time listbox | Typeahead to the first matching time. |
| Enter / Space | Time listbox | Pick the active time, keeping the date; closes and focuses the trigger (closeOnSelect). |
| Tab / Shift+Tab | Popup open | Month buttons, the day, the time listbox, then Done; leaving the popup (other than to the trigger) closes it. |
| Enter / Space | Done | Close the popup and focus the trigger. |
| Escape | Popup open | Close the popup and return focus to the trigger. Inside a Dialog, only the popup closes. |
| Date Input keys | Segment focused | As in Date Input. |
| Element | Role |
|---|---|
| segment group | group (Date Input) |
| trigger button | button (native), aria-haspopup=dialog |
| popup | dialog (aria-modal=false) |
| calendar | grid (Calendar) |
| time list | listbox with option children |
| Done | button (native) |
| icons | none (aria-hidden=true) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-label |
trigger button | “Choose date and time”, or “Change date and time, <long date and short time>” when a value is set |
aria-expanded |
trigger button | “true” while the popup is open |
aria-controls |
trigger button | Popup id |
aria-label |
popup | Localized “Choose date and time” |
aria-modal |
popup | “false” (non-modal; the page stays reachable) |
aria-labelledby |
listbox | Visible “Time” label |
aria-activedescendant |
listbox | Active option id while open |
aria-selected |
option | “true” for the field’s time of day |
aria-disabled |
option | “true” when the time resolves outside min/max on the current date |
aria-describedby |
segment group | Help text, time-zone and DST hint, and error (Date Input) |
Opening moves focus into the calendar grid; closing with focus inside the popup returns it to the trigger button before the popup hides. Outside pointer presses close the popup. Moving focus between the segments, button, and popup does not mark the field touched.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- The segments remain the primary input; the popup is an alternative (APG).
- The popup is non-modal: focus is not trapped, and the rest of the page stays reachable.
- DST adjustments are explained in text (hint referenced by aria-describedby), not only by the changed value.
- The time zone is always stated in the hint, so the meaning of the wall-clock time is explicit.
- The active time has an outline, not only a color change; unavailable times are also struck through.
- Screen-reader behavior of the popup (dialog announcement, listbox inside a dialog) has not yet been manually verified with NVDA, JAWS, and VoiceOver.
- Hint changes after picking are conveyed through aria-describedby and are not announced live.
Behavior
Section titled “Behavior”States
Section titled “States”empty, partial (incomplete), valid, valid with a DST hint (adjusted or repeated time), invalid (Date Input errors including nonexistent-time/ambiguous-time under 'reject', unavailable day, custom), open, disabled, read-only.
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| closed | button, or Alt+ArrowDown on a segment | open; focus on the selected day or today; the active time is the field’s time (else 09:00); aui-open |
| open | pick a day | the date changes and the time of day is kept (09:00, or the first available time, when empty); aui-change if changed; stays open |
| open | pick a time | the time changes and the date is kept (the focused day when empty); aui-change if changed; closed and focus on the button (closeOnSelect) |
| open | Done, or Escape | closed; focus on the button; aui-close |
| open | pointer outside, or Tab out of the popup | closed |
| any | user types | aui-change; the calendar and listbox follow while open |
| any | reset() |
closed, initial value, pristine, untouched |
Accessibility notes
Section titled “Accessibility notes”- The segments stay the primary input; the popup is an alternative.
- The popup is non-modal: focus is not trapped and the page stays reachable. Escape and outside presses close it through the shared overlay stack, so only the topmost overlay responds.
- The time zone is always stated in the hint (for example “Time zone: Eastern Daylight Time (GMT-04:00)”). DST adjustments are explained in text, the same way for typed and picked input.
- The active time has an outline, and unavailable times are struck through, so neither state relies on color alone.
- Manual screen-reader verification is pending.
Responsive behavior
Section titled “Responsive behavior”The field fits its content like Date Input. The popup is positioned under the field (bottom-start), flips above when there is no room, and scrolls when taller than the available height (--aui-available-height). On narrow viewports the time column wraps below the calendar and keeps a minimum height of four options.
Right-to-left
Section titled “Right-to-left”Segment order follows the locale. The trigger sits at the inline end of the field. The popup aligns to the field’s inline start, the calendar mirrors arrows and chevrons, and the time column follows the calendar in inline order.
Theming
Section titled “Theming”Role tokens only; verified in the light, dark, and high-contrast themes. In forced-colors mode the popup has a CanvasText border, the selected time uses Highlight/HighlightText, and unavailable times use GrayText.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ common: { timezone: 'America/New_York' }, components: { dateTimePicker: { step: 15, hourCycle: 'h23' } } });Changes apply to existing instances immediately. Changes that affect only step or closeOnSelect do not rebuild the field, so partially typed input is kept.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All options are dynamic.
Timezones
Section titled “Timezones”Values are Instant. Wall-clock input is resolved in the configured timezone (default UTC, never the host zone) with disambiguation. Picked input goes through the same Date Input code as typed input.
| Case (America/New_York) | compatible (default) |
earlier |
later |
reject |
|---|---|---|---|---|
| 2026-03-08 02:30 (does not exist) | 03:30 EDT (07:30Z), hint “Adjusted to 3:30 AM…” |
01:30 EST (06:30Z) |
03:30 EDT | error nonexistent-time |
| 2026-11-01 01:30 (occurs twice) | 01:30 EDT (05:30Z), hint “…the earlier occurrence (GMT-4) is used.” |
01:30 EDT | 01:30 EST (06:30Z) |
error ambiguous-time |
The segments keep the wall-clock time the user chose, so changing disambiguation or timezone re-resolves it. min and max are instants; the calendar disables days whose local date (in timezone) is outside them, and the listbox disables times that resolve outside them. “Today” in the calendar is today in timezone.
icons.calendar (default calendar).
Security
Section titled “Security”All text uses textContent and Intl formatting; no HTML parsing. The popup uses the Popover API; no inline styles or handlers. CSP-safe.
Data handling
Section titled “Data handling”No persistence, network access, or telemetry. When name is set, the inner Date Input’s hidden input carries the ISO 8601 instant with Z.
Performance
Section titled “Performance”One Date Input, one Calendar, and one listbox per picker, created once. The options (1440 / step, 48 by default) are rebuilt only when locale, the hour cycle, or step changes. Picking updates their states in place. Budget: under 15 ms average per DateTimePicker.create across 100 instances.
Supported data scale
Section titled “Supported data scale”100 pickers per page; step: 1 (1440 options) is supported but slower to scroll.
Edge cases
Section titled “Edge cases”- Picking a day in an empty field uses 09:00 (or the first available time at or after it). Picking a time in an empty field uses the calendar’s focused day (today in
timezoneunless the user navigated). - Picking 2026-03-08 while the field shows 02:30 in New York keeps “02:30” in the segments, stores 03:30 EDT, and shows the adjustment hint. With
'reject'the value isnulland the error is shown immediately. - A time with a DST error under
'reject'stays listed and pickable, so the user sees why it is invalid. Picking another time keeps the date. - Re-picking the current day or time emits no
aui-change. - Inside a modal Dialog, the first Escape closes the popup and the second closes the dialog.
- Opening from an empty required field does not show “This field is required.” until focus leaves the whole field.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
DATE_TIME_PICKER_OPTIONS |
configuration | Missing label; value/min/max not an Instant; min > max; non-function isDateDisabled. |
CALENDAR_OPTIONS |
configuration | firstDayOfWeek outside 1-7. |
CFG_* |
configuration | Invalid locale, timezone, disambiguation, direction, step, hourCycle, or closeOnSelect. |
Validation (validity.error) |
validation | Date Input errors (incomplete, invalid-date, value-missing, range-underflow, range-overflow, nonexistent-time, ambiguous-time) and custom (consumer error, or “This date is not available.”). |
A failed construction leaves no rendering, listeners, registrations, or subscriptions behind.
Known limitations
Section titled “Known limitations”- Gregorian calendar only; years 1 to 9999; minute precision.
- Suggested times start at midnight every
stepminutes; they are not offset tomin. - Validation messages follow Date Input timing;
validationTimingis not supported. - A typed unavailable day is reported as
custom; a consumer custom error takes precedence over it. - No Cancel button: picking commits, and Done only closes.
- DST hints are referenced by
aria-describedbyand are not announced live. - Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.
Unsupported scenarios
Section titled “Unsupported scenarios”- Seconds or milliseconds.
- Choosing a timezone inside the picker.
- HTML in the label, help text, or messages.