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.

At first, I thought it was a Safari compatibility issue on Date object. Research on the Internet suggests it seems that Safari could not parse some date format properly. But after looking at the javascript code for the date picker, I realized it is actually a serious bug in code.

In datepicker.js, two variables are defined:

1
2
var yesterday = today.setDate(today.getDate() - 1);
var tomorrow = today.setDate(today.getDate() + 1);

But setDate will actually return a number of milliseconds since 1 January 1970 (doc), so basically our yesterday and 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 tomorrow and 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)
screen shot 2016-03-10 at 00 00 47

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:

1
2
3
4
var yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
var tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);

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.

screen shot 2016-03-10 at 00 00 47

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.

Now this is still an issue with Safari, and this may be even a Safari bug that I should report to Apple. Why javascript has different behavior in different browsers? It’s because JavaScript is actually just a implementation of ECMAScript, and there is no specification on how different ECMAScript engine should work. This is not like JVM that everyone uses the same thing. So different browsers author may have their own engine, and hence leads to difference in executing JavaScript code. This is a thing that every JavaScript developer should understand and take care of.