According to the specification, the format in question ("year-month-day", format defined by ISO 8601 standard) should have the month and day always with 2 digits.
Anything other than this will have undefined behavior, and depend on the browser/environment in which the code runs. For example, testing in Firefox (version 68.0.2 Windows 64-bit), both new Date('2019-9-10')
how much new Date('2019-09-10')
gave the same result. But in Chrome (version 76.0.3809.132 Windows 64-bit) and Node (version 14), the results were different, the way you reported.
In my opinion, this behavior is in accordance with this section of the specification:
If the String does not conform to that format the Function may Fall back to any implementation-specific heuristics or implementation-specific date formats.
In free translation:
If the string does not agree with this format, any implementation-specific heuristic or format can be used.
That is, how 2019-9-10
does not fit the specified format, each browser deals with the way you think best.
That said, in the documentation of Date
there is the following warning:
date-only strings (e.g. "1970-01-01") are treated as UTC
In free translation:
strings containing only the date (e.g., "1970-01-01") are treated as UTC.
In addition, the documentation also says that the time fields, when omitted, are set to zero.
Does that mean that new Date('2019-09-10')
(note that the month is written in double digits) creates a date corresponding to September 10, 2019 at midnight (00:00:00.000) on UTC.
But when you print a date (be with console.log
, or when the browser console itself shows the created value), it is converted to the browser time zone (which in turn, usually uses what is configured in the operating system).
In your case, by the results, the time zone is the Official Time of Brasilia.
And how 10 September 2019 at midnight in UTC is equal to 9 September 2019, at 21h in Brasilia Time, You see this "difference". In general, Brazil’s time zones are always a few hours behind UTC (Brasília Time is 3 hours ago - or two, when it is daylight saving time), so any date created at midnight in UTC will correspond to the previous day in Brasília Time.
When the month is with only one digit, Chrome considers it to be midnight in the browser’s time zone (in this case, midnight in Brasilia Time), and not in UTC (as happens when the month has 2 digits). Like I said before, probably is some implementation detail, which is different from Firefox (because in this browser, in both cases, the result was midnight in UTC).
Another point that can explain this difference is that the standardization of the format was only defined in SE5 (in 2009). Before that, each browser implemented different formats (and with different results), which may or may not work in other browsers, and may or may not have the same behavior. Many have been maintained to this day for reasons of backward compatibility (as is probably the case for 2019/09/10
), and so there is no guarantee that they will work the same way in all browsers.
Anyway, if you want to maintain the same independent behavior of the month, you have two options:
if you want the time at midnight at UTC (which may not match the same day in the browser’s time zone), always use the 2-digit month in the string (2019-09-10
instead of 2019-9-10
).
if you want the midnight hour in the browser time zone, can use the constructor that receives the numeric values. For example, new Date(2019, 8, 10)
creates a date for September 10, 2019, at midnight, in the browser Timezone (yes, you have to subtract 1 of the month, because the months are indexed at zero: January is zero, February is 1, etc).
- another way to force the hour to midnight in the browser’s time zone is to use the 2-digit month and day and include the time:
new Date('2019-09-10T00:00')
So, the problem is that I had no bug with dates with month with 1 digits (January to September), I only had problem with the months that have 2, November to December. Although this part of the time zone could have some relation, this error happens only in months with 2 digits, and dates with 2 digits, 2019-12-9 not error, but with 2019-12-10 happens.
– EJSpawn
@Ejspawn That’s what I said, if you use a 1-digit month or day, the format is no longer accepted in the specification and each browser will handle it one way (in Chrome it makes a difference, not in Firefox). I don’t consider bug because the specification says this can happen (any format that does not have the month and day with 2 digits will be dependent on the implementation). Anyway, I suggest you always put in two digits, and if you want you can force the time to midnight, because then he starts to consider the local time. Ex:
new Date('2019-12-09T00:00')
(Add "T00:00" to force the hour to midnight)– hkotsubo
In my case I could not set the hour to midnight, but I saw that effectively it would solve the problem. Another solution that best served my case was to create a Function that would return the date without a time zone: data.valueOf() - data.getTimezoneOffset() * 60000
– EJSpawn
@Ejspawn Actually, by subtracting from the
valueOf
vc is creating a date in the past (which by coincidence can show a date and time value "correct"). I explain this in detail in that reply (is in the "What if I use another timestamp?" section, but it’s interesting to read everything to better understand what actually happens when subtracting fromvalueOf
) :-) Even at the beginning of the answer I explain why aDate
Javascript actually has no time zone...– hkotsubo