Javascript bug that subtracts day from -1 and adds time in 2-digit months+days

Asked

Viewed 487 times

2

I was interacting with a calendar component made with pure Javascript, passing an array of dates to the component to select the corresponding dates in the calendar.

I noticed that when I sent full month date ranges, dates were added effectively, except in October, November and December.

At first I thought it was a plugin bug, later I found that the problem was actually in the parse of the object Date using hyphenate and only in months + days with 2 digits...

The bug subtracts a 1 day at the end date, and sets a time at the date.

The value is correct with 1 digit months: cro

Incorrect value with 2-digit months: inserir a descrição da imagem aqui inserir a descrição da imagem aqui inserir a descrição da imagem aqui

The value is correct if you do not use hyphen: inserir a descrição da imagem aqui

Does anyone know why?

1 answer

8


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:

  1. 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).

  2. 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.

  • 1

    @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)

  • 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

  • 1

    @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 from valueOf) :-) Even at the beginning of the answer I explain why a Date Javascript actually has no time zone...

Browser other questions tagged

You are not signed in. Login or sign up in order to post.