Thoughts from a bug with Date object
Recently I met an interesting issue on TEAMMATES. One of the datepicker in an input box could not be displayed properly on Safari. This issue happened on Safari only as the datepicker could be shown on other browsers such as Chrome and firefox.
datepicker.js, two variables are defined:
var yesterday = today.setDate(today.getDate() - 1);
setDate will actually return a number of milliseconds since 1 January 1970 (doc), so basically our
tomorrow will be two numbers instead of two Date objects. This is where things start to go wrong.
Now we pass
tomorrow as a
defaultDate option to initialize a jQuery-UI datepicker. But why datepicker didn’t complain at this time? After checking the jQuery UI document, it suggests that, if the
defaultDate is a number, then it will be treated a date offset from today (-1 means yesterday, +1 means tomorrow, etc.). Since
yesterday are very big numbers (milliseconds since 1 January 1970), datepicker will try to use the
tomorrow as a offset from today and create that new date, but it realized the offset is too much and the date is an “invalid date”. At this point, I thought the correct behavior is actually report that the default date is invalid and throw an error. But why it doesn’t? Research on source code indicates that if the date is invalid, the library will actually create another default date (
new Date()) and use it as the displayed date.
Here is a debug screenshot in the jQuery UI source code with Chrome DevTool, you can see the date is a number (the pass-in
tomorrow variable) and
newDate is “Invalid Date” (failed to parse it)
Therefore, even if the datepicker works on Chrome previously, it didn’t work as expect to show the
tomorrow date, but it will still show “today” as it use
new Date() as the time. And nobody noticed this bug until it goes wrong on Safari.
I fixed the issue in this PR by fixing the date object:
var yesterday = new Date();
Now the bug is fixed and works well on all browsers.
However, one thing still confuses me. Since if the
defaultDate is invalid, the datepicker will create another date to use. But why it sill fails on Safari?
Then I recreate the bug, on both Chrome and Safari, and finally figure out the reason.
In the screenshot, the above browser is Chrome, and below one is Safari. I manually update the date with a very big offset, and I realize that in Chrome, it knows that the resulted date is invalid, and will set the date to “Invalid Date”. While in Safari, it will still parse the invalid date into a very “Invalid” date object. That’s why the datepicker failed to display properly in Safari, because it doesn’t realize the date is “invalid” :(
I checked with Firefox as well as Node’s V8 engine, they all can detect the “Invalid Date” properly.