Sep 10 2012

Asp.net Mvc and the Nightmare of Dates and Time Zones

Category: Asp.net | MVCFrancesco @ 03:06

Who never suffered because of Time zones problems in his application? I am speaking of dates that returns changed to the server without being modified by the user! This is just one of the two nightmares connected to the dates handling in web applications, the other one being the different formats of dates in different cultures. I aready discussed the second nightmare in this post about globalization, so in this post I will focus just on how to handle properly Time zones in web applications.

Someone, might say: “I am not interested in this subject because in my applications both the browser and the server share the same Time zone!”. Unluckly this is not the case! If you don’t care of Time zone related issues you might have problems also when browser and the server share the same Time zone!!

The point is that all json serializers try to send the date to the client in UTC format(The 0 Time zone date), so they may transform the date by adding the Time zone offset. Now when the json date reaches the client a javascript date is created using the UTC format, but it shows up in the Time zone of the Borwser, so if both browser and server are on the same Time zone the dates appear to be exactly the same in both the browser and the server. For instance, say you have 11:00 time on the server that is in the +02 Time zone, this date will be converted into 09:00 before being serialized into json. When this date reaches the client the browser creates a 09:00 UTC date, but before showing it, or before returning the Hour it will add again the Time zone offset of the browser, so the date will show again as 11:00 in the browser if browser and server share the same Timzone! And what in case browser and server are in different Time zones? Simple: the browser will show the time of the server converted into the Time zone of the bowser.

That’s right! It is exactly what we want…so what is the problem? Why sometimes we receive the wrong date on the server?…Because when the client sends the time to the server it sends to the server the UTC time, that is 09:00 , but this time the de-serializer doesn't transform automatically the date in the local Time zone…so we receive 09:00…that appears to be the wrong hour…I said “appears” because it is not the wrong hour!

In order to understand why the above behaviour is correct, we need to know that in .Net DateTime structures have a property called Kind whose possible values are:

  • DateTimeKind.Unspecified
  • DateTimeKind.Utc
  • DateTimeKind.Local

All deserializers just return a date in Utc format with the Kind set to DateTimeKind.Utc simply because they dont know how the remainder of the system processes the dates, so they use the neutral Utc format.

So if we need the date in local time we just need to convert it by calling the ToLocalTime method of the DateTime structure!

So, it is quite simple…or…not? Unluckly this doesnt solve the Time zone nightmare! In fact as one might expect the DateTimeKind.Unspecified value creates problems, and unluckly most of data sources (such as databases) set the Kind property just to DateTimeKind.Unspecified .

So…what if the kind is set to DateTimeKind.Unspecified ? The serializer dont know if the Date Time is expressed in the local Time zone or in Utc, so some serializers assume it is already in Utc format and will not subtract the Time zone offset before sending it to the client while others will subtract the Time zone offset. In particular:

  • The JSON.NET serializer used as default by WebApi controllers assumes that the date is in Utc format and doesnt subtract the Time zone offset
  • The Microsoft JavaScriptSerializer used by the remainder of the Asp.net Mvc framework assumes the date is in the local Time zone format and do subtract the Time zone offset.

As a conclusion to avoid problems, please substitute all Datetimes with DateTimeKind.Unspecified with DateTimes with the right Kind before serializing them to json.

Unluckly this substitution cannot be done easily in case of WebApi action methods that return an IQueryable<T> and are decorated with the [Queryable] attribute. In fact the IQueryable is executed after the query contained in the Url is applied to it by the [Queryable] action filter, so the substitution can be done only after this event by another action filter!

In the Mvc Controls Toolkit we defined an action filter that do this job. Below an example of usage:

  1. [Queryable, DateConversionFilter(DateConversionFilterOperation.DeclareLocale)]
  2. public IQueryable<ToDoView> Get()
  3. {
  4.  
  5.     return ToDoViewModel.GetToDoQueryable();
  6. }

Obviously we can also use DateConversionFilterOperation.DeclareUTC if we know all dates are in the UTC format.

Finished?….Wait….Wait….There are two more issues to discuss to get rid of the Time zone nightmare.

  1. As we discussed before dates that are sent in json format to the browser are shown in the local Time zone of the client, but dates that are processed directly on the server and put into input html fields are shown in the Time zone of the server, since the conversion into a string is done on the server. Accordingly, it is not adviced to mix the two techniques to send dates to the browser if browser and server may be in different Time zones. However, also dates rendered on the server maybe expressed in the client Time zone if we detect someway the Time zone of the browser.
  2. Dates only fieds whose time part is not processed on the client may create problems because of the truncation of the time part if server and browser are in different Time zones.

To understand better the problem stated in point 2. let suppose we have a day stored in the server database, say, 2012-01-10. When this date is transferred into a DateTime it becomes: 2012-01-10 00:00:00. Now suppose the Time zone of the server is +02, when this DateTime is transformed into  Utc before the serialization its value becomes : 2012-01-09 22:00:00. Now if the browser Time zone is +01, this date shows up as 2012-01-09 23:00:00 into an input fields,…but since the time part is truncated…it becomes simply 2012-01-09 …that is 2012-01-09 00:00:00. Thus it is sent to the server as an Utc date whose value is 2012-01-08 23:00:00. The sever adds 2 hours to the above value to get the date in local time, yielding: 2012-01-09 01:00:00, that is stored again in the database as 2012-01-09  with an error of one day!

Adding an offset to avoid this problem doesnt solve since this will cause problems with other client Time zones. There are just two ways two solve the problem properly:

  1. Avoiding truncations. This means that the time part that is not shown to the user must be stored someway and added to whatever input day the user insert. In the Mvc Contols Toolkit if we use Client Blocks and we decorate the input field with the data-date-only=”true” Html5 attribute, this job is done automatically by the Client Blocks engine.
  2. Rounding the result at midnight of the nearest day after each conversion of the DateTime into a different format. It can be proved mathematically, that the above technique yields the right result. Now if the date is stored with a 0 time part, rounding must occurr just after the date is transformed into the local time of the client, on the client side, and after the date is received again on the server. The two roundings must be done in exactly the same way. In the Mvc Controls Toolkit we defined the following helper methods to perform easily this job:
    • On the client side: MvcControlsToolkit_DateTimeToDate(date).
    • On the server  side: ControllerUtilities.RoundDateTimeToDate(DateTime x), and ControllerUtilities.RoundDateTimeToDate(DateTime? x). There is also a mehod that rounds all dates contained in a ViewModel: ControllerUtilities.RoundDateTimesToDates<T>(T x).  The rounding can be performed also while transforming the date from Utc to local time: ControllerUtilities.DatesToLocale<T>(T x, bool round=false) that converts to local time and rounds all dates contained in a ViewModel.

 

Summing up, in order to handle poperly dates we need:

  1. To ensure all dates have the right Kind property attribute before serializing them, possibly with the help of an action filter.
  2. To transform all dates received by the server from their Utc format into the desired format.
  3. To deal adequately with dates only fields with the help of the data-date-only=”true” attribute and of the rounding technique.

Now we have…actually finished…examples showing the techniques discussed in this post are contained in the Mvc4 Client- Filtering -Paging -Sorting-updating and Advanced JSon Communication files of the download area of the Mvc Controls Toolkit.

 

                       That’s All

                                     Francesco

Tags: , , ,