Skip to content
design
Components

Date and time input

A date and a time of day typed or chosen together, read in one zone that the time states beside it, reported as an instant in UTC.

On this page
Time of the entry
UTC

11 October 2026, 09:30 UTC

Flight home
UTC

A log entry's time in UTC that cannot be after the current time, and a flight home in the device's own zone with its offset.
Code
import { DateTimeInput } from "@bosunkit/components/date-time-input";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";

export const description: string =
  "A log entry's time in UTC, which cannot be after the current time, and a crew member's flight home in the " +
  "device's own zone, printed with its offset. Each reports the instant in UTC.";

export default function Showcase(): ReactElement {
  const [entry, setEntry]: [string, Dispatch<SetStateAction<string>>] = useState<string>("2026-10-11T09:30:00.000Z");
  const [flight, setFlight]: [string, Dispatch<SetStateAction<string>>] = useState<string>("");
  return (
    <div style={{ width: "100%", display: "grid", gap: "var(--space-xl)" }}>
      <DateTimeInput
        label="Time of the entry"
        now={Date.parse("2026-10-11T12:00:00.000Z")}
        max="2026-10-11T12:00:00.000Z"
        value={entry}
        onValueChange={setEntry}
      />
      <DateTimeInput
        label="Flight home"
        zone="local"
        now={Date.parse("2026-10-11T12:00:00.000Z")}
        value={flight}
        onValueChange={setFlight}
      />
    </div>
  );
}

A date and time input holds an instant: a group named by its label, with the date picker for the date and the time picker for the time of day, side by side where there is room. Both read in one zone, which the time states beside it. By default that is UTC, as the log keeps times; zone="local" makes it the device's own zone, printed as its offset from UTC at that instant (UTC+02:00), for a time that belongs to a person ashore. The page always receives the instant as ISO 8601 in UTC. Under the group the instant is written out with its reference: "11 October 2026, 09:30 UTC".

The instant is reported once both parts are set and it is within min and max, or both are cleared. The bounds are instants: an entry that records what has happened takes the current time as max, and a later one is named with the bound and its reference, "Choose a time up to 11 October 2026, 12:00 UTC.". The calendar disables the days outside the bounds. Moving between the parts, or into their calendar and lists, is not leaving the group. A date without a time or a time without a date is named once the crew leave it.

Examples

Empty

Departure
UTC

Empty: a date and a time to type or choose, the time with UTC beside it.
Code
import { DateTimeInput } from "@bosunkit/components/date-time-input";
import type { ReactElement } from "react";

export const description: string = "Empty: a date and a time to type or choose, the time with UTC beside it.";

export default function Empty(): ReactElement {
  return <DateTimeInput label="Departure" now={Date.parse("2026-10-11T12:00:00.000Z")} />;
}

Not after now

Time of the entry
UTC

11 October 2026, 11:40 UTC

With `max` set to the current time, for an entry that records what has happened: a later instant is named with its reference and not reported.
Code
import { DateTimeInput } from "@bosunkit/components/date-time-input";
import type { ReactElement } from "react";

export const description: string =
  "With `max` set to the current time, for an entry that records what has happened: a later instant is named with " +
  "its reference and not reported.";

export default function Latest(): ReactElement {
  return (
    <DateTimeInput
      label="Time of the entry"
      now={Date.parse("2026-10-11T12:00:00.000Z")}
      max="2026-10-11T12:00:00.000Z"
      defaultValue="2026-10-11T11:40:00.000Z"
    />
  );
}

The device's zone

Taxi to the airport
UTC

`zone="local"` reads and writes the device's own time and prints its offset from UTC beside it, for a time that belongs to a person ashore.
Code
import { DateTimeInput } from "@bosunkit/components/date-time-input";
import type { ReactElement } from "react";

export const description: string =
  '`zone="local"` reads and writes the device\'s own time and prints its offset from UTC beside it, for a time that ' +
  "belongs to a person ashore.";

export default function LocalZone(): ReactElement {
  return <DateTimeInput label="Taxi to the airport" zone="local" now={Date.parse("2026-10-11T12:00:00.000Z")} />;
}

Invalid

Departure
UTC
Choose a time from 15 October 2026, 00:00 UTC on.
A date without a time, a time without a date, or an instant past the bounds is named under the group once the crew leave it.
Code
import { DateTimeInput } from "@bosunkit/components/date-time-input";
import type { ReactElement } from "react";

export const description: string =
  "A date without a time, a time without a date, or an instant past the bounds is named under the group once the " +
  "crew leave it.";

export default function Invalid(): ReactElement {
  return (
    <DateTimeInput
      label="Departure"
      now={Date.parse("2026-10-11T12:00:00.000Z")}
      defaultValue="2026-10-14T06:15:00.000Z"
      error="Choose a time from 15 October 2026, 00:00 UTC on."
    />
  );
}

Read-only

Anchored
UTC

10 October 2026, 17:20 UTC

Read-only: the instant shown in its parts, with dashed outlines.
Code
import { DateTimeInput } from "@bosunkit/components/date-time-input";
import type { ReactElement } from "react";

export const description: string = "Read-only: the instant shown in its parts, with dashed outlines.";

export default function ReadOnly(): ReactElement {
  return (
    <DateTimeInput
      label="Anchored"
      readOnly
      now={Date.parse("2026-10-11T12:00:00.000Z")}
      defaultValue="2026-10-10T17:20:00.000Z"
    />
  );
}

Props

PropTypeDefaultDescription
labelrequiredstring–The group's name, translated by the caller: "Departure", "Time of the entry".
valuestring | undefined–The instant as ISO 8601 in UTC (2026-10-11T14:30:00.000Z); "" for none.
defaultValuestring | undefined–The instant it starts with when the caller does not hold value.
onValueChange((iso: string) => void) | undefined–Called with the instant, ISO 8601 in UTC, when both its date and time are set and within the bounds; "" when both are cleared.
minstring | undefined–The earliest instant that can be chosen (ISO 8601).
maxstring | undefined–The latest instant that can be chosen (ISO 8601): the current time for an entry that records what happened.
zoneClockZone | undefined"utc"The clock the date and time are read and written in: utc by default, as the log keeps them; local for the device's time zone, whose offset is printed.
nownumber | undefined–The current instant in epoch milliseconds, in place of the clock's, for Today and Now.
hintstring | undefined–Text under the group, translated by the caller; the instant written out with its reference when unset.
errorstring | undefined–The caller's own error, shown under the group.
requiredboolean | undefined–Whether the group must hold an instant.
disabledboolean | undefined–Whether the group takes no input.
readOnlyboolean | undefined–Whether the group shows its instant without letting the crew change it.
namestring | undefined–The form field name; the form receives the instant, ISO 8601 in UTC.
classNamestring | undefined–Classes added to the group's own.
testIdstring | undefined–data-testid on the group.