Date changes results if called with an array of parameters - javascript

I tried the following script with both Node.js and my Chrome console:
console.log(new Date([1988,11,5]))
console.log(new Date(1988,11,5))
I was expecting the two methods to give the same results, but they behave differently:
Sat Nov 05 1988 00:00:00 GMT+0100 (W. Europe Standard Time)
Mon Dec 05 1988 00:00:00 GMT+0100 (W. Europe Standard Time)
In particular, the second field, which specifies the month, starts the counting from zero in the first case.
I would expect the month starting from zero in both cases, or have I misunderstood something?

The first line is not a valid Date constructor; it doesn't accept an array element as parameter. I think it's assuming it as a string value and is intepreted as yyyy,mm (1 based),dd.
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
These are equivalently a wrong init:
console.log(new Date([1988,11,5]))
console.log(new Date([1988,11,5].toString()))
console.log(new Date("1988,11,5"))
Demo: https://jsfiddle.net/IrvinDominin/Lweww6et/

The second call, new Date(1988, 11, 5), matches the specific format required by the specification for the Date constructor:
new Date (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )
The first call will be passed through to the other form of the constructor, as you're passing in a single array:
new Date (value)
Looking at the steps here, it will be calling an internal implementation detail, ToPrimitive, on the array. Following the specification through the definitions of ToPrimitive and [[Default Value]], we see that the value argument passed to new Date in your first instance is a string representation of the array, ie 1988,11,5.
Going back to the spec for new Date (value), we see that if the type is a String, which it now is, it will be handed off to the same code that is used for Date.Parse, which 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. Unrecognisable Strings or dates containing illegal element values in the format String shall cause Date.parse to return NaN.
(emphasis mine).
So, the implementors of V8, Chrome's and Node.js' JavaScript engine, have chosen to recognise and parse that string using a 1-based month. But because it's not in the spec, it might change, and it might be different in different implementations/browsers, so don't rely on it.

Related

What's the correct format of the default javascript Date returned object

When the new Date() is called, it returns something like this
Wed Jul 13 2022 16:15:35 GMT-0600 (Central Standard Time)
So I was curious what is the correct format used in that case? I tried building it with the following format: E MMM d yyyy H:mm:ss zzzz However the result was
Wed Jul 13 2022 16:15:00 GMT-06:00
This is the closest I can get. I've been searching the internet but can't find the correct format. By the way, the local used is en_US
Example:
let momentDate = moment(date, 'E MMM d yyyy H:mm:ss zzzz', true);
If I execute the momentDate.isValid() it returns false.
From MDN: Date.prototype.toString() returns a string representation of the Date in the format specified in ECMA-262.
That last part is described as:
Optionally, a timezone name consisting of:
space
Left bracket, i.e. "("
An implementation dependent string representation of the timezone, which might be an abbreviation or full name (there is no standard for names or abbreviations of timezones), e.g. "Line Islands Time" or "LINT"
Right bracket, i.e. ")"
Different date formatting libraries in different frameworks will have different ways to get a string representation of the time zone and, importantly, this is implementation-specific. This SO answer shows one way to get that value, basically by outputting it from a date and parsing the result. But you should not rely on it (or really any aspect of date outputs, apart from toISOString()) having any consistency between browsers or other execution environments.
Until ECMAScript 2018 (edition 9), the format of the string returned by Date.prototype.toString was implementation dependent. Therefore it should not be relied upon to be in the specified format.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString#description
output variations are by design, and allowed by the specification... you cannot expect to be able to compare the results of toLocaleString() to a static value
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString#avoid_comparing_formatted_date_values_to_static_values

When is the month in a Date-object zero indexed

I know, because zero indexed month, for the date
Thu Mar 02 2017
I have to write
new Date(2017, 2, 2);
The same if I use a variable
var thismonth = 2; new Date(2017, thismonth, 2);
But, if I say
var thisday = [2017, 2, 2]; new Date(thisday);
I get a result with a one indexed month
Thu Feb 02 2017
This means, I can use the array-vars like the date-vars in the real life
var thisday = [2017, 3, 2]; new Date(thisday);
and get the (un)expected result
Thu Mar 02 2017
Thats nice, but confusing! Why this behaviour? Is there a rule?
That's an interesting find! However there is no documented Date constructor that accepts an array of values such as [2017, 3, 2], so what you are getting in this case isn't something you should rely on.
You can see that among your examples, only the first one is documented:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date
I don't think the code does what you think it does. The Date constructor does not accept arrays. Quoting the spec
Let v be ? ToPrimitive(value).
If Type(v) is String, then
Let tv be the result of parsing v as a date, in exactly the same manner as for the parse method (20.3.3.2). If the parse resulted in an abrupt completion, tv is the Completion Record.
which means you actually pass 2017,3,2 as a string. Now what happens to that string? Well, check the spec again.
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. The function first attempts to parse the format of the String according to the rules (including extended years) called out in Date Time String Format (20.3.1.16).
Interesting, another link to the spec
Your format doesn't follow the YYYY-MM-DDTHH:mm:ss.sssZ format or date only YYYY-MM-DD which means we're back to the previous spec section which says
If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
So basically you should not rely on it. Depending on the JS engine/browser the date might not be parsed at all. It looks like V8 accepts not only colons/dashes as separators, but also commas, which is why it works.
Edit: found it
https://github.com/v8/v8/blob/68960eeb763f93dcedb37d5b663b1019192b7f36/src/builtins/builtins-date.cc#L115
which calls
https://github.com/v8/v8/blob/68960eeb763f93dcedb37d5b663b1019192b7f36/src/dateparser-inl.h#L16
which has plenty of comments and weird stuff to keep searching if you want.

Why does the same date have different hours?

Maybe the answer is obvious, but I don't get it. Why are the Dates in the Code Snippet different? Does the format say something about the hours as well?
console.log("2017-1-9 -->")
console.log(new Date("2017-1-9"))
console.log("2017-1-09 -->")
console.log(new Date("2017-1-09"))
console.log("2017-01-9 -->")
console.log(new Date("2017-01-9"))
console.log("2017-01-09 -->")
console.log(new Date("2017-01-09"))
console.log("2017-01-11 -->")
console.log(new Date("2017-01-11"))
.as-console-wrapper {
min-height: 100%;
}
Even in my chrome-console and the code-snippet-console I get different dates:
My problem is not that both consoles log different dates. Look only at the hours in each console, for the very same date, only the datestring is different. Consequently my question:
Why are the dates different?
UPDATE
Why is new Date("2017-1-1").getTime() != new Date("2017-01-01").getTime() --> true?
var date = new Date("2017-2-9");
var dateWithZero = new Date("2017-02-09");
console.log(date);
console.log(date.getTime());
console.log(dateWithZero);
console.log(dateWithZero.getTime());
console.log(date.getTime() == dateWithZero.getTime());
.as-console-wrapper {
min-height: 100%;
}
The dates being displayed are using different timezones. The Chrome console output is using your browsers local timezone and adjusting it appropriately. The (GMT+0100) is telling you which timezone and adjustments that are being made. The code snippet console is displaying using UTC. The 'Z' at the end of the string signifies that.
If you want to convince yourself of that they are the same, print the timestamp for each date also. This is also why when you are dealing with dates, you should rely on timestamps rather than comparing dates like this. Things get very confusing and difficult to deal with.
TL;DR: Because the language specification says that date strings not conforming to the specified format can be parsed according to "any implementation-specific heuristics or implementation-specific date formats," and YYYY-M-D is just such a string.
Let's dive into the spec. Here's what the ECMAScript 5.1 spec says about the Date constructor (I'm quoting it instead of the current, ES2016 spec just because it's simpler, but the latter works basically the same in this case):
15.9.3.2 new Date (value)
...
The [[PrimitiveValue]] internal property of the newly constructed
object is set as follows:
Let v be ToPrimitive(value).
If Type(v) is String, then
Parse v as a date, in exactly the same manner as for the parse method (15.9.4.2); let V be the time value for this date.
...
And here's the spec for parse (emphasis mine):
15.9.4.2 Date.parse (string)
The parse function applies the ToString operator to its argument and interprets the resulting String as a date and time... 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. ...
Date Time String Format, in a nutshell, is YYYY-MM-DDTHH:mm:ss.sssZ and its subsets. Since YYYY-M-D doesn't conform to that format, the interpreter is (unfortunately) free to do whatever it wants. If you want to know why Chrome does it in this particular way, you'll have to dig around in the V8 source.
In some browsers, months or days with no leading zeroes may produce an error:
new Date("2017-2-9");
In consquent, that the behavior of the format "yyyy-mm-dd" is undefined.
Some browsers will try to guess the format, some will return NaN and some will return null.
That is why new Date("2017-02-09") has Thu Feb 09 2017 01:00:00 GMT+0100 (Mitteleuropäische Zeit) as output, because the behavior for this format is defined and it adds the timezone to the date. new Date("2017-2-9") has Thu Feb 09 2017 00:00:00 GMT+0100 (Mitteleuropäische Zeit) as output, because chrome trys to guess the format, but cannot add the timezone. In Safari in return null.

Why isn't "2016-02-16" equal to "2016-02-16 00:00"?

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?
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).
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 ;)
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.
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)
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.

please explain this javascript debugger output regarding Date constructor

I am trying to instantiate a date so that the code works in Chrome and IE (et al). Ideally I'd like to find a simple statement rather than a UDF, if it's possible. Is it not possible to Date.parse the string value in javascript when the time chunk is represented as T00:00:00?
Here's what I have in the Immediate Window in Visual Studio; caldate contains a string representation of a date returned by the back-end database; passing that string to Date.parse() returns a timestamp, 1371441600000, and passing that timestamp to the Date() constructor returns both Mon Jun 17 00:00:00 EDT 2013 and [prototype]: Invalid Date.
?caldate
"2013-06-17T00:00:00"
?Date.parse(caldate);
1371441600000
?new Date( Date.parse(caldate) );
Mon Jun 17 00:00:00 EDT 2013
[prototype]: Invalid Date
The Invalid Date is normal. That is just what the debugger prints for the proto object of a Date. I believe this is because the debugger calls the toString method on the proto object without supplying the actual Date instance, and so the toString method returns "Invalid Date".
I suggest you read the MDN documentation on Date.
You can just use new Date(caldate) to create a Date from your string.

Categories