What is the difference between new Date("2017-01-01") and new Date("2017-1-1")? [duplicate]

I input new Date("2017-01-01") in chrome console, the output shows its hour is 8, but new Date("2017-01-1") and new Date("2017-1-01") shows their hour are both 0, so how does new Date(dateString) parse?

new Date("2017-01-01")
// Sun Jan 01 2017 08:00:00 GMT+0800 (中国标准时间)*
new Date("2017-01-1")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
new Date("2017-1-1")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
new Date("2017-1-01")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*

Solution 1:

"2017-01-01" follows the ISO standard ES5 Date Time String format (simplification of ISO 8601 Extended Format), and therefore it is in UTC time, which is 8am in China. All other strings are parsed as local time in Chrome1.


1 Relevant source code in Chromium: https://cs.chromium.org/chromium/src/v8/src/dateparser-inl.h?type=cs&l=16

Date parsing in Chromium follows the standard ES5 rules as well as these extra rules:

  • Any unrecognized word before the first number is ignored.
  • Parenthesized text is ignored.
  • An unsigned number followed by : is a time value, and is added to the TimeComposer. A number followed by :: adds a second zero as well. A number followed by . is also a time and must be followed by milliseconds. Any other number is a date component and is added to DayComposer.
  • A month name (or really: any word having the same first three letters as a month name) is recorded as a named month in the Day composer.
  • A word recognizable as a time-zone is recorded as such, as is (+|-)(hhmm|hh:).
  • Legacy dates don't allow extra signs (+ or -) or unmatched ) after a number has been read (before the first number, any garbage is allowed).
  • Any strings that satisfy the ES5 rules and the rules above will be parsed using ES5 rules. This means "1970-01-01" will be in UTC time-zone not in local time-zone.

What does that mean?

First note that "2017-01-01" is parsed in UTC time because it is a "date" string instead of a "date-time" string, and it matches the ES5 definition of a "date" string. If time is attached, then it will follow the ISO standard and parse it in local time.

Examples:

  • 2017-01-01 - Jan 1, 2017 in UTC time
  • 2017-01-01T00:00 - Jan 1, 2017 in local time
  • 2017-1-1 - Jan 1, 2017 in local time
  • 2017-(hello)01-01 - Jan 1, 2017 in local time
  • may 2017-01-01 - May 1, 2017 in local time
  • mayoooo 2017-01-01 - May 1, 2017 in local time
  • "jan2017feb-mar01apr-may01jun" - Jun 1, 2017 in local time

Solution 2:

difference between new Date("2017-01-01") and new Date("2017-1-1")

new Date("2017-01-01") is in-spec (more below). new Date("2017-1-1") is not, and so falls back on any "...implementation-specific heuristics or implementation-specific date formats" the JavaScript engine wants to apply. E.g., you have no guarantee how (or whether) it will successfully parse and if so, whether it will be parsed as UTC or local time.

Although new Date("2017-01-01") is in-spec, sadly what browsers are supposed to do with it has been a moving target, because it doesn't have a timezone indicator on it:

  • In ES5 (December 2009), strings without a timezone indicator were supposed to be parsed as UTC. But that's at variance with the ISO-8601 standard the date/time format is based on, which says strings without a timezone indicator are meant to be local time, not UTC. So in ES5, new Date("2017-01-01") is parsed in UTC.
  • In ES2015 (aka ES6, June 2015), strings without a timezone indicator were supposed to be local time, not UTC, like ISO-8601. So in ES2015, new Date("2017-01-01") is parsed as local time.
  • But, that was changed again in ES2016 (June 2016) because browsers had been parsing date-only forms with - in them as UTC for years. So as of ES2016, date-only forms (like "2017-01-01") are parsed in UTC, but date/time forms (like "2017-01-01T00:00:00") are parsed in local time.

Sadly, not all JavaScript engines currently implement the spec. Chrome (as of this writing, v56) parses date/time forms in UTC even though they should be local time (so does IE9). But Chrome, Firefox, and IE11 (I don't have IE10 or Edge handy) all handle date-only forms correctly (as UTC). IE8 doesn't implement the ISO-8601 form at all (having been released before the ES5 spec was released).

Solution 3:

When parsing dates, JavaScript interprets ISO dates as UTC time and other formats as local time.

As MDN article suggests,

Where the string is ISO 8601 date only, the UTC time zone is used to interpret arguments.

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 (ES5 and ECMAScript 2015). Therefore Date objects produced using those strings may represent different moments in time depending on the version of ECMAScript supported 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.

// 2017-03-28 is interpreted as UTC time,
// shown as 2017-03-28 00:00:00 in UTC timezone,
// shown as 2017-03-28 06:00:00 in my timezone:
console.log("ISO dates:");
var isoDates = [new Date("2017-03-28")];
for (var dt of isoDates)
{
  console.log(dt.toUTCString() + " / " + dt.toLocaleString());
}

// Other formats are interpreted as local time, 
// shown as 2017-03-27 18:00:00 in my timezone,
// shown 2017-03-28 00:00:00 in my timezone:
console.log("Other formats:");
var otherDates = [new Date("2017-3-28"), new Date("March 28, 2017"), new Date("2017/03/28")];
for (var dt of otherDates)
{
  console.log(dt.toUTCString() + " / " + dt.toLocaleString());
}

Solution 4:

This format is International Standard (ISO format)

new Date("2017-01-01")

This guarantees same output across all browsers.

However, the other formats may change according to browser as they are not so well defined.

As you can see that this format

new Date("2017-1-1")

is parsed successfully in chrome, but gives error in IE 11