Native JavaScript and HTML5 Time Features
Recently, I collaborated with a company on a prototype that needed to display sensor data in charts, which involved some time conversions, but only in the following common formats:
- Communicating with Chunghwa Telecom’s IoT platform API, which requires data in ISO8601 UTC format.
- The format of the browser’s local time.
- The string format of the browser’s built-in date picker.
Because it doesn’t involve other time zones, I didn’t use any packages and directly used native JavaScript to handle it, and I’ll organize the concepts I learned this time.
Timestamp
To handle time, you first need to understand the concept of a timestamp. In the world of information, the method of recording time sets the starting point at January 1, 1970, 0:00:00:000.
Even if you haven’t written JavaScript, you can try this: open Chrome browser, press Fn + F12 (on Windows, just press F12), click console, then in the console that appears, type or paste Date.now(), press Enter, and you can get how many milliseconds have passed from 1970 to now (1625798004061 milliseconds). Then you can tell others you’ve written JavaScript and it’s not Hello World (just kidding).
Note that this timestamp uses UTC time (same timezone as Greenwich Observatory), so no matter which timezone you’re in, you’ll get the same timestamp. Your phone will know you’re in Taipei through GPS or network positioning based on your timezone, and then calculate the correct Taipei time by adding 8 hours to the timestamp, displaying it on your phone.
Timezone and types
People with programming experience know the importance of types. This project of mine uses three types of conversions: number, object, and string.
The Date.now() mentioned above returns a number type. If you want to convert it to a Date object type, you can use new Date(1625798004061), which will display in the browser’s timezone, giving you Fri Jul 09 2021 10:33:24 GMT+0800 (Taipei Standard Time).
new Date() with no parameters can directly get the current Date object. You can try putting different format parameters into it, but they’ll all return a Date object. For example, you can put a string in this format: new Date('2021-07-09'), or a number in this format: new Date(2021,7,9).
If you use JSX (or HTML5) <input type="date" onChange={this.onChange} />, which is the browser’s built-in date picker, the value you get or the props you give it will be a time string in the browser’s timezone, for example: '2021-07-09'. We’ll talk about this more later.
For this project I’ve been working on, Chunghwa Telecom’s API requires ISO8601 UTC format, which is a string type, like this: 2021-07-09T02:33:24Z. Note that this timezone is the same as the timestamp, Greenwich Observatory time, not the timezone where your phone is located.
Conversions
With the concepts of types and timezones above, we can practice some common conversions. Things to note:
- For the methods we’ll practice below, note that they must be called on Date objects. If you find you can’t use them, you’re probably calling them on a number or string.
.setHours().toISOString().getDate()and.setDate().toLocaleDateString();.getFullYear().getMonth()
- The return values of these methods are often not Date objects, so you need to convert them back to Date objects using
new Date()to continue using these methods.
Practice 1: ISO8601 UTC format conversion
The goal is to convert a Date object to the format required by Chunghwa Telecom so it can be used to send requests to Chunghwa Telecom.
const today = new Date();
const requestTime = new Date(today.setHours(0, 0, 0)).toISOString().slice(0, 19) + 'Z';
console.log('Format required by Chunghwa Telecom:',requestTime)
//Format required by Chunghwa Telecom: 2021-07-09T02:33:24Z
Because today is a Date object, we can use .setHours(0,0,0) to set the time Fri Jul 09 2021 10:33:24 GMT+0800 (Taipei Standard Time) to the start of the day Fri Jul 09 2021 0:0:0 GMT+0800 (Taipei Standard Time).
Here, .setHours() doesn’t return a Date object, but a number, so we need to put it back into new Date() to convert it back to a Date object before we can use the next method .toISOString().
After .toISOString(), it’s very close to the format Chunghwa Telecom wants. It’s a UTC timezone string. Use .slice() to remove the part after the decimal point and add Z, and you have the correct format: 2021-07-09T02:33:24Z.
Practice 2: Date calculations
Another important reason to use Date objects for time is that it’s convenient for calculations. For example, if I write code to calculate yesterday’s date, the situation is different if today happens to be July 1st or July 2nd, and I’d need to consider them separately, which is troublesome. But using Date objects and their methods makes it very convenient.
const firstDay = new Date('2021-07-01');
const oneDaysBeforeFirstDay = new Date(firstDay.setDate(firstDay.getDate()-1));
console.log(oneDaysBeforeFirstDay);
//Wed Jun 30 2021 08:00:00 GMT+0800 (Taipei Standard Time)
When calculating the day before July 1st, .getDate() gets the number 1 for July 1st. After we subtract 1, it becomes 0, and .setDate() will cleverly know that 0 is the last day of the previous month, so after directly subtracting 1 from July 1st, the calculated date is June 30th.
Here, remember that .setDate() also returns a timestamp number type, and needs to go through new Date() to convert to a Date object.
Practice 3: Converting Date objects to strings
.toLocaleDateString() can convert a date to a string format like 2017/7/9, but if you want to use it for an HTML5 date picker, it won’t work directly because it needs a string format like 2017-07-09. I could only think of doing this conversion manually. If anyone has other methods, please let me know.
The method is to use .getFullYear(), .getMonth(), and .getDate() separately to get the year, month, and day as numbers, then use .toString() to convert the numbers to strings. You also need to check whether the originally obtained number is one digit or two digits. If it’s one digit, you need to add a 0 string in front.
Also, for .getMonth(), you need to pay special attention to one detail. Like array index rules, if the number you get is 6, that represents July; if the number is 11, that represents December. So you need to add 1 to the number you get to convert correctly.
//Convert time object to date picker compatible format
formatDate = (dateOriginal) => {
//Get year
const year = dateOriginal.getFullYear().toString();
//Get month, JS month numbers start from 0, so need +1
//If only one digit, add 0 in front
const month =
(dateOriginal.getMonth() + 1).toString().length === 1 ?
'0' + (dateOriginal.getMonth() + 1).toString()
:
(dateOriginal.getMonth() + 1).toString();
//Get day
//If only one digit, add 0 in front
const day = dateOriginal.getDate().toString().length === 1 ?
'0' + dateOriginal.getDate().toString()
:
dateOriginal.getDate().toString()
//Combine year, month, day
const dateFormatted = year + '-' + month + '-' + day;
return dateFormatted
}
const today = new Date();
const dateForInput = formatDate(today);
console.log('Today is',dateForInput);
//Today is 2021-07-09
Date picker
After converting the format using the custom function example above, we can use it to set the date picker supported by HTML5. For example, if today is July 16th, limit the dates this picker can select from June 28th to yesterday (max and min props). The code for the date picker is as follows:
<input
className="mv2 ba"
type="date"
placeholder="yyyy-mm-dd"
value={this.state.selectedDate}
onChange={this.onChange}
max={this.formatDate(yesterday)}
min={'2021-06-28'}
/>
However, this date picker’s UI is provided by the browser. Although iOS Safari provides a UI, desktop Safari doesn’t, so on Mac, what appears will be an interface where you need to type with the keyboard. If you’re not like me, just temporarily making a prototype, you can find some existing packages to use, which will make it easier to control the interface across different browser versions.
Conversions for other timezones
If your needs involve other timezone conversions, you can refer to this article: Using Native JavaScript to Calculate Times in Various Timezones. There are also some powerful and useful packages available, such as the famous Moment.js. If you’re concerned that Moment.js is no longer maintained, you can use the newer luxon.
Thoughts
HTML5 and native JavaScript time handling looks a bit tedious, but conceptually it’s not too complex. It might be quite suitable for JavaScript beginners to practice, and you can also learn that the programming process often requires conversions between different types and required formats. If anyone has different approaches, feel free to share.
Update
Regarding the formatDate() mentioned earlier, someone in Front-End Developers Taiwan provided a good alternative. You can go check it out. My personal favorite approach is as follows:
const formatDate = (dateOriginal) => `${dateOriginal.toLocaleString('en', {year: 'numeric'})}-${dateOriginal.toLocaleString('en', {month: '2-digit'})}-${dateOriginal.toLocaleString('en', {day: '2-digit'})}`