Input and JS dates
Tags: html, javascript, date
Published: 2020-09-15 01:00:26

---
title: Input and JS dates
published: true
description: Converting JavaScript Date objects to HTML date-related inputs and vise versa 
tags: #HTML #JavaScript #date
//cover_image: https://direct_url_to_image.jpg
---
# 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.

<i>Unless you're on GMT you should see that the hour in the input is different than in the p element</i>
{% codepen https://codepen.io/MendyBerger/pen/qBNdzzR default-tab=js,result %}

## 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](https://caniuse.com/#search=time%20input) (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

```javascript
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

```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 <sup>1</sup> | Yes             |
| `time`           | Yes             | Yes             |
| `date`           | Yes             | Yes             |
| `week`           | Yes             | Yes             |
| `month`          | Yes             | No <sup>2</sup> |

<sup>1</sup> `datetime-local` doesn't have a `valueAsDate` property.

<sup>2</sup> 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](https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats).)

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"`.

```javascript
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.

```javascript
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"`.

```javascript
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.

```javascript
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

```javascript
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.

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



---
## type=month

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

### JavaScript to HTML

```javascript
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.

```javascript
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.