JSON DateTime Serialisation Gotchas

DateTimes are a bit nasty, really. They appear deceptively elementary and unthreatening, leading generations of programmers to misuse them, or even grossly underestimate them and attempt to roll their own datetime libraries, only to end up in no man’s land with war stories galore. At work, we recently encountered a problem where the dates on our front-end lagged the dates in our database by a day. This was immediately indicative of a time zone issue, but we weren’t sure exactly where it was coming from.

It turns out that the discrepancy was partially a result of our MVC3 web service serialising datetimes using its default JavaScriptSerializer, and then our front-end deserialising them using DataContractSerializer – they use different formats. But worse still, after doing a bit of testing, I discovered that JavaScriptSerializer’s serialisation and deserialisation operations aren’t inverse. That’s to say that if you use it to serialise a DateTime of a non-UTC kind (i.e. local or unspecified), then deserialise the result, you’ll end up with a different date to the one you started with.

Here’s what happens when you serialise a DateTime of 1/1/2000 12 am with its Kind set to UTC and Local respectively. A bigger number means a later date/time. Note that NZ is UTC+13, so 1/1/2000 Local is equivalent to 31/12/1999 11am UTC. As this is earlier than 1/1/2000 UTC, the Local  number is smaller than the UTC one.

JavaScriptSerializer DataContractSerializer
UTC \/Date(946684800000)\/ \/Date(946684800000)\/
Local \/Date(946638000000)\/ \/Date(946638000000+1300)\/

Prior to serialisation, both serialisers convert the DateTime to UTC if it’s Local/Unspecified – and in the case of NZ, this means subtracting time. The difference is that the DataContractSerializer keeps track of this subtraction by way of a timezone annotation, so that you can reliably deserialise it later. And it shows, because the DataContractSerializer will spit your original DT back at you if you point it at either of its serialised forms above. The same can’t be said for the JavaScriptSerializer, which treats everything as UTC, and hence, only succeeds with UTC DTs.

This won’t be an issue for people who always convert their DateTimes to UTC before sending them across the wire. It caught us out because we were transmitting Unspecified DateTimes which are treated the same as local dates, causing the JavaScriptSerializer to  convert them to UTC, subtracting 13 hrs in the process. Since we wanted to pass around semantically unspecified DateTimes (i.e. unknown time zone), it didn’t make sense to convert to UTC. So we decided to instead serialise them as strings in the format YYYY-MM-DDThh:mm:ss, a variation on a W3C suggested format found here.

About Nathan Pitman

Undergrad software engineer at the University of Auckland
This entry was posted in C#, Web and tagged , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>