96

I'm trying to pass both date strings to new Date(t).

I expect both strings represent the same time, after all, if I omit the time, shouldn't it be midnight of that day?

But while,

new Date("2016-02-16 00:00")

returns 2016-02-16, midnight, local time as expected,

new Date("2016-02-16")

returns 2016-02-16, midnight UTC, which is wrong, or at least not what I expected given what the other string parses as.

I would understand it if they would both have the same behavior, whether it is to return the time as local time, or as UTC, but it seems very inconsistent why they return different things like this.

As a workaround, whenever I encounter a date that has no corresponding timestamp I can append " 00:00" to get consistent behavior, but it seems like this is rather fragile.

I am getting this value from an INPUT element, of type 'datetime-local' so it seems especially inconsistent that I have to work around a value returned by a page element.

Am I doing something wrong, or should I be doing something differently?

5
  • 3
    2016-02-16 00:00 -- this does not look like the valid time at all. ecma-international.org/ecma-262/6.0/…, but even after you put T there it indeed behaves differently
    – zerkms
    Commented Feb 17, 2016 at 5:13
  • How exactly are you currently obtaining the Date object from the input element based on its value?
    – BoltClock
    Commented Feb 17, 2016 at 5:14
  • 4
    As per the standard - "If the HH, mm, or ss fields are absent "00" is used as the value and the value of an absent sss field is "000". If the time zone offset is absent, the date-time is interpreted as a local time." --- it should behave the same.
    – zerkms
    Commented Feb 17, 2016 at 5:15
  • @BoltClock Hmm, looks like it's taking the value field of the element and (as zerkms noticed) removing the T for some reason (I think because the value is getting displayed to the user in a context where "T" would be confusing)
    – Michael
    Commented Feb 17, 2016 at 5:16
  • 1
    @Michael: Fantastic. Browser quirks like this are my favorite. (Or could it be a DOM specification quirk? No idea, I haven't really looked.)
    – BoltClock
    Commented Feb 17, 2016 at 5:21

5 Answers 5

100

It's what the ES5.1 specification says to do:

The value of an absent time zone offset is “Z”.

It also says:

The function first attempts to parse the format of the String according to the rules called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.

Since the format requires a T separator between date and time, the valid times go to UTC:

> new Date("2016-02-16T00:00:00")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)
> new Date("2016-02-16")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)

...while in node.js, an invalid time (without the T separator) seems to go to the implementation specific localtime:

> new Date("2016-02-16 00:00:00")
Tue Feb 16 2016 00:00:00 GMT+0100 (CET)

Note that ES6 changed this, in the same part of the documentation it changes to:

If the time zone offset is absent, the date-time is interpreted as a local time.

The joy of breaking changes.

Edit

According to TC39, the specification is meant to be interpreted as date and time strings without a time zone (e.g. "2016-02-16T00:00:00") are treated as local (per ISO 8601), but date only strings (e.g. "2016-02-16") as UTC (which is inconsistent with ISO 8601).

2
  • 20
    The joy of breaking changes, indeed. In a new draft of the ES7 standard, part of the change from UTC to local time was reverted The new standard says dates should be interpreted as UTC if no timezone is specified, while date-times should be interpreted as a local time if no timezone is specified.
    – hichris123
    Commented Feb 17, 2016 at 12:32
  • 3
    They really should both be local time, regardless of date-only or date-time forms. That's how ISO8601 works. It's ridiculous that the date-only form is interpreted as midnight UTC, as a timeless UTC date doesn't even make sense conceptually. There was a heated debate about this at ECMA tc39. I fought for local time, and lost. github.com/tc39/ecma262/issues/87 Commented Feb 24, 2016 at 1:25
10

According to the specifications:

The function first attempts to parse the format of the String according to the rules called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.

And Date Time String Formats accept 2016-02-16 as a valid date

This format includes date-only forms:

YYYY
YYYY-MM
YYYY-MM-DD

[...] If the HH, mm, or ss fields are absent “00” is used as the value and the value of an absent sss field is “000”. The value of an absent time zone offset is “Z”.

Thus 2016-02-16 translates to 2016-02-16T00:00:00.000Z.

The other date 2016-02-16 00:00 does not conform to the format and therefore its parsing is implementation specific. Apparently, such dates are treated as having local time zone and your example date will return different values depending on time zone:

/* tz = +05:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-15T19:00:00.000Z
/* tz = -08:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-16T08:00:00.000Z

Summary:

  • For conforming date time formats the behavior is well defined — in the absence of time zone offset the date string is treated as UTC (ES5) or local (ES6).
  • For non-conforming date time formats the behavior is implementation specific — in the absence of time zone offset the usual behavior is to treat the date as local.
  • As a matter of fact, the implementation could choose to return NaN instead of trying to parse non-conforming dates. Just test your code in Internet Explorer 11 ;)
7

You are perhaps running into a differences between ES5, ES6 implementations and your expected result. Per Date.parse at MDN, "especially across different ECMAScript implementations where strings like "2015-10-12 12:00:00" may be parsed to as NaN, UTC or local timezone" is significant.

Additional testing in Firefox 44 and IE 11 revealed they both return a date object for new Date("2016-02-16 00:00"), which object returns NaN when atttempting to get a date component value, and whose toString value is "Invalid Date" (not "NaN"). Hence appending " 00:00 to get consistent behavior" can easily break in different browsers.

As noted in other answers new Date("2016-02-16") uses a timezone offset of zero by default, producing midnight UTC instead of local.

1
  • OP seems to get different results on the same implementation. Commented Feb 18, 2016 at 7:37
6

Per DateParser::Parse() of V8 source codes for Chrome.

ES5 ISO 8601 dates:

[('-'|'+')yy]yyyy[-MM[-DD]][THH:mm[:ss[.sss]][Z|(+|-)hh:mm]]

An unsigned number followed by ':' is a time value, and is added to the TimeComposer.

timezone defaults to Z if missing

> new Date("2016-02-16 00:00")
  Tue Feb 16 2016 00:00:00 GMT+0800 (China Standard Time)

A string that matches both formats (e.g. 1970-01-01) will be parsed as an ES5 date-time string - which means it will default to UTC time-zone. That's unavoidable if following the ES5 specification.

> new Date("2016-02-16")
Tue Feb 16 2016 08:00:00 GMT+0800 (China Standard Time)
3

returns 2016-02-16, midnight UTC, which is wrong, or at least not what I expected given what the other string parses as.

It adds the timezone offset to the 00:00

new Date("2016-02-16") outputs Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)

My timezone being IST with an offset value (in minutes) +330, so it added 330 minutes to 00:00.

As per ecma-262, section 20.3.3.2 Date.parse ( string )

If ToString results in an abrupt completion the Completion Record is immediately returned. Otherwise, parse interprets the resulting String as a date and time; it returns a Number, the UTC time value corresponding to the date and time. The String may be interpreted as a local time, a UTC time, or a time in some other time zone, depending on the contents of the String.

When you explicitly set the time-units new Date("2016-02-16 00:00") it wll use set that as hours and minutes,

Otherwise as stated here in 20.3.1.16

If the time zone offset is absent, the date-time is interpreted as a local time.

13
  • Yes but why does it do this in one case but not the other?
    – Michael
    Commented Feb 17, 2016 at 5:10
  • @Michael yes, check ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2 Commented Feb 17, 2016 at 5:20
  • So why those are different results? The standard claims it must be the same
    – zerkms
    Commented Feb 17, 2016 at 5:21
  • @zerkms Standard says what it will do when you pass the time units, it doesn't say what it will do when you won't. It clearly says The String may be interpreted as a local time, a UTC time, or a time in some other time zone, depending on the contents of the String. Where is it claiming that it must be same? Commented Feb 17, 2016 at 5:32
  • @gurvinder372 I have provided the quote in the question comments: "If the HH, mm, or ss fields are absent "00" is used as the value and the value of an absent sss field is "000". If the time zone offset is absent, the date-time is interpreted as a local time."
    – zerkms
    Commented Feb 17, 2016 at 5:34

Not the answer you're looking for? Browse other questions tagged or ask your own question.