Skip to content
LyEve UI v0.60.1

Form controls

DateTimePicker

DatePicker and TimePicker as one field, with one label, one hint and one error between them. The value is a local wall clock with no zone, and that is the part a reader gets wrong, so every example below prints what the field is holding.

Basic

The label points at the date trigger, which is a button, so clicking the label opens the calendar. Pick a day and set the clock, and watch the value under the field.

Value: 2026-03-04T15:30

Under a touch screen the date half takes the DatePicker's 44px days in a 320px calendar, and the time half takes the TimePicker's 44px segments. A press on the time half's padding or a colon focuses the nearest segment.

The wire format

There are exactly three shapes the value can take.

  • Unset is the empty string.
  • Without seconds it is 2026-03-04T15:30.
  • With seconds it is 2026-03-04T15:30:45.

Four digit year, two digit zero padded everything else, a T between the halves. No offset, no trailing Z, no fraction. A string that is none of those three empties both halves rather than being guessed at, and a bare 2026-03-04 is read as that day with no time, because that is what a date field's own value carries.

The value carries no offset on purpose. A datetime column is zone aware on PostgreSQL and zone blind on MySQL and SQL Server, so an offset written into the value is kept by one and dropped by the others and one string names a different instant depending on where it lands. The offset a browser can supply is the viewer's own, not the zone the content is scheduled against, so an editor in Jakarta and an editor in New York would write two different strings for the same intent. Converting a wall clock to an absolute instant belongs at the boundary that knows which zone to apply.

A day picked before the clock is touched publishes as that day at midnight, so the calendar never marks a day the page does not hold. A clock reading with no day publishes nothing, because there is no instant to name yet.

Seconds

Value: 2026-03-04T15:30:45

Twelve hour

hour12 changes only what the time half draws. The hour segment runs 1 to 12 beside an AM and PM segment that a click, an arrow key, or A or P sets. The value stays 24-hour, so a form posts the same string either way.

Value: 2026-03-05T14:15

Bounds

min and max bound the whole instant, never a half on its own. With a floor of 4 March at 09:00 and a ceiling of 6 March at 17:00, eight in the morning on the fifth is inside the range and is left alone. A time bound reaches the time half only on the day that bound falls on. A bare date as a bound means the whole of that day: a min starts at its midnight and a max runs to its last second, so the last allowed day is a day the user can actually use.

Between 4 March 09:00 and 6 March 17:00.

Value: (empty)

In a form

Neither half carries a name. name goes on one hidden input holding the whole value, so a submit posts the instant and not the two strings it was built from. The label, the hint and the error belong to the field rather than to either half, which is what keeps three message rows from stacking under one control.

API

PropTypeDefaultDescription
value string''Bindable local wall clock: YYYY-MM-DDTHH:mm, or with seconds YYYY-MM-DDTHH:mm:ss.
label string no defaultField label. Points at the date trigger.
hint string no defaultHelper text below the field. An error replaces it.
error string no defaultError message, reported through the group description.
seconds booleanfalseAdd a seconds segment, and carry seconds in the value.
hour12 booleanfalseDraw the time half 12-hour with an AM and PM segment. The value stays 24-hour.
step number1Arrow-key step for the minute and second segments on the time half.
min string no defaultEarliest allowed instant. A bare date means its midnight.
max string no defaultLatest allowed instant. A bare date means its last second.
required booleanfalseMark as required for form validation. Renders a red asterisk.
disabled booleanfalseDisable both halves.
id string no defaultDOM id the half ids are built from. Generated when omitted.
name string no defaultNative form name. One hidden input carries the whole instant.
class string no defaultExtra classes on the field wrapper.
onchange (value: string) => void no defaultFires whenever the published value changes.
noAI booleanfalseKeep the value from an assistant: a FormAssist never reports it or fills it, and the control is marked data-no-ai for a browser agent.