Tuesday, 29 July 2014

JavaScript's Date/Time Format Is Not ISO-8601

TL;DR JavaScript's date/time string format isn't ISO-8601. It's much simpler, covers fewer use cases, and assumes strings written without a timezone indicator are in UTC (whereas ISO-8601 assumes they're "local" time).

Update October 2014: The draft ES6 specification has changed the timezone thing so ES6 will match ISO-8601 and assume local time, and some implementations (such as V8 in Chrome) have already updated their handling to implement that change.


There's a rumor going around that the date/time format in ECMAScript 5th edition (ES5, the current version of "JavaScript") is ISO-8601. It isn't. It's a bit like it, but it isn't, and despite the spec calling it a "simplification" of ISO-8601, it's not just a subset, either; the same string can mean two different times (or even years) when interpreted in ISO-8601's format vs. JavaScript's format.


ISO-8601 is incredibly complicated; JavaScript's format is much simpler

ISO-8601 covers a very wide range of use cases, not just date/times, but spans of time, various ways of writing time, and so forth. JavaScript's format is much simpler:

  • 201405 is a valid ISO-8601 string: It refers to the entire month of May 2014. It's an invalid date/time string in JavaScript.
  • 2014-05 also refers to the entire month of May in ISO-8601; in JavaScript, it refers to May 1st at midnight UTC (omitted fields have the implied value of 0).
  • ISO-8601 has week numbers, and Here There Be Dragons: 2009-W01-1 is Monday 29 December 2008, for instance. Fortunately, JavaScript doesn't do week numbers.
  • ISO-8601 allows fractions (of any length) on the smallest time unit in the string, so 2014-07-29T02.5Z is 2:30 in the morning UTC (so is 2014-07-29T02,5Z). JavaScript doesn't allow fractions, but uses the . (only, not the ,) to separate seconds from an optional milliseconds value (which is very like allowing up to three digits of a fraction on seconds).
  • And so on. ISO-8601 covers various other kinds of time spans and all kinds of things.

So yeah, not the same thing. Why is JavaScript's so different? Probably a combination of the fact that A) ISO-8601 is difficult to parse, and B) JavaScript's Date object has no way of representing a span of time (the entire month of May, for instance).

Time Zone Differences

Update (October 2014): See above, ES6 fixes this. I guess they decided it was a bug after all.

In New York, in ISO-8601, 2014-07-29T02:37:21 means 2:37 a.m. on the 29th; but in JavaScript, it means 10:37 p.m. on the 28th. That's because the string doesn't have any timezone indicator on it. ISO-8601 says that no timezone indicator means "local time," and so the string becomes context-sensitive. JavaScript says no timezone indicator implies Z (UTC). This is a pretty big difference. 2014-01-01T01:28:55 isn't even in the same year in the two systems in New York (or about half the rest of the planet).

Why would TC-39 (the committee that steers ECMAScript) do something so bone-headed? It probably wasn't bone-headed. Remember that this didn't come in until ECMAScript 5th edition, in 2009. Most likely, engines had been parsing strings like YYYY-MM-DD in UTC for years. TC-39 couldn't just waive a wand over it and say "Thou shalt now use local time." Too much existing code would break.

My only quarrel is with the wording in the specification, which says that the format is a "simplification" of ISO-8601. "Simplification" (to me) implies "subset," as in, a string that's valid in the subset will mean the same thing in the superset. But that's not true here. They probably should have just said "It looks a bit like ISO-8601, but it isn't" and highlighted the more important differences.

Happy coding!

No comments: