Why does the same date have different hours? - javascript

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.

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

What is the input timezone for javascript `new Date` function? [duplicate]

In doing some testing I've found inconsistant behavior between browsers with the following javascript
new Date("2013-09-10T08:00:00").toString()
In IE and Firefox the result is
"Tue Sep 10 2013 08:00:00 GMT-0400 (Eastern Daylight Time)"
In Chrome the result is
"Tue Sep 10 2013 04:00:00 GMT-0400 (Eastern Daylight Time)"
So according to my reading of the ECMA script of the format for Date strings it says...
All numbers must be base 10. If the MM or DD fields are absent "01" is
used as the value. 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"
However in the documentation for the "new Date()" constructor it says
15.9.3.2 new Date (value)
Let v be ToPrimitive(value).
If Type(v) is String, then
a. 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.
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; 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.
Any ideas which implementation is correct?
Standards clash. ISO 8601 states that:
If no UTC relation information is given with a time representation, the time is assumed to be in local time.
ECMA says:
The value of an absent time zone offset is “Z”.
Mozilla devs think that ISO takes precedence, Chrome folks seem to disagree.
The current draft of ES6 says (under 20.3.1.15):
If the time zone offset is absent, the date-time is interpreted as a local time.
so Mozilla's implementation is (will be) correct.
There are several questions on stackoverflow.com that address this issue. I gave a rather thorough explanation here if anyone reading this is interested in the browser-to-browser details.
The bottom line though is, for now at least, you should either avoid the ISO 8601 format all together or ALWAYS include a timezone specifier when using it. And, never use the 'YYYY-MM-dd' format because it gets interpreted as a short version of ISO 8601 without a time zone specifier.

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.

Date changes results if called with an array of parameters

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.

new Date() vs Date() and why does it return a different time (-2 hours)?

I have these 2 console logs, but they return different times (-2 hours off).
console.log(new Date()) // Date 2015-04-20T15:37:23.000Z
console.log(Date()) // "Mon Apr 20 2015 17:37:23 GMT+0200 (CEST)"
I know using Data() is the same as using the constructor and calling .toString().
However, i do need the Date() time and not the new Date() time. So why is it returning the wrong time and how can i reset it to output the correct one?
thx,
So why is it returning the wrong time and how can i reset it to output the correct one?
Your first statement is calling console.log with a Date object. It appears that whatever browser/console plugin you're using chooses to display that Date object by using an ISO-8601 string in UTC. E.g., the format is chosen by the console implementation.
Your second statement is calling console.log with a string, so the format is chosen by the Date implementation. The specification makes no requirement on what that string format must be from JavaScript engine to JavaScript engine (yes, really*) other than that it must be in the local timezone.
Apparently, on your browser, the console implementation and the Date#toString implementation don't match up.
However, i do need the Date() time and not the new Date() time.
Those strings define the same moment in time (plus or minus a couple of microseconds); it's just that the strings have been prepared with different timezone settings.
If you need to log a string to the console in local time, use
console.log(String(new Date()));
...to reliably get the string from the Date object, not something generated by the console.
* Yup, the format you get from Date#toString is undefined and entirely up to the JavaScript implementation; the only requirement is that Date() and Date.parse() can both parse the string toString outputs and get back to the original date. (JavaScript didn't even have a standard date/time format until ES5, and it only applies to the toISOString method, not toString. And ES5 got it slightly wrong and it had to be amended in ES6, leading to the unfortunate situation at the moment where Chrome does one thing and Firefox does another when parsing strings in the new date/time format with no timezone on them.)
Those are the same time. The string generated by Date() just uses a different timezone, as can be seen from the GMT+0200 (CEST) suffix.
So if you need your time string in your local timezone, just use .toString() instead of .toUTCString().

Categories