This week I had the particular displeasure of working with Date parsing algorithms across browsers. The internet, in general, has pretty much settled on ISO 8601 and variations thereof as the standard formats for representing date / time information. For example, the W3C specifies a subset of 8601 as the official date/time format in the HTML5 specification and for the datetime attribute of the time element.
The rub, of course, comes in when you decide that you want to parse dates formatted like so. On Chrome, Safari, Firefox, and IE 9+, it’s actually not that bad. You can correctly use the new Date constructor and get something like…
> new Date("2014-02-07T07:06:41Z") Fri Feb 07 2014 02:06:41 GMT-0500 (EST)
Seems legit right? Unfortunately, I live in a world that requires me to also support IE8, which does not support 8601 properly.
> new Date("2014-02-07T07:06:41Z") NaN
Boom! Well it’s right, in a way. A date is technically not a number.
Running parallel to this I was also making use of Date.js, which in an ideal world would smooth over these discrepancies for us. Date.js, however, actually breaks 8601 support in its implementation of Date.parse. So, bupkis on that front. I needed to evaluate options to get date parsing working reliably cross-browser.
Using a Different Format
The first option I evaluated was using a different format for dates that were sent down from the server. It turns out that all browsers can properly interpret a date formatted like so:
Fri Feb 07 2014 02:06:41 GMT-0500 (EST)
So, if we could just send down that format then, in theory, things “just start working.” Unfortunately I found a few issues with this plan when I started implementing it.
- Other JS libraries that were in use were (correctly) expecting ISO 8601 formatted dates. When we tried to move to the format above, they broke.
- Since 8601 was the standard for the datetime attribute of
My conclusion on realizing these points was that switching date formats was the incorrect approach. Standards compliance aside, to get this solution working properly, we were going to essentially need to modify every library that had correctly standardized on ISO 8601 to use the format we wanted. It was the date parsing equivalent of 1984.
Doubling down on ISO 8601
The option I decided to pursue in the end was to double down on ISO 8601. This meant I needed to do a few things. First, I needed to fix the fact that Date.parse was broken because of Date.js. Second, I needed to somehow augment that Date.parse method such that IE8 could parse ISO 8601 dates. This was not a completely new rodeo. I ran across some helpful solutions on StackOverflow that pointed me in the right direction. The first was an alternate implementation of Date.js’s parse method and the second was a pretty succinct implementation of a manual 8601 parser.
I mish-mashed these implementations together into my own implementation that essentially amounts to the following logic:
- When Date.parse is called, try to use the Date constructor on whatever string is passed in. If you get a valid Date, return it.
- If not, try to run the string through Date.js’s grammar parser. If you get a valid Date, return it.
- If not, assume the string is ISO 8601 and that the reason step 1 failed is because you’re in IE8. Run the manual parsing algorithm and return the resulting Date instance.
Ultimately, this appears to be pretty stellar. We now just need to make sure we standardize our code to use Date.parse instead of the Date constructor, and our intelligent Date.parse method will take care of the rest. Browsers that natively support 8601 won’t be negatively impacted by the manual algorithm, and we still have the full power of the Date.js grammar parsing when we want it.
There are, however, a few interesting notes with this implementation.
- According to the Mozilla Developer Network the native Date.parse method “parses a string representation of a date, and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC” (link). This implementation does not do that. My implementation returns a Date instance instead of a number of milliseconds, which is in line with the behavior that Date.js already implemented.If you would prefer to match the specified, native implementation by returning milliseconds, you’ll need to tweak your implementation accordingly.
- We do inherit a bit of overhead from this approach by having to keep an eye on the behavior of the grammar parser whenever we update Date.js. If its behavior changes in some significant way (e.g. starts throwing an exception instead of returning null for 8601 dates) we’ll need to make sure we’re aware of that and keep our custom implementation in sync.
- We have to enforce the usage of Date.parse over new Date() in our code via code review. Both will seem to yield the correct result, but only one will do so across our entire browser matrix and that’s something the entire team is going to need to be aware of.
Ultimately, after beating my head against the brick wall that is IE for several hours I was pretty happy with the way this came out. As always, would love to hear some comment love on the solution. Let me know what you think. ?