Date pickers and the Temporal API
Here’s what the Temporal docs have to say:
“Because
Temporal.PlainDateTime
does not represent an exact point in time, most date/time use cases are better handled using exact time types likeTemporal.ZonedDateTime
andTemporal.Instant
. But there are cases whereTemporal.PlainDateTime
is the correct type to use:
- Passing data to/from a component that is unaware of time zones, e.g. a UI date/time picker.
All HTML date inputs are unaware of time zones. So we need to use Plain types when working with the HTML date, time, datetime-local and month inputs. What’s the best way to do that?
There are multiple ways to a get a value from the different date/time HTML inputs: .value
, .valueAsNumber
and .valueAsDate
.
Two inadvisable ways to get the value from a HTML date input
valueAsNumber
and valueAsDate
should both be avoided.
.valueAsNumber
While .valueAsNumber
is usually used in conjunction with <input type="number">
, it also works for date, week, month and time input
elements.
For a date input, .valueAsNumber
returns the number of milliseconds elapsed from midnight UTC on the morning of 1970-01-01 to midnight UTC on the morning of the parsed date.
For a month input, .valueAsNumber
returns the number of months between January 1970 and the chosen month.
For a time input, .valueAsNumber
returns the number of milliseconds since the start of the day. So 12:00 noon will return 43200000 because there are 86400000 milliseconds in a day.
The datetime-local input does not support .valueAsNumber
.
.valueAsDate
For a date input, .valueAsDate
returns a new Date object representing midnight UTC on the morning of the parsed date.
For a month input, .valueAsDate
returns a new Date object representing midnight UTC on the morning of the first day of the parsed month.
For a time input, .valueAsDate
returns a new Date object representing the parsed time in UTC on 1970-01-01. It should go without saying that this is not a sensible way to represent a time, and makes it clear why Temporal types like PlainTime
are useful.
The datetime-local input does not support .valueAsDate
.
The ISO 8601 format
ISO 8601 is a standard for formatting dates, times and durations. It specifies a YYYY-MM-DD format for dates.
You can get and set the .value
of the HTML date input, time input, datetime-local input and month input as an ISO 8601 string.
<input type="date" value="2025-11-27" />
<input type="month" value="2025-11" />
<input type="time" value="13:30" />
<input type="datetime-local" value="2025-11-27T13:30" />
The from()
method of Temporal.PlainDateTime
, Temporal.PlainDate
, Temporal.PlainTime
and Temporal.PlainYearMonth
all accept an ISO 8601 string as an argument.
Date input
Create a PlainDate
object when working with a <input type="date">
const dateInput = document.querySelector('input[type="date"]');
const date = Temporal.PlainDate.from(dateInput.value);
Time input
Create a PlainTime
object working with <input type="time">
const timeInput = document.querySelector('input[type="time"]');
const time = Temporal.PlainTime.from(timeInput.value);
datetime-local
Create a PlainDateTime
object when working with <input type="datetime-local">
const datetimeInput = document.querySelector('input[type="datetime-local"]');
const dateTime = Temporal.PlainDateTime.from(datetimeInput.value);
You can also specify a timezone to create a ZonedDateTime
. You can specify the timezone as a string like 'America/New_York'
, or get the users system timezone by using Temporal.Now.timeZoneId()
.
const timeZone = Temporal.Now.timeZoneId();
const zonedDateTime = Temporal.PlainDateTime.from(datetimeInput.value).toZonedDateTime(timeZone);
You could optionally let the user explicity choose a timezone with a dropdown. Intl.supportedValuesOf('timeZone')
returns all the supported timezones.
PlainDateTime
from seperate date and time inputs
Should you choose to use seperate inputs for date and time rather than using <input type="datetime-local">
, the Temporal API makes it easy to combine a date and time value into a plainDateTime
object.
const dateInput = document.querySelector('input[type="date"]');
const timeInput = document.querySelector('input[type="time"]');
const date = Temporal.PlainDate.from(dateInput.value);
const dateTime = date.toPlainDateTime(timeInput.value);
Month input
Create a PlainYearMonth
object when working with <input type="month">
const monthInput = document.querySelector('input[type="month"]');
const yearMonth = Temporal.PlainYearMonth.from(monthInput.value);
<input type="month">
is not supported by all browsers.
Week input
Some browsers support a week input. <input type="week">
does not map to any Temporal type.
Third-party date pickers
Because ISO 8601 is a standard, it is used by many open-source third-party custom date pickers. If a custom date or time picker returns an ISO 8601 string, you will be able to construct a Temporal type from it in the same fashion as when working with standard HTML date/time inputs. The Cally web component, to take one example, returns its value as an ISO 8601 formatted string.