DEV Community

Mendy Berger
Mendy Berger

Posted on • Edited on

Input and JS dates

Making time related inputs work with local timezones - JavaScript

The problem

HTML supports multiple time-related input types like: time, date, week, month, and datetime-local. All this is great, however, there can be one small problem: we need a way to set the value of the input from a JavaScript Date object, as well as a way to get the value from the input into a date object.

Unless you're on GMT you should see that the hour in the input is different than in the p element

valueAsDate and valueAsNumber

The time-related inputs have a property named valueAsDate or valueAsNumber (some have both and some only one), which is a JavaScript Date object or a epoch timestamp number, great no? Well, no, not really, let me explain, the time-related inputs are always on UTC, that is, if the user enters say 9:00 AM in the input, valueAsDate will return 9:00 AM on UTC not local time, which unless you're on UTC will result in the time being a number of hours off.

Another problem that valueAsDate has, is that since some browsers don't support (at the time of this writing) some of the time-related inputs, valueAsDate is therefore also not supported which means that this won't work at all in those browsers instead of gracefully degrading.

Solution for timezone problem

A possible solution for the timezone problem with valueAsDate and valueAsNumber is to move the time with the offset of the current timezone from UTC, so that its actually in the wrong time (because JS Dates are always on UTC, not on local time), but will work to get the input to display the correct time (as mentioned that the input is always on UTC). And then, when we get the date out we can move the time back with the timezone offset.

When using this technique we have to be careful not to change the source date but to always first make a copy of the date and work on the copy.

JavaScript to HTML

let htmlDate = new Date(jsDate); // make a copy of the source date
htmlDate.setMinutes(htmlDate.getMinutes() + htmlDate.getTimezoneOffset());

input.valueAsDate = htmlDate; // Setting by date
// Or:
input.valueAsNumber = htmlDate.getTime(); // Setting by number

HTML to JavaScript

// make a copy of the source date
let jsDate = new Date(input.valueAsDate); // Getting by date
// Or:
let jsDate = new Date(input.valueAsNumber); // Getting by number

jsDate.setMinutes(jsDate.getMinutes() - jsDate.getTimezoneOffset());

Where to use valueAsDate or valueAsNumber.

Input type valueAsDate valueAsNumber
datetime-local No 1 Yes
time Yes Yes
date Yes Yes
week Yes Yes
month Yes No 2

1 datetime-local doesn't have a valueAsDate property.

2 The valueAsNumber property of the month input is not an epoch but the numbers of months since 1970.

However this only solves the timezone problem, not the browser compatibility problem

The solution

Since valueAsDate and valueAsNumber won't work in unsupported browsers we can't really use them, rather we'll need a way that can gracefully fallback when the input type isn't supported.

To get the value from a JavaScript Date object to the HTML input we can use the Date.toLocalString method that can get us the proper format for most of these inputs. (I found that the sv-SE locale is quite close to the HTML format.)

To get the value from HTML to a JavaScript Date can be a bit more tricky, JavaScript Date objects require both date and time down to the millisecond, there is no such a thing as a date-only or time-only JavaScript object, while datetime-local has both date and time information, none of the others have all the information to create a proper date just from the input, which means that if we want to get the information from a time-related input to a JavaScript Date we will need to provide the missing information another way.

type=datetime-local

Expected value

The expected value for the datetime-local input is yyyy-mm-ddThh:mm:ss.

JavaScript to HTML

We can use sv-SE but we'll have to replace the space between the date and time with a T to make it compatible with HTML.

If you have an input without seconds remove second: "2-digit".

input.value = date.toLocaleString("sv-SE", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit"
}).replace(" ", "T");

HTML to JavaScript

Getting the value from a datetime-local input to a Date object is simple, just pass in input.value to a Date constructor, it will default to the local timezone.

new Date(input.value);

type=time

Expected value

The expected value for the time input is hh:mm:ss.

JavaScript to HTML

If you have an input without seconds remove second: "2-digit".

input.value = date.toLocaleString("sv-SE", {
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit"
});

HTML to JavaScript

To get a Date object from the time the user entered we will need a day in the following format yyyy-mm-dd to attach to the time.

Tip to get the day from a Date object you can use the same .toLocaleString options as type=date uses to get from JavaScript to HTML.

const day = "2020-01-01";
new Date(day + "T" + input.value);


type=date

Expected value

The expected value for the date input is yyyy-mm-dd.

JavaScript to HTML

input.value = date.toLocaleString("sv-SE", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit"
});

HTML to JavaScript

The JavaScript Date object constructor has a weird rule, if it gets a date without a time it defaults to UTC instead of the local timezone.

To get around this we need to enter the time manually, I use 00:00 (12:00 AM) as here but you can change it to whatever time in the day you want.

new Date(input.value + "T00:00");

type=month

Expected value

The expected value for the month input is yyyy-mm.

JavaScript to HTML

input.value = date.toLocaleString("sv-SE", {
    year: "numeric",
    month: "2-digit"
});

HTML to JavaScript

As mentioned before (type=date) we'll need to add the missing parts (day in month, and time) manually.

new Date(input.value + "-01T:00:00");

type=week

Expected value

The expected value for the week input is yyyy-Www.

This one is not easy, .toLocaleString doesn't support getting weeks and I'm not aware of any way to create a Date object from week data, so the only way is to use the valueAsDate or valueAsNumber with the technique described above, resulting in limited browser support.

So if you do have any ideas that you think might make this cross platform please let me know.

Top comments (0)