JavaScript Date: a Bad Part

 Date: March 10, 2015

We all know that JavaScript has some bad parts. However, one of them is usually forgotten, and skipped in most of "JavaScript Bad Parts" lists on the Internet. It is a Date object. You can have hard time working with it, especially if you want to support time zones. In this post I would like to outline quircks of Date in JavaScript.

Basics

In order to create a Date object, you just do:

var now = new Date();

This will create a date set to now.
If you want to create Date on specific day:

var year = 2015;
var month = 2-1; // February
var day = 27;
var now = new Date(year, month, day);

Here you encounter the first quirk: months are numbered from 0 to 11. January is 0, December is 11.

You can also specify hours, minutes, seconds, and milliseconds as additional parameters (no more pitfalls here):

var year = 2015;
var month = 2-1; // February
var day = 27;
var hours = 14; // 2PM
var minutes = 56;
var seconds = 37;
var milliseconds = 997;
var now = new Date(year, month, day, hours, minutes, seconds, milliseconds);

For more details about creating Date, check Date at MDN.

Formatting

If you want to display a date, there are a few available converters. Here are the most commonly used:

var date = new Date(2015, 1, 27, 14, 56, 37, 997);
console.log(date.toDateString());       // Fri Feb 27 2015
console.log(date.toTimeString());       // 14:56:37 GMT-0800 (Pacific Standard Time)
console.log(date.toLocaleDateString()); // 2/27/2015
console.log(date.toLocaleTimeString()); // 2:56:37 PM
console.log(date.toUTCString());        // Fri, 27 Feb 2015 22:56:37 GMT
console.log(date.toGMTString());        // Fri, 27 Feb 2015 22:56:37 GMT
console.log(date.toISOString());        // 2015-02-27T22:56:37.997Z
console.log(date.toJSON());             // 2015-02-27T22:56:37.997Z

Be careful with 3 and 4. Both return different results, depended on user's machine settings and geographical location. This may cause UI issues (some date formats might be long).

Converters 5 and 6 are equivalent in all, most popular browsers. However toGMTString is deprecated.

Converters 7 and 8 are also equivalent. Moreover, toJSON use toISOString underneath to return result.

There is no significant issues with displaying Date, but there is no way to format it with custom format (e.g., 'YYYY-MM-DD HH:mm') like in C# or Java.

Alternative for formatting is Intl.DateTimeFormat object from ECMAScript Internationalization API. However, this API is not yet supported by older browsers, most of mobile browsers, and Safari (check browser compatibility on MDN).

Parsing

More tricky than displaying dates, is parsing them. There are two common ways to parse date from string:

var parsedDate1 = new Date("2015-02-27 22:56:37");
var parsedDate2 = Date.parse("2015-02-27 22:56:37");

First returns Date object, second - number 1425106597000 (milliseconds since January 1, 1970, 00:00:00 UTC).

There is one, very unpleasant detail in parsing:

Given a date string of "March 7, 2014", parse() assumes a local time zone, but given an ISO format such as "2014-03-07" it will assume a time zone of UTC. Therefore Date objects produced using those strings will represent different moments in time unless the system is set with a local time zone of UTC. This means that two date strings that appear equivalent may result in two different values depending on the format of the string that is being converted (this behavior is changed in ECMAScript ed 6 so that both will be treated as local).

This means that date1 and date2 below (created from machine located in Seattle, WA) are different:

var date1 = new Date("2014-01-02");
var date2 = new Date("1/2/2014");
console.log(date1); // Wed Jan 01 2014 16:00:00 GMT-0800 (Pacific Standard Time)
console.log(date2); // Thu Jan 02 2014 00:00:00 GMT-0800 (Pacific Standard Time)

Recommended format for parsing is: YYYY/MM/DD - user local settings independent. It always assumes local timezone offset for date string given as a parameter.

var date1 = new Date("2014/01/02");
console.log(date1); // Thu Jan 02 2014 00:00:00 GMT-0800 (Pacific Standard Time)

Timezones

JavaScript Date object always assumes local timezone offset for the machine on which it is displayed. What is important: JavaScript Date does not support time zones, but only timezone offsets. This is a definition of both from Wikipedia:

A time zone is a geographical region where just about everybody observes the same standard time.
A time offset is an amount of time subtracted from or added to UTC to get the current civil time - whether it's standard time or Daylight saving time.

In other words: time zone is constant (as long as government does not changed it) for some place (e.g., San Francisco) all the time, while time zone offset changes (usually, twice a year - this is what we call 'daylight savings').

To get timezone offset:

var myDate = new Date();
var timezoneOffset = myDate.getTimezoneOffset(); // result in minutes

The result is in minutes. Here is another quirk: timezones that are 'west' from UTC (offset = 0) have positive values (60 - 720), and timezones that are 'east' from UTC have negative values (-60 - -780). Thus, Pacific Standard Time (UTC -8:00), in JavaScript, has timezone offset 480, and Central European Time (UTC +1:00) has timezone offset -60.

One more pitfall: local timezone offset is always set depends on value of Date object. Thus, for Computer located in Seattle, WA:

var date1 = new Date(2015,1,20); // date in Pacific Standard Time (before timezone offset change)
var date2 = new Date(2015,2,20); // date in Pacific Daylight Time (after timezone offset change)
console.log(date1.getTimezoneOffset()); // 480
console.log(date2.getTimezoneOffset()); // 420

In this case your current 'local' timezone offset (for the time in which you are creating Date object) does not matter.

Even if you create Date object with date before the time change, and then you modify it to date after the time change - its timezone offset changes to reflect offset for the new (current) value:

var date = new Date(2015,1,20); // date in Pacific Standard Time
console.log(date.getTimezoneOffset()); // 480
date.setMonth(2); // change month so date is in Pacific Daylight Time now
console.log(date.getTimezoneOffset()); // 420

Moment.js

Moment.js is a JavaScript library designed to work on the client side (in browser), and on the server side (in node.js). It has a lot of functions to parse, display and manipulate dates. This makes dealing with dates less painful.

Moment Timezone - a library built on top of Moment.js - supports real time zones (not only timezone offsets). The data for Moment Timezone comes from the IANA Time Zone Database - most popular time zone database. New versions are released periodically as timezone laws change in various countries.

Consider using Moment.js and Moment Timezone in your application when you are doing more complex operations with dates. Especially when you want to support time zones.

Summary

It is really surprising that working with dates in programming is still causing a lot of issues. However, we are getting better at that. To get a comprehensive overview of date/time in programming language I recommend Date and Time Fundamentals by Matt Johnson. You can also check Matt Johnson's blog. He blogs mainly about date/time in variety of environments. His article JavaScript Date type is horribly broken might be good supplement for this post.

To get a quick insight why dealing with time zones is very complex, check this video. It is also worth to check What Every Programmer Should know about Time.

 Tags:  programming

Previous
⏪ Hidden feature of the Azure Portal: keyboard shortcuts

Next
Guest Speakers in Computer Science ⏩