UIDateTimeEdit is one segmented UITextInput surface with date, time, and local date-time
modes. UIDatePicker adds a calendar popup, UITimePicker configures time mode without a
popup, and UIDateTimePicker combines a calendar and time editor in one popup.
UICalendar can also be used as a standalone widget.
<datepicker id="birthday" value="1990-06-17" />
<timepicker id="start" value="20:30:00" minute-step="15" />
<datetimepicker id="scheduled" value="2026-09-28T20:30:00" />
<datetimeedit mode="date" value="2026-09-28" />
<calendar first-day-of-week="1" value="2026-09-28" />Values are optional typed civil values: CalendarDate, TimeOfDay, and LocalDateTime,
from <eepp/system/datetime.hpp>. They do not carry time zones. Use setDate(), setTime(),
or setDateTime() and the corresponding optional getters. clear() removes a value when
allow-empty is true (the default). Canonical setters clamp to the active mode's bounds and
emit OnValueChange only when the typed value changes. The value property exposes ISO
serialization for ordinary data binding. Hidden seconds and milliseconds remain part of the
value; serialization preserves nonzero milliseconds even if they are not displayed.
DateTimeFormatter in <eepp/system/datetimeformat.hpp> formats ISO values independently of
the display locale and parses them with an explicit pattern:
using namespace EE;
using namespace EE::System;
const CalendarDate date{ 2026, 9, 28 };
const std::string storedDate = DateTimeFormatter::toISODate( date ); // "2026-09-28"
const auto restoredDate = DateTimeFormatter::parseDate( storedDate, DateTimeFormat( "yyyy-MM-dd" ) );
const LocalDateTime value{ date, { 20, 14, 32, 125 } };
const std::string storedValue = DateTimeFormatter::toISOLocalDateTime( value, true );
const auto restoredValue = DateTimeFormatter::parseDateTime(
storedValue, DateTimeFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS" ) );
// The application knows this value uses UTC-03:00; the types do not store a timezone.
const Int32 utcOffsetSeconds = -3 * 60 * 60;
const auto timestamp = value.toUnixTimestampMilliseconds( utcOffsetSeconds );
if ( timestamp ) {
const auto restored = LocalDateTime::fromUnixTimestampMilliseconds(
*timestamp, utcOffsetSeconds );
}Unix timestamps count seconds or milliseconds since 1970-01-01T00:00:00Z, without leap
seconds. CalendarDate::toUnixTimestamp() uses midnight UTC; fromUnixTimestamp() extracts
the UTC date containing that instant. LocalDateTime conversions default to UTC and accept an offset
in seconds, where local = UTC + offset. The offset is not inferred from the
system timezone and does not resolve daylight-saving gaps or ambiguous times. Applications
using named timezones must resolve the offset for the particular date/time before conversion.
The seconds methods discard milliseconds, rounding down even before the epoch. The
toUnixTimestampMilliseconds() / fromUnixTimestampMilliseconds() methods preserve them.
Conversions return std::optional: invalid civil values, an out-of-range year, or an
unrepresentable Int64 millisecond timestamp produce std::nullopt. The full Int32 year range
fits in Int64 seconds, but only part of it fits in Int64 milliseconds. These conversions are
independent of locale, time_t size, and the machine's timezone. For a date-only database
column, ISO date text or typed date fields preserve its civil meaning directly.
Click a section to select it. Left/Right navigate sections; Home/End select the first/last.
Up/Down step the active section. Numeric entry automatically advances when a complete value
has been entered. Pending digits are shown with underscores, commit on navigation, Enter,
focus loss, or timeout, and can be canceled with Escape. Configure the timeout in C++ with
setPendingDigitTimeout() (default: one second). Delete/Backspace clear the value when allowed.
Copy and select-all use normal input commands. Paste replaces the whole value, including when
only one section is selected, and accepts ISO regardless of the display format. Invalid input
preserves the previous valid value. Wheel edits require both focus and wheel-editing: true;
wheel editing is disabled by default. allow-editing: false prevents user mutation.
Month/year edits clamp the day at month ends. Time-only arithmetic wraps within the day; local date-time arithmetic carries across date boundaries. AM/PM entry changes the period without advancing the date. Utility arithmetic outside the representable Int32 year range leaves the value unchanged.
Display defaults come from DateTimeLocale::system() (Windows regional settings on Windows), with a centralized locale-name fallback
for information unavailable through the standard library. setLocale() accepts explicit date
order/separator, hour cycle, month and weekday names, AM/PM labels, and first weekday; this also
supports locales unavailable on the host. fromLocaleName() resolves ordering and cycle
fallbacks; applications can supply translated names in the returned structure.
| Property | Meaning |
|---|---|
mode |
date, time, or datetime on the shared editor |
date-format, time-format |
locale (default), or quoted token pattern |
hour-cycle |
locale, 12, or 24 |
show-seconds |
Show seconds without milliseconds; default false |
show-milliseconds |
Show milliseconds and seconds; default false |
hour-step, minute-step, second-step |
Positive integer steps |
min-date, max-date |
Date-mode ISO bounds |
min-time, max-time |
Time-mode ISO bounds |
min-datetime, max-datetime |
Local date-time ISO bounds |
allow-empty, wheel-editing |
Empty-value and focused wheel editing policies |
active-section |
none, day, month, year, hour, minute, second, millisecond, am-pm |
popup-to-root |
Parent picker popup to scene root instead of its window container |
popup-open |
Inspectable picker open state |
first-day-of-week |
Calendar weekday: Sunday 0 through Saturday 6 |
calendar-view |
Calendar days, months, or years |
displayed-month, focused-date |
Calendar ISO navigation state |
Date patterns require day, month, and year. Time patterns require one hour token and minutes;
12-hour patterns also require a. Tokens: d/dd, M/MM, yy/yyyy, H/HH, h/hh,
m/mm, s/ss, S/SS/SSS, and a. Quote literal text with single quotes inside the
pattern. Two-digit years parse into 2000–2099. Standard serialized dates use yyyy-MM-dd,
times HH:mm:ss[.SSS], and local date-times yyyy-MM-ddTHH:mm:ss[.SSS].
Setting a minimum above the maximum moves the maximum to that minimum; setting a maximum below the minimum moves the minimum to that maximum. Empty bound properties remove constraints.
Seconds and milliseconds can be toggled separately with setShowSeconds() and
setShowMilliseconds() or their CSS/XML properties. Both default to hidden. Enabling
milliseconds includes seconds; hiding milliseconds leaves seconds enabled. Hiding seconds
hides both. These options change display precision without discarding stored precision, and
also update the date-time popup footer. An explicit time-format controls its own sections.
Click the trailing button, press F4, or press Alt+Down. Opening an empty picker displays today without assigning it. Date-only selection updates the value, closes the popup, and restores editor focus. In a date-time picker, selecting a date preserves the time and keeps the popup open so the footer can edit it. Selecting a date into an empty value uses midnight, then applies its constraints.
The footer uses the same segmented editing as UITimePicker, with SVG up/down buttons for
hour, minute, and any displayed seconds, milliseconds, or AM/PM sections. It follows the
field's locale, format, steps, and boundary-date time constraints. Changing these settings while
the popup is open updates its controls immediately. Setting the owner read-only closes the popup.
Committed edits update the field immediately and preserve hidden precision. getPopupTimePicker() returns the owned
footer editor after the first opening. Enter in the footer closes the popup; Escape cancels
pending digits and closes it while preserving already committed changes. Focus leaving the
field and its entire popup also closes it. The popup measures both calendar and time footer
before choosing its placement, flips above when space permits, and clamps to scene bounds.
UIPopUp::findBestPosition() supplies the shared world-pixel placement calculation used by
field popups and UIMenu::findBestMenuPos() for trigger and cursor menus. Cascading menus
retain their menu-specific rules for avoiding the previous menu.
Calendar selection and navigation are separate. Arrows move focus by day/week, Home/End move
to week boundaries, PageUp/PageDown move by month, and Ctrl+PageUp/PageDown move by year.
Enter explicitly selects focus. Click the title to choose a month, then click it again to choose
a year. Previous/Next page through those views. Today navigates to today without selecting it.
OnItemSelected reports explicit selection, including re-selection of the same date;
OnValueChange reports a changed selection. The 42 day widgets are reused across navigation.
Breeze provides light/dark palette styling. Theme tags include datetimeedit, datepicker,
timepicker, datetimepicker, datepicker::button, datetimepicker::button, calendar,
and calendar::header/previous/next/title/weekdays/weekday/grid/day/today.
The combined popup exposes datetimepicker::popup, datetimepicker::time, and
datetimepicker::time-up/time-down; arrow classes identify hour, minute, second,
millisecond, and am-pm. CSS controls the footer font, padding, minimum height, and SVG
arrow size in dp. Picker buttons use the standard arrow cursor; editable text uses the I-beam.
Calendars size to their text metrics by default, with a theme minimum of 160dp
set by the Calendar rule in bin/assets/ui/breeze.css and bin/assets/ui/uitheme.css.
Override min-width / min-height to change the preferred size while retaining automatic
text fitting.
Changing fonts automatically updates the popup size, header, weekday row, and day grid.
Explicit fixed width/height remain application overrides; choose enough space for the selected font.
Picker buttons use an inline SVG in foreground-image, with foreground-size: 12dp 12dp,
centered positioning, and theme-controlled foreground-tint. The SVG uses a white base fill
so the native image tint supplies the light/dark and hover colors. No font glyph is required.
Calendar previous/next buttons likewise use CSS-supplied left/right SVGs, centered at 12dp.
Read-only picker buttons use [allow-editing=false] selectors to mute the calendar icon and
remove its hover highlight, while leaving the input text readable and selectable.
Day classes are selected, today, outside-month, and focused; constrained cells use
normal disabled state. Today uses a border, selection a fill.
Build eepp-ui-date-time-picker and run bin/eepp-ui-date-time-picker; --us switches to a
Sunday-first, 12-hour locale. --light, --small-font, --large-font, and --narrow
exercise theme and sizing variants. F11 opens the inspector. Set EEPP_PIXEL_DENSITY=2 to check
scaling, and use the inspector to vary font size, width, and placement.
Range selection, named timezone support, and time-list popups remain separate follow-ups.
HTML input types date, time, and datetime-local now map to these native controls through
UIHTMLInput. HTML values are sanitized and serialized independently of the display locale:
yyyy-MM-dd, HH:mm[:ss[.SSS]], and yyyy-MM-ddTHH:mm[:ss[.SSS]]. Local date-time values
have no timezone. Valid date/time value attributes retain their ISO spelling; local date-time
values normalize their separator to T, omit zero seconds, and trim trailing fractional zeros.
The adapter supports min, max, step (including any and fractional seconds), required,
readonly, and disabled. Range violations remain unchanged and report invalidity; native
controls retain their default clamping policy. Time ranges can cross midnight. Step validation
uses the minimum, otherwise the value attribute, otherwise the Unix civil epoch as its base.
Date steps use days; time and local date-time steps use seconds, defaulting to 60.
Use UIHTMLInput::getValidity() / checkValidity() and UIHTMLForm::checkValidity() /
requestSubmit() for the implemented temporal constraints. Clicking a submit control validates;
direct UIHTMLForm::submit() bypasses validation. Anonymous subparts use the light user-agent
defaults in getHTMLDocumentDefaultsCSS() in uiwidgetcreator.cpp, independently of the application
theme. The HTML
adapter marks private control content with the existing UI_IGNORE_GLOBAL_CSS; stylesheet matching
keeps the document's low-priority UA rules and excludes author rules for that content. Popups remain
in the document scene and fit its visible viewport. Date/time editors measure their intrinsic width
from the displayed text or placeholder plus padding and the calendar button, so author CSS is not
required to give an input a usable size.
General HTML validity for other input types, validation messages, and
novalidate / formnovalidate are outside this temporal mapping.