Date & time¶
Craft renders date and time pickers as plain text inputs with a trailing glyph — the calendar
and clock are cues, not native <input type="date"> controls. On touch devices Craft swaps in
native date/time inputs instead, so the desktop markup below isn't what mobile users get.
Leave blank to never expire.
{{ forms.dateField({ label: "Post date", name: "postDate", value: entry.postDate }) }}
{{ forms.timeField({ label: "Doors open", name: "doorsAt", value: value }) }}
{{ forms.dateTimeField({
label: "Expiry",
instructions: "Leave blank to never expire.",
name: "expiryDate",
value: entry.expiryDate,
}) }}
Pass a DateTime (or anything Twig's date() accepts) as value; pass null for an empty
field. dateTimeField sets fieldset: true, since it groups two labelled controls.
What gets submitted¶
A date field doesn't post a single string. It posts an array — the visible value plus the locale and timezone needed to interpret it:
postDate[date] => "7/16/2026" // formatted in the user's locale, not ISO
postDate[locale] => "en-US"
postDate[timezone] => "America/Los_Angeles"
dateTimeField adds postDate[time], and emits the locale and timezone once rather than per
control. Craft's DateTimeHelper normalises this shape for you, so prefer
over reading [date] directly — parsing the localised string yourself will break for any user
whose formatting locale isn't yours. Set outputLocaleParam: false or outputTzParam: false
to suppress the hidden inputs when you're handling conversion another way.
Note
Mobile is different markup. Craft checks craft.app.request.isMobileBrowser and, on a
match, renders a native <input type="date">/type="time" with an .empty-value class when
unset — no datepicker JS, no calendar glyph. Don't target .datewrapper input[type=text] in
plugin CSS or your styles will silently drop on touch devices.