Monday, June 3, 2013

Web Browser timezone specific date display in asp.net

It may sound like a simple requirement to display date and time localized to the logged in user but it gets really tricky if you have to implement it on an age old asp.net application.Applications built  with SQL Server 2005 and below with no time zone aware datatypes and data are mostly stored at the servers time zone (no UTC based storage adopted aswell). 

The problem statement was simple; though the server is situated in VICTORIA(AEST – Australian Eastern Standard Time) a user logging in from Perth (AWST – Australian Western Standard Time) should see the time-zone according to their browser time-zone. The server is located in Victoria and users around the world should be able to see the date-time according their web browser's time-zone. 

Lets see the problem with an example;
User from Sydney teleconference task scheduled at 10 am AEST(winter time). A user from Perth should see this as 8 am AWST but our good old system database will have the data stored as 10 am as server is in AEST time-zone.

There are two aspects to this problem, 
1. Getting the time zone information from the browser
2. Implement the time zone specific date and time displayed through out the system

This can be easily implemented by following the four steps
Step 1: Define a JavaScript function to create cookie to store browser time zone [TimezoneFinder.js file]


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)) {          
            $.cookie(test_cookie, null);  // delete the test cookie           
            $.cookie(timezone_cookie, new Date().getTimezoneOffset()); // create a new cookie
            location.reload();// re-load the page
        }
    }
    else {// if the current timezone and the one stored in cookie are different
          // then store the new timezone in the cookie and refresh the page.
        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();
        }
    }

}

Step 2: Include script file and call the "setTimezoneCookie" function in the Master page
<script src="[path]/TimezoneFinder.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
  setTimezoneCookie();
});

</script>

Step 3: Lets assume all the aspx codebehind files are inheriting from a common BasePage to allow pushing common UI specific logic. Now lets read the timezone cookie value and store it in a session variable.
protected override void OnPreLoad(EventArgs e)
{
  base.OnPreLoad(e);
 //Save the timezone information from cookie to a session variable
 Session["timezoneoffset"] = this.Request.Cookies.AllKeys.Contains("timezoneoffset",
s => s.ToLower())
this.Request.Cookies["timezoneoffset"].Value : null;

}

Step 4: Final step will be to create a bunch of utility methods to help with the conversion. Lets create some extension method to hide the detail and give a clean syntax for the calling code.
[TimezoneConvertExtensions class]
public static class TimezoneConvertExtensions
{
    private static int? _serverTimezoneOffset = null;
    private static int ServerTimezoneOffset
    {
        get
        {
            return _serverTimezoneOffset.HasValue
                        ? _serverTimezoneOffset.Value
                        : (_serverTimezoneOffset = GetServerTimezoneOffset()).Value;
        }
    }
    private static int GetServerTimezoneOffset()
    {
        var timeZone = TimeZone.CurrentTimeZone;
        var offset = timeZone.GetUtcOffset(DateTime.Now);

        return offset.Hours * 60 + offset.Minutes;
    }

    public static DateTime ToClientTime(this DateTime dt)
    {
        if (dt == DateTime.MinValue) return dt; 
// read the value from session         
        var timeOffSet = HttpContext.Current.Session["timezoneoffset"];
        if (timeOffSet != null)
        {
            var offset = int.Parse(timeOffSet.ToString()) + ServerTimezoneOffset;
            dt = dt.AddMinutes(-1 * offset);
            return dt;
        }// if there is no offset in session return the datetime in server timezone           
        return dt.ToLocalTime();
    }

    public static DateTime ToServerTime(this DateTime dt)
    {
        if (dt == DateTime.MinValue) return dt;
        var timeOffSet = HttpContext.Current.Session["timezoneoffset"];
        if (timeOffSet != null)
        {
            var offset = int.Parse(timeOffSet.ToString()) + ServerTimezoneOffset;
            dt = dt.AddMinutes(1 * offset);
            return dt;
        }
        return dt.ToLocalTime();
    }

}

How to Use in code:
By default all the data will be saved with respect to the server timezone (here AEST ) .In other words the created datetime of a record will be according to db server timezone. 

These set of records can be easily localised and displayed in the browsers timezone  using the following code
someDate.ToClientTime() 
e.g. test using DateTime.Now.ToClientTime()

On the other hand If the system wants to save the user entered date and time then received datetime has to be explicitly converted to server timezone for saving. The following call will convert the data to server timezone before saving. (for example a conference appointment time saved from AWST timezone has to be saved in AEST timezone in the server so that it makes sense for a browser request from AWST at later point in time)
 e.g. userEnteredDate.ToServerTime()



1 comment:

  1. There are two ways to scrape data from website, first, with the help of automated software, and second, by hiring web scarping services.
    scraper bot
    data extraction services
    web crawling services
    web scraping services

    ReplyDelete