Handling datetime entries with native HTML inputs

Recently I needed to handle datetime objects in a small web application. In the past I've reached out for some JavaScript polyfill library to have a visual input widget for the user. But trying to keep the dependencies small this time, I've checked what new input types HTML 5 has to offer and how well they are supported out of the box.

While the current support is surprisingly good, you may still want to fall back to a JavaScript alternative, especially when support on iOS mobile is required. Following is a summary of the experience (in 2024) for different HTML input types related to date and time.

First candidate: "datetime-local"

The most obvious first choice would be <input type="datetime-local"> which promises to handle date and time in a single input field. Both MDN and the actual W3C specification give a very detailed description of the input field, including its supported attributes. For the sake of this article, I'm not gonna repeat all of it here.

To get a first impression how the input field looks and behave, here is an example with default values:

On all major desktop browsers tested (Safari, Firefox, Chrome on MacOS) the UX of the keyboard input is actually quite good, i.e. using <tab> will jump through the input mask as you would expect, and the order of year, month and day corresponds to my local setting. All three browser provide a "calender" popup, which varies in quality. Firefox and Safari only allow to set the date, while Chrome provides input for time as well. Chrome and Firefox provide a "hierarchical" date selection, i.e. the user can step out of the current month to select the year and month directly, which can be very useful when selecting dates which are years in the past (Safari requires the user to step back one month at a time).

Interestingly on mobile, the keyboard input is skipped completely and focusing the input field will directly open a native widget for date and time.

So far so good. The "datetime-local" input field seems to handle the use case quite well. However, by default the field only allows the user to input or edit time values for hours and minutes (not seconds). Consulting MDN again, the reason for this behaviour is the default value of its step attribute. Quoting MDN

[...] The step attribute is a number that specifies the granularity that the value must adhere to, or the special value any, which is described below. Only values which are equal to the basis for stepping (min if specified, value otherwise, and an appropriate default value if neither of those is provided) are valid. [...]

[...] For datetime-local inputs, the value of step is given in seconds, with a scaling factor of 1000 (since the underlying numeric value is in milliseconds). The default value of step is 60, indicating 60 seconds (or 1 minute, or 60,000 milliseconds). [...]

- MDN

As mentioned, the default value for step is "60", i.e. full minute increments. I assume, this is the reason why all browsers prevent the entry or modification of seconds by default. On a side note, this behaviour seems to be fully based on an interpretation by browser vendors, since the HTML 5 specification mentions the step attribute only in the context of validation and does not link it to the presentation and UX behaviour of the input field.

Setting step="1" should give us the ability to handle seconds as well. How well is this working in practice?

All desktop browser tested support the step attribute, and setting step to anything other than a multiply of 60 will allow the user to enter arbitrary values for seconds independent of the chosen step value. If a step mismatch occurs, the input field is marked as invalid (including the :invalid pseudo CSS class) and form submission is prevented. There is no dedicated UI widget which limits the entry to only valid step values (e.g. selecting only hours in case step="3600").

However it looks like the support for mobile browsers, is servery lacking. I.e. even after setting step="1", mobile browser will provide no way to input time values beyond hours and minutes. I've tested this with Chrome, Firefox and Edge on Android, as well as Safari and Chrome on iOS. All of those browsers won't let the user enter numbers directly but bring up the native datetime entry field, limited to hours and minutes. What makes this even worse (in my opinion) is the fact that the step attribute is still honoured during form validation. On top of that, modifying a pre-populated input field will zero out the seconds, with no way to reenter those values again.

So in summary: using <input type="datetime-local"> works fine as long as

More examples for different step values (including validation and serialization behaviour)
Default:

Serialization:
step="1":

Serialization:
step="3600":

Serialization:
step="25":

Serialization:
step="15" value="2024-02-02T14:27:20" (step mismatch):

Serialization:

Second option: type="date" and type="time"

In addition to datetime-local, the HTML spec provides the date and time input types (in addition to month and week). Let's see if those inputs have better support on mobile browsers. If so, both could be combined to handle datetimes. The downside is obviously a slightly different UX, since two different input fields need to be set (with different popups) rather than one. Although depending on the scenario, this could also be seen as beneficial.

Similar to "datetime-local", both input types support the step attribute. In case of of "time", it reflects number of seconds, for "date" it's the number of days. Setting the value to "1" gives us

Surprisingly, using <input type="time" step="1"> on Android with Edge, Chrome and Firefox gives us a popup which supports the entry of seconds! It's interesting to see that the same input dialog was not used for "datetime-local". I haven't done any mobile development, so I don't know if this is a fundamental limitations of the underlying OS dialog. However, when used under iOS (Safari and Chrome) the popup is still limited to hours and minutes.

In summary: Splitting the entry of a datetime into its date and time parts and using the corresponding input types, works great except for the limitation that iOS still does not support a time resolution of seconds.

Other things to keep in mind

There are two other things I've stumbled upon when handling the form values of datetime-local and time on the backend.

  1. The serialization format of both fields can depend on their current value. While the base format is YYYY-mm-ddTHH:MM:SS (or HH:MM:SS for "time"), the specification states that the seconds can be dropped in case seconds are zero. For all browser I've tested this is true, so 24th of Dec. 2024, 10:24:00 will serialize to 2024-12-24T10:24. Hence, the backend validation and parsing logic will need to be able to handle the different formats.
  2. By design none of the entry field will provide timezone information, not even the current offset of the local environment. MDN suggests to either have a dropdown field with all possible offsets (which is just a horrible UX, given that the correct one can not be preselected) or - if the current user is logged in and has a default timezone set in the account - use a hidden input field with the offset.

Not the end of the world, but definitely a small annoyance which broke my form handling at the beginning.

Why it matters

I'm personally happy to see the support of date and time values in HTML forms improving. It provides a better unified UX experience throughout the web, is great for quick prototyping as well as internal tools and can reduce the download and execution overhead of web applications. Obviously, third party solution still have their place, especially if data entry is one of the key parts of the application.

There is however an argument to be made about the ever growing complexity of the web standard, which makes implementing new browser engines more and more difficult. But I guess this is a discussion for another time.