8

Why does the first input work correctly, but the second input gives me a result for 5 hours ago?

new Date("2000-1-1")
Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new Date("2000-01-01")
Fri Dec 31 1999 19:00:00 GMT-0500 (EST)

how can i get the second one to cooperate with me?

var a = new Date("2000-1-1"); // Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
var b = new Date("2000-01-01"); // Fri Dec 31 1999 19:00:00 GMT-0500 (EST)
console.log(a, a.getTime());
console.log(b, b.getTime());
mplungjan
  • 169,008
  • 28
  • 173
  • 236
Logan Murphy
  • 6,120
  • 3
  • 24
  • 42
  • 3
    Appears to occur in Chrome, IE, and Edge. Firefox parses correctly... perhaps using [momentjs](https://momentjs.com/) may be useful? javascript's `Date` tends to be really inconsistent – Patrick Barr Apr 20 '17 at 14:34
  • that's interesting it has nothing to do with that particular year either, it's the same behavior for other years as well – Chris Hawkes Apr 20 '17 at 14:36
  • 1
    The first example is a non-standard date format: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse and the second is correct as it is taking into account the local time offset. – evolutionxbox Apr 20 '17 at 14:37
  • 1
    @PatrickBarr - the JS `Date` object actually isn't really that inconsistent, it just only officially supports a certain set of parameter formats. Many browsers have added support for non-standard formats, to avoid errors when possible, but since they are not official, their implementations can vary across browsers. – talemyn Apr 20 '17 at 14:39
  • 1
    If you split on "-" and use `new Date(part[0],part[1]-1,part[2],12,0,0,0)` you should be safer – mplungjan Apr 20 '17 at 14:40
  • @PatrickBarr additionally safari gives invalid date and 5 hour ago date for the two inputs – Logan Murphy Apr 20 '17 at 14:40
  • also running the stackoverflow code snippet i actually get 5 hours later on the first and correct on the second :/ – Logan Murphy Apr 20 '17 at 14:42

2 Answers2

2

The reason why you are seeing this is actually described on the Date.parse() page of MDN (where a lot of the details around officially-supported Date parameter formats are described). Specifically:

Differences in assumed time zone

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.

So what you're seeing is a combination of two things:

  1. The constructor is reading your properly-formatted ISO date value as UTC, since no time zone is provided, and
  2. It is accurately identifying that your local computer timezone is U.S. Eastern, and adjusting the value appropriately.

As a result, you are seeing the U.S. Eastern Time zone version of midnight on January 1st, 2000, UTC time (or, 7 PM on December 31st, 1999).

Since your first example is using a non-standard format (from JS's point-of-view), the first assumption is not coming into play and the value is being created assuming the local timezone for the value (a browser-specific decision on how to handle a non-standard format).

Community
  • 1
  • 1
talemyn
  • 7,822
  • 4
  • 31
  • 52
0

The golden rule is never parse strings with the Date constructor or Date.parse as they are largely implementation dependent. There are many quirks.

According to ECMA-262, the string "2000-01-01" should be parsed as a valid ISO 8601 date-only form, but for timezone UTC+0000. Old browsers like IE 8 may not parse it at all, and some may treat it as local.

The string "2000-1-1" is not precisely ISO 8601 format so browsers may fall back to any heuristics they like, including:

  1. returning an invalid date as it's not a valid ISO 8601 date format
  2. parsing as a local date for 1 Jan 2000
  3. treating as ISO 8601 and parsing as UTC+0000

At least 1 and 2 occur in current browsers.

So your best bet is to use a library with a parser and provide the format of the string, or write a simple function if you only have one format to deal with, e.g.

/* Parse ISO 8601 date only form as local
** @param {string} s - string in format YYYY-M[M]-D[D]
** @returns {Date} If date parts out of range, returns invalid date
*/
function parseISOLocal(s) {
  var b = s.split(/\D/);
  var d = new Date(b[0], --b[1], b[2]);
  return isNaN(d) || d.getMonth() != b[1]? new Date(NaN) : d;
}

['2000-1-1','2000-01-01','2000-2-29','1900-2-29'].forEach(function(s) {
  console.log(s + ' : ' + parseISOLocal(s).toString());
});
Community
  • 1
  • 1
RobG
  • 142,382
  • 31
  • 172
  • 209