How to display dates and times in clients timezone
Introduction
Dates and times are very important in any online application. If you are making an online transaction it is important to record the date and time you are making the payment. Usually those values are recorded by the payment processing system and stored in a database. This payment processing system can be built using different server-side technologies like Java, ASP.NET, PHP etc. Most of these languages provides a built-in data-type to store date and time. The nice thing about web is the payment processing system and all the data associated with it is stored in a server that can be located in any part of the world and the users from all over the world can access the system for making payments through a browser.
Since different parts of the world have different timezones the same event can be represented by different date-times depending upon the timezone. In this article I’m going to explain you how we can display the date-time in clients timezone. Although I used ASP.NET MVC for explaining things in server-side the principle is applicable for most of the server languages. One important thing to note down is the method that applied here not considers the daylight savings time into account on displaying date-times to the user.
Problem
Suppose the server is located in USA (MST) and an user from Chennai (IST) is making a payment. Both the server and user are in different timezones. The MST (Mountain Standard Time) is 7 hours behind GMT (Greenwich Meridian Time) and the IST (Indian Standard Time) is 5.30 hours ahead of GMT, that is both the server and user have a time difference of 12:30 hours. If the user is making the payment on 9/20/2011 9:00 AM then the time at the server is 8/20/2011 8:30 PM. So which time you have to record in the database? If the application stores the user’s time in the database then if someone from US is seeing the transaction will likely see a wrong time i.e. 8/20/2011 8:30 PM. The other option is storing the server’s time in the database (that what we normally do), then the user will be unhappy seeing a wrong time.
So storing the date-time of the transaction in either client timezone (IST) or server timezone (MST) is problematic. Instead of that we have to store the date-time in a common timezone and that is the GMT timezone. The GMT timezone is also called as UTC i.e. Universal Coordinated Time. ASP.NET provides a built-in class DateTime to store date-time. Mostly people use the DateTime.Now property for calculating current date-time to store in database or display to the user. DateTime.Now returns the current date-time based on the server timezone. If the server is in US then it returns the date-time based on that timezone. This won’t creates problem if both the server and the users are in same timezone but mostly that won’t be the case. When you are creating application that will be consumed by users from different timezones then you have to think seriously using the DateTime.Now property. Not only the users will see a wrong time but if those data are stored in database and later the server is moved to different timezone then those data are non-transportable unless adjusting the stored date-times by running a script.
The DateTime class also has another property that returns the current UTC time and it is UtcNow. When storing date-times in database you have to always (unless the server and users are always going to be in the same timezone) store them in UTC timezone. Storing is just a part of the story but you can’t display these UTC times directly to the user, you have to convert these date-times that are in UTC timezone to the clients timezone and for that you have to know the timezone of the client.
How a server can know the timezone of the client? The client access any web application by sending requests and these requests are called as HTTP requests. These HTTP requests carries many information like the HTTP method (GET, POST..), User-Agent(browser) etc. but not the timezone of the user. Knowing the timezone is really tricky and we need little help from JavaScript to achieve that.
Solution
JavaScript also has a Date class to represent date-times. This class provides different methods to work with UTC. Some of the interesting methods are:
Method | Explanation |
---|---|
getTimezoneOffset() | Returns the difference in client timezone and GMT in minutes. If the client timezone is ahead of GMT returns a negative value. For ex. if the user is in IST(5:30 hours ahead of GMT) then the method returns -330. |
toUTCString() | Returns the current UTC time ex. "Fri, 23 Sep 2011 11:13:38 GMT". |
UTC() | Returns the number of milliseconds in a date string since midnight of January 1, 1970, according to universal time. |
The method that I’m interested upon is getTimezoneOffset() that returns the time difference between client and GMT timezones in minutes. By knowing this value when the client send the request we can adjust the UTC date-times into client date-time. For ex. if the method returns -330 then I’ve to add 330 minutes or 5:30 hours from the UTC to convert get into client date-time.
There are different ways to pass this offset value to the server from client but one of the way I’m going to show in the article is using Cookies. So the strategy is we are going to store the timezone offset in the cookie and since the cookies are sent to server for every request; from the server side we have to retrieve the value from the cookie and store in session. Whenever we are going to display the date-time to the user we have to convert the UTC date-time into client time based on the offset value stored in the session.
So these are the steps we are going to do in the client-side and server-side.
Client-side
- Create a script that will be executed at every time the page loads.
- In the script check if the cookie that stores timezone offset exists.
- If the cookie not exists check whether the browser supports cookie.
- If the browser supports cookie create a new one and store the timezone offset.
- Reload the page if the cookie is created for the first time or expired.
- If the timezone cookie already exists check if the current timezone and the one stored in cookie are same. If they are different then overwrite with new timezone and reload the page.
Server-side
- Read the cookie value on every request and store it in session.
- Create a utility method that converts the passed datetime in UTC to the client timezone by adjusting it with the offset read from session.
- If there is no value in session possibly the browser doesn’t supports cookie then convert the date-time to server timezone.
Lets write some code. I’ve used the jQuery library and a plugin for creating / managing cookies that is downloadable from here. Below is the script that does the client-side job.
$(function(){ setTimezoneCookie(); });
function setTimezoneCookie(){ var timezone_cookie = "timezoneoffset"; // if the timezone cookie not exists create one. if (!$.cookie(timezone_cookie)) { // check if the browser supports cookie var test_cookie = 'test cookie'; $.cookie(test_cookie, true); // browser supports cookie if ($.cookie(test_cookie)) { // delete the test cookie $.cookie(test_cookie, null); // create a new cookie $.cookie(timezone_cookie, new Date().getTimezoneOffset()); // re-load the page location.reload(); } } // if the current timezone and the one stored in cookie are different // then store the new timezone in the cookie and refresh the page. else { var storedOffset = parseInt($.cookie(timezone_cookie)); var currentOffset = new Date().getTimezoneOffset(); // user may have changed the timezone if (storedOffset !== currentOffset) { $.cookie(timezone_cookie, new Date().getTimezoneOffset()); location.reload(); } } }
The setTimezoneCookie() method first checks whether our cookie exists if not check whether the browser supports cookie and create a new one storing the timezone offset. If the cookie already exists then check whether the stored and the current offset values are same. If they are different overwrite with the current offset (this happen when the user changes the timezone in system).
Let’s complete the things that we have to do in the server-side. In ASP.NET MVC we have to retrieve the cookie value from every request and store in session and one way that can be achieved is creating a base controller and overriding the OnActionExecuting event.
public class BaseController : Controller { protected override void OnActionExecuting(ActionExecutingContext filterContext) { if (HttpContext.Request.Cookies.AllKeys.Contains("timezoneoffset")) { Session["timezoneoffset"] = HttpContext.Request.Cookies["timezoneoffset"].Value; } base.OnActionExecuting(filterContext); } }
All the controllers in the application has to derive from the BaseController. Regarding the utility method let’s create a nice extension method named ToClientTime() for the DateTime class that will read the offset value stored in the session and adjust the passed UTC time accordingly. If the session don’t have an offset value (that may happen if the browser doesn’t supports cookie), then the method returns server time by calling the built-in method ToLocalTime().
public static string ToClientTime(this DateTime dt) { // read the value from session var timeOffSet = HttpContext.Current.Session["timezoneoffset"]; if (timeOffSet != null) { var offset = int.Parse(timeOffSet.ToString()); dt = dt.AddMinutes(-1 * offset); return dt.ToString(); } // if there is no offset in session return the datetime in server timezone return dt.ToLocalTime().ToString(); }
Sample Application
I’ve attached a small sample MVC application that has a payment page. The payment page consists of a form to make new payment. Below the form all the made payments are displayed including the payer name, amount and transaction date-time. Change the timezone of your system and refresh the page you will see the transaction date-times are changed accordingly.
Summary
In this artcile we have seen how to display the date-times in client timezone for that we have stored all the date-times in UTC timezone in server and used cookie and javascript to get the timezone offset of the client and thereby adjusting the date-times to client timezone. As I already said we didn’t consider the daylight savings time of the client but I hope the strategy we applied here can be improved to take that into account and show the correct times of events to the client.