Meditation and Mindfulness for Software / IT Professionals. Conducted by Bipin Joshi in Thane. Read more...

Store DateTime as UTC and Convert As Per User's Time Zone

Developers often store date and time values in the database that are returned through DateTime.Now. These values are returned as per server's time zone. In some cases, however, you need to display these values to the users as per their time zone. This post discusses one way to deal with the situation.

Let's try to understand the problem outlined above with an example. Say you are building a discussion forum software. Each message and its reply has a date-time stamp. This date-time stamp is to be displayed to the end user as part of the UI of the discussion forum. Now the problem is - the date-time stamp obtained using DateTime.Now (or any such normal date function) are server side date-time values. Let's say the said system is hosted on a server residing in the USA. That means the date-time stamp will be as per USA time zone. Now if user's from other countries (say UK, India etc.) use the same system then the time they see on the pages won't be as per their time zone. This can lead to confusion amongst the users. Better way would be to show the date-time stamps as per individual user's time zone.

A user's time zone can be obtained at the time of registration or at the time of filling the user profile. This will need to be stored somewhere in the database. Additionally, you should store all the date-time values in UTC format instead of server's time zone.  Coordinated Universal Time or UTC is the time standard using which clocks and time are tuned. You can think of UTC date-time as a "neutral" value, not belonging to a specific time zone. When you have UTC date-time and user's time zone you can convert UTC value to user's local date-time value using inbuilt methods of certain .NET date-time related structures (you will know about them later in this post).

Ok. Now that you know what the problem is, let's build a simple ASP.NET MVC example that illustrates the date-time conversion mentioned above. Here is how the sample view looks like:

As you can see the Index view shown above consists of a DropDownList. The DropDownList shows a list of all time zones. You can select one and click on Show button. Doing so displays the current UTC date-time value as well as date-time as per selected time zone. In a more realistic case the time zone will come from database or some storage instead of a DropDownList.

The following code shows the Index() action method of the Home controller that deals with the GET (initial) request.

public ActionResult Index()
{
    var timeZones = TimeZoneInfo.GetSystemTimeZones();
    List<SelectListItem> items = new List<SelectListItem>();
    foreach (var timeZone in timeZones)
    {
        items.Add(new SelectListItem() { Text = timeZone.Id });
    }
    ViewBag.TimeZones = items;
    return View();
}

The code uses TimeZoneInfo class and calls its GetSystemTimeZones() method. This method returns all the time zones as a collection of TimeZoneInfo instances. The TimeZoneInfo object has Id property that gives a unique string ID for the time zone under consideration. In our example this ID is displayed in the DropDownList. The List of SelectListItems is passed to the Index view through ViewBag's TimeZones property.

The other Index() handles POST request (when user clicks on Show button) and is shown below:

[HttpPost]
public ActionResult Index(string timezone)
{
    var timeZones = TimeZoneInfo.GetSystemTimeZones();
    List<SelectListItem> items = new List<SelectListItem>();
    foreach (var timeZone in timeZones)
    {
        items.Add(new SelectListItem() { Text = timeZone.Id });
    }
    ViewBag.TimeZones = items;

    //store dbDateTime in the database

    DateTime serverDateTime = DateTime.Now;
    DateTime dbDateTime = serverDateTime.ToUniversalTime();

    //get date time offset for UTC date stored in the database
    DateTimeOffset dbDateTimeOffset = new 
                    DateTimeOffset(dbDateTime, TimeSpan.Zero);

    //get user's time zone from profile stored in the database
    TimeZoneInfo userTimeZone = 
                 TimeZoneInfo.FindSystemTimeZoneById(timezone);

    //convert  db offset to user offset
    DateTimeOffset userDateTimeOffset = 
               TimeZoneInfo.ConvertTime
              (dbDateTimeOffset, userTimeZone);

    //format user offset for display purpose
    string dbDateTimeString = 
                    dbDateTimeOffset.ToString
                    ("dd MMM yyyy - HH:mm:ss (zzz)");
    string userDateTimeString = 
                    userDateTimeOffset.ToString
                    ("dd MMM yyyy - HH:mm:ss (zzz)");

    ViewBag.UtcDateTime = dbDateTimeString;
    ViewBag.UserDateTime = userDateTimeString;

    return View();
}

This is an important method because it does the date-time conversion mentioned earlier. Since we don't have any database in this example, the code uses DateTime.Now and stores it in serverDateTime variable. This date-time is as per server's time zone. So, ToUniversalTime() method is used to convert it into UTC value. The UTC value is stored in dbDateTime variable.

Then DateTimeOffset structure is created based on the UTC date-time just obtained. The DateTimeOffset structure stores an offset value relative to UTC in addition to the date-time.

Then user's time zone is obtained using FindSystemTimeZoneById() method of TimeZoneInfo class. This method takes a string parameter - a time zone id. Recollect that this time zone ID is selected from the DropDownList.

Then dbDateTimeOffset is converted as per user's time zone. This is done using ConvertTime() method of TimeZoneInfo class.

For the sake of display, UTC date-time offset and user's date-time offset are formatted and assigned to two ViewBag properties - UtcDateTime and UserDateTime.

Note :
There is also TimeZoneInfo.ConvertTimeFromUtc() method that converts a UTC date-time to user's time zone. However, there is a problem. It returns DateTime without user's time zone details. So, if you call ToString() on it with the format specified above, the zzz part will be as per server's time zone. DateTimeOffset, on the other hand, contains user's time zone and gives us correct formatting.

The Index view that displays the formatted date-time values is shown below:

...
...
@using(Html.BeginForm("Index","Home",FormMethod.Post))
{
    @Html.DropDownList("timezone",
               ViewBag.TimeZones as List<SelectListItem>)
        
    <input type="submit" value="Show" />
}

<br />
<h3>UTC Date Time : @ViewBag.UtcDateTime</h3>
<h3>User's Date Time : @ViewBag.UserDateTime</h3>
...
...

The Index view displays a DropDownList and populates it with the values from ViewBag.TimeZones. The view also outputs UtcDateTime and UserDateTime ViewBag properties.

That's it! Run the application and try a few time zones to confirm that the conversion is correctly being done.




Bipin Joshi is a software consultant, an author and a yoga mentor having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 10 Feb 2015



Tags : ASP.NET Data Access MVC C#