Andre's Blog

Personal blog of Andre Perusse

Dates and Time Zones in Javascript, C#, and SQL Server

An issue that always seems to plague developers who write software that is used in multiple geographically disperse locations is that of dates and time zones. If a user in the Eastern time zone updates a record that contains a date and time field (say, 1/15/2014 4:35 PM), and then a user in the Pacific time zone views that record, what date and time are they supposed to see? The time as it was entered in Eastern time, or should they see their local time instead (1/15/2014 1:35 PM)?

Generally speaking (there are, of course, exceptions) date and time information should be stored in a database as a UTC (Coordinated Universal Time) time, and automatically converted and displayed in the user's current time zone in the client application. But that is often not as easy at it seems to implement. Below, I'll outline a process that has worked well for me in several apps.

Step 1 - Getting data from the client to the server

In the past, web apps collected data in an HTML <form> element that was submitted to the server causing a full page refresh. Today, I prefer to use single-page application frameworks where almost all client-server communication is done using the Javascript XmlHttpRequest (xhr) object. For me, I find jQuery nicely abstracts away the details of xhr using the $.ajax() api method. But how do we wrap up user-entered date-time data into an xhr request so that time zone data is preserved and can be interpreted by our server?

The Javascript date object does have time zone support, but it's not immediately obvious how we can make that work properly with the server. When sending data in an xhr object, it's usually JSON formatted, meaning just about everything is a string - there are no data types. Javascript date objects have a handy toString() method that outputs a date like this:

Sun Feb 23 2014 15:33:09 GMT-0400 (Atlantic Standard Time)

This would seem to contain all the information we require, but alas, the default ASP.NET WebAPI configuration can not translate this to a C# DateTime instance. But there is a solution - the ISO-8601 date format, which looks like this:


In fact, most modern browsers have the ability to output this format using the toISOString() method, but there are a couple of drawbacks. First, the toISOString() method is not supported on all browsers and second, this built-in method deletes the time zone information and simply outputs it as UTC (such as 2014-02-23T19:33:09Z). Sometimes it is desirable to preserve the originating time zone data.

Thus, I prefer to use the excellent moment.js library for formatting date and time values. It is incredibly flexible and powerful and you'll find yourself using it everywhere in your Javascript code when dealing with dates. The default format() method of moment.js outputs dates in ISO format.

Step 2 - Processing on the Server

Actually, with WebAPI and its default use of Newtonsoft's Json.NET library, there is nothing else you have to do on the server. Using the default model binder, any date that is submitted in ISO-8601 format will be automatically converted to local server time and, most importantly, the Kind property of the DateTime instance will be set properly to DateTimeKind.Local.

Step 3 - Saving in SQL Server

Now here's one step I see a lot of people miss - saving the datetime properly in SQL Server. We want the date to be stored as a UTC value so that it can be converted to any time zone for display purposes. In order to do this, you must convert the C# datetime instance to UTC when adding it to the parameters of your stored procedure, like so:

parameters.Add("myDateField", myDateTime.ToUniversalTime());

Step 4 - Retrieving from SQL Server

Another step often missed by developers is the requirement to properly stamp the Kind property of the C# DateTime instance when retrieving it from the database. You must mark the value as UTC like this:

myDateField = DateTime.SpecifyKind((DateTime)dbReader("myDateField"), DateTimeKind.Utc);

Step 5 - Displaying on the Web Page

Actually, there is no step 5. The Json.NET serializer will format the date value in ISO-8601 format which is automatically converted to local time in the client Javascript code. If you output this value to the web page, it will be displayed in client local time.

By following this process you can ensure that all date and time data in your database is consistently represented as UTC, and can be reliably converted to the time zone being used by the client application. One gotcha to be careful of, however, is when filtering date values. Just like storing a date value in the database, the client specified date filter must be converted to UTC time before the comparison against SQL data is performed.

Happy coding!

Comments (3) -

  • Frode

    10/24/2015 1:02:17 PM |

    I'm struggeling with dates in javascript, webapi2, C#and MSSQL at the moment. This was very helpful. Thanks!

  • Alejandro

    4/14/2016 4:23:52 PM |

    This is what I was looking for. Thanks a lot!

  • kiquenet

    2/23/2018 12:47:55 PM |

    What's about DateTime Kind using Dates from Sql Server, JSON.NET for serializer and REST API... ?

    I'm GMT+1 time zone (Madrid)

    string isoDate1 = DateTime.Now.ToIso8601Date();
    Console.WriteLine(isoDate1 + " Kind: " + DateTime.Now.Kind);

    DateTime date1 = isoDate1.FromIso8601Date();
    Console.WriteLine(isoDate1 + " Kind: " + date1.Kind);

    string isoDate = DateTime.UtcNow.ToIso8601Date();
    Console.WriteLine(isoDate + " Kind: " + DateTime.UtcNow.Kind);

    DateTime date = isoDate.FromIso8601Date();
    Console.WriteLine(isoDate + " Kind: " + date.Kind);

    2018-02-23T13:27:05Z Kind: Local
    2018-02-23T13:27:05Z Kind: Unspecified

    2018-02-23T12:27:05Z Kind: Utc
    2018-02-23T12:27:05Z Kind: Unspecified

    DateTime has countless traps in it that are designed to give your code bugs:

    DateTime values with DateTimeKind.Unspecified are bad news. It's common for code to call ToUniversalTime() or ToLocalTime() to make sure it's got the date properly ready for storage or display respectively. If you call ToUniversalTime() it converts X hours in one direction, but if you call ToLocalTime() on it, it converts X hours in the opposite direction. Both of those can't possibly be correct.
    and more...