Source: https://monarchui.dev/components/date-time-picker/

Components · Date, time and scheduling

# DateTimePicker

A date and a wall-clock time in one field: DatePicker plus a typed 12- or 24-hour time, a step and a time-zone slot.

@rookcom/monarch-react/scheduling

Component

## Interview time with a time zone

## Usage examples

### 12-hour clock (en-US) and sizes

### Disabled

## Purpose

Collects a moment such as a meeting, interview or scheduled send as a date plus a wall-clock time. The value is `YYYY-MM-DDTHH:mm`, the same string as `<input type="datetime-local">`, so it is never converted between time zones by accident; the zone it belongs to is shown in the `timeZone` slot. Import from `@rookcom/monarch-react/scheduling`.

### Use when

- Scheduling a meeting, an interview, a reminder or a send time.
- Any form that today uses `type="datetime-local"`.

### Avoid when

- Only a date: use DatePicker.
- Choosing among offered slots (booking): show the slots as buttons instead of free entry.
- A length of time: use DurationInput.

## Design principles

- Wall-clock values only: the component never converts zones. The product converts to UTC with the zone it shows.
- The clock follows the locale (12 or 24 hours) and accepts either when typed.
- Picking a date keeps the time; picking a date before any time uses `defaultTime`.
- On the boundary day of `min` or `max`, a picked date clamps the time into range; a typed out-of-range time is explained, not silently changed.

## API reference

```tsx
import { DateTimePicker } from '@rookcom/monarch-react/scheduling';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `DateTimePicker` | component | client | `.mn-date-time-picker` |

The ref goes to the root `role="group"` element.

The date part is a DatePicker; its messages can be translated through the same `messages` prop.

Generated from the TypeScript declarations of `packages/react/src/components/scheduling/date-time-picker`.

## Properties

`value`/`onValueChange` (controlled) or `defaultValue`, as `YYYY-MM-DDTHH:mm` or `null`.

`min`/`max` are date-times; days outside are disabled, times outside on the boundary day are rejected.

`timeZone` is a slot: a label ("IST", "Europe/London") or a time-zone select.

`onValidationChange` reports the date part’s errors, `invalid-time`, or `null`.

### `DateTimePicker`

A date and a wall-clock time in one field: a DatePicker plus a typed time in the locale's 12- or 24-hour clock, with arrow-key steps and a slot for the time zone. The value is `YYYY-MM-DDTHH:mm`, the same as `<input type="datetime-local">`, so it never shifts with the device's zone.

| Prop | Type | Description |
| --- | --- | --- |
| `children` | `ReactNode` |  |
| `className` | `string` |  |
| `defaultTime` | `string` | Time used when a date is picked before any time, `HH:mm`. Default `'09:00'`. |
| `defaultValue` | `string \| null` | Initial value (uncontrolled). |
| `disabled` | `boolean` |  |
| `hourCycle` | `HourCycle` | 12- or 24-hour clock. Default: from the locale. |
| `id` | `string` |  |
| `invalid` | `boolean` | Marks both parts invalid. Inside a Field, the Field's `invalid` is used. |
| `isDateDisabled` | `((date: IsoDate) => boolean)` | Blocks extra dates (weekends, holidays). |
| `locale` | `string` | BCP 47 locale for typing, display and the calendar. Default: the runtime's. |
| `max` | `string` | Latest allowed value. |
| `messages` | `Partial<DateTimePickerMessages>` | Text for translation. |
| `min` | `string` | Earliest allowed value, `YYYY-MM-DDTHH:mm`. Earlier days are disabled and earlier times on that day rejected. |
| `name` | `string` | Submits the value (`YYYY-MM-DDTHH:mm`) under this name in a form. |
| `onValidationChange` | `((error: DateTimeValidationError \| null) => void)` | Called when the built-in validation result changes (`null` when valid). |
| `onValueChange` | `((value: IsoDateTime \| null) => void)` | Called with a new valid value, or `null` when the date is cleared. |
| `presets` | `readonly DatePickerPreset[]` | Date quick picks (see DatePicker). The time is kept. |
| `readOnly` | `boolean` |  |
| `required` | `boolean` |  |
| `size` | `"sm" \| "md" \| "lg"` | Control height. Default `md`. |
| `step` | `number` | Minutes the time moves with the Up and Down arrow keys. Default 15. |
| `timeZone` | `ReactNode` | The time zone the value is in, shown after the time: a label such as "IST" or "Asia/Kolkata", or a time-zone select. The picker itself never converts between zones. |
| `today` | `string` | Today's date. Default: the device's. |
| `value` | `string \| null` | The wall-clock date and time (controlled), `YYYY-MM-DDTHH:mm`, or `null`. No time zone: see `timeZone`. |
| `weekStartsOn` | `Weekday` | First day of the calendar week. Default: from the locale. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `hourCycle` | `'12'``'24'` | Clock format. Default: from the locale. |
| `step` | `'minutes'` | How far ↑ and ↓ move the time, snapping to multiples. Default 15. |
| `size` | `'sm'``'md'``'lg'` | Control height. Default `md`. |
| `timeZone` | `'none'``'label'``'control'` | Shown after the time. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Empty | – | Placeholders show the date format and an example time. |
| Invalid time | `aria-invalid="true" on the time input` | Unreadable, or outside min/max on that day; a message explains it. |
| Invalid date | `aria-invalid="true" on the date input` | As DatePicker. |
| Disabled | `disabled, data-disabled` | Both parts. |

## Interaction behaviour

- The date part behaves as DatePicker (typing, calendar, presets, clear). Clearing the date clears the value.
- The time is read on blur and Enter: `14:30`, `2:30 pm`, `2pm`, `1430`, `9.15` all work.
- ↑ and ↓ in the time move to the next or previous multiple of `step`, wrapping at midnight on the same day.
- Typing a time before a date uses today (or the `min` day if later).

## Keyboard support

| Keys | Action |
| --- | --- |
| Tab | Date field, its buttons, then the time field. |
| ↑ / ↓ (time) | Later / earlier by `step` minutes. |
| Enter (time) | Read the typed time. |
| ↓ (date) | Open the calendar (see DatePicker). |

## Accessibility

Role

group containing two textboxes

Accessible name

The group is named by FieldLabel (or

aria-label

); the parts are "<label> date" and "<label> time".

- Each part gets `aria-invalid` and its own error, linked with `aria-describedby`.
- The time zone is visible text next to the time, not only a tooltip.
- Inside a Field, the label, required state and description apply to both parts.

## Responsive behaviour

- Date and time sit in one row and wrap when the container is narrow; on phones the date takes the full width with the time and zone below.
- The calendar popover follows DatePicker’s phone layout.

## Theme support

- Input tokens for both parts; every theme and more-contrast mode applies.
- No motion of its own; the calendar respects reduced motion.

## Do and don't

### Do

- Always show the zone the value is in with `timeZone`, and convert to UTC on the server with that zone.
- Use `step` that matches the product’s slots (15 or 30 minutes for meetings).

### Don't

- Do not pass `new Date().toISOString()` (UTC) as the value; use the wall-clock string in the shown zone.
- Do not hide the time part for all-day events; use DatePicker instead.

## Related components

- [DatePicker](https://monarchui.dev/components/date-picker/): Only a date.
- [TimezoneSelect](https://monarchui.dev/components/timezone-select/): Let people choose the zone shown in the slot.
- [DurationInput](https://monarchui.dev/components/duration-input/): A length of time.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [Field](https://monarchui.dev/components/form-field/), [DatePicker](https://monarchui.dev/components/date-picker/) |

## Testing coverage

- Labelled group with "date" and "time" parts and the zone slot; axe clean.
- 12-hour clock for en-US and through `hourCycle`.
- Typed times in either clock, on Enter and blur.
- ↑/↓ step and snap to multiples.
- Picking a date first uses `defaultTime`; typing a time first uses today.
- Unreadable time flagged without changing the value; `onValidationChange`.
- min/max: days disabled, time clamped on the boundary day, typed times and dates rejected with messages.
- Clearing the date clears the value; controlled value; hidden input; aria-label without Field; disabled.

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/date-time-picker; Cross-browser axe: /components/date-time-picker |

Tests: `packages/react/src/components/scheduling/date-time-picker`

## All example code

### Interview time with a time zone

```tsx
import { useState } from 'react';
import { Field, FieldLabel, FieldDescription } from '@rookcom/monarch-react';
import { DateTimePicker } from '@rookcom/monarch-react/scheduling';

const TODAY = '2026-11-04';

function Interview() {
  const [value, setValue] = useState<string | null>('2026-11-10T14:30');
  return (
    <Field>
      <FieldLabel>Interview</FieldLabel>
      <DateTimePicker
        locale="en-GB"
        today={TODAY}
        min="2026-11-04T09:00"
        step={15}
        timeZone="IST"
        value={value}
        onValueChange={setValue}
      />
      <FieldDescription>Value: {value ?? 'none'}</FieldDescription>
    </Field>
  );
}

<Interview />
```

### 12-hour clock (en-US) and sizes

```tsx
import { Field, FieldLabel } from '@rookcom/monarch-react';
import { DateTimePicker } from '@rookcom/monarch-react/scheduling';

const TODAY = '2026-11-04';

<Field>
  <FieldLabel>Send at</FieldLabel>
  <DateTimePicker
    locale="en-US"
    today={TODAY}
    defaultValue="2026-11-05T08:00"
    timeZone="New York"
  />
</Field>
<DateTimePicker
  aria-label="Reminder"
  size="sm"
  locale="en-GB"
  today={TODAY}
  defaultValue="2026-11-05T17:45"
/>
```

### Disabled

```tsx
import { Field, FieldLabel } from '@rookcom/monarch-react';
import { DateTimePicker } from '@rookcom/monarch-react/scheduling';

const TODAY = '2026-11-04';

<Field disabled>
  <FieldLabel>Starts</FieldLabel>
  <DateTimePicker
    locale="en-GB"
    today={TODAY}
    defaultValue="2026-11-04T13:00"
    timeZone="UTC"
  />
</Field>
```
