World Time Engine

In my last post I discussed about the procedure to show local date time in our application UI. Toady I am going to tell you the concept which you can think just an extension of my previous post. I told that in the case of localization we should store our date time in UTC format and at the time of showing we can convert in local time. I showed some sample code in C# and JavaScript to convert local date time to UTC and vice versa. But those code depending on the system time zone on which the code is executing. If we change our system time zone to a different time zone which is not right, then the conversion will also showing the wrong date time. For example suppose in a web application the user who is using our application from browser change her time zone to a wrong time zone then our JavaScript code will actually calculate the wrong date time. If this is not ok with our application we are currently developing then what is the solution?

The solution is we have to track the original time zone of the user who is using our application. For that we can use a third party service. There are many actually, I am using here ‘World Time Engine’ which is a paid service.

There are two procedures to access the service. One is through IP address and another is through Latitude and Longitude of the user who is using our application. When we create a membership in the service then they will generate a unique key for us and send it to our mail. We have to send this unique key along with the IP address.

http://worldtimeengine.com/api/ip/[unique key]/[IP address]

If all are going well and right then the service will send a XML as a response to us.

<?xml version="1.0" encoding="UTF-8" ?>
<timezone xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:noNamespaceSchemaLocation="http://worldtimeengine.com/timezone.xsd">
    <version>1.1</version>
    <location>
        <region>United Kingdom</region>
        <latitude>51.5</latitude>
        <longitude>-0.1167</longitude>
    </location>
    <time>
        <utc>2008-07-11 13:30:38</utc>
        <local>2008-07-11 14:30:38</local>
        <zone>
            <hasDST>true</hasDST>
            <current>
                <abbreviation>BST</abbreviation>
                <description>British Summer Time</description>
                <utcoffset>+1:00</utcoffset>
                <isdst>true</isdst>
                <effectiveUntil>2008-10-26 00:00:00</effectiveUntil>
            </current>
            <next>
                <abbreviation>GMT</abbreviation>
                <description>Greenwich Mean Time</description>
                <utcoffset>+0:00</utcoffset>
                <isdst>false</isdst>
                <effectiveUntil>2009-03-29 01:00:00</effectiveUntil>
            </next>
        </zone>
    </time>
</timezone>

In the case of any error the service will send a XML response with the error code and message.

<?xml version="1.0" encoding="UTF-8" ?>
<error xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://worldtimeengine.com/error.xsd" type="exception">
    <code>10001</code>
    <message>Invalid or Expired API Key used</message>
</error>

You can write some code to calculate the local time from UTC or vice versa. The folling sample is to convert UTC date time to local date time using the third party service.

using System;
using System.IO;
using System.Net;
using System.Xml;
using UsingWorldTimeEngine.Codes;

namespace UsingWorldTimeEngine
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // Use the 3rd party service with a demo key.
            string requestUrl = string.Format("http://worldtimeengine.com/api/ip/{0}/{1}", 
                "9d89405104e16732fa00caa1e8e6cdfd", Request.UserHostAddress);             
            
            try
            {
                string strResponse = GetResponse(requestUrl); 
                string utcOffset = GetUtcOffset(strResponse);
                DateTime utcDateTime = DateTime.UtcNow; // UTC datetime may be comming from persistent layer.
                DateTime localDate = GetLocalDateTime(utcDateTime, utcOffset); 

                lblOutput.Text = localDate.ToString();
            }
            catch (WorldTimeEngineException ex)
            {
                // Use the custom exception object.
                lblOutput.Text = string.Format("Code: {0}<br />Message: {1}", 
                    ex.Code, ex.Message);
            }
            catch (ArgumentException ex)
            {
                lblOutput.Text = ex.Message;
            }
            catch (Exception ex)
            {
                lblOutput.Text = ex.Message;
            }
        }

        private string GetResponse(string requestUrl)
        {
            WebRequest request = HttpWebRequest.Create(requestUrl);
            WebResponse response = request.GetResponse();
            StreamReader sr = new StreamReader(response.GetResponseStream());

            return sr.ReadToEnd();
        }

        private string GetUtcOffset(string strResponse)
        {
            XmlDocument xdocResponse = new XmlDocument();
            xdocResponse.LoadXml(strResponse);

            if (xdocResponse.GetElementsByTagName("timezone").Count != 0) // Success.
            {
                // Get the first 'utcoffset' element value which is inside the 'current' tag 
                // not under the 'next' tag.
                string utcOffset = xdocResponse.GetElementsByTagName("utcoffset")[0].InnerText;
                return utcOffset;
            }
            else if (xdocResponse.GetElementsByTagName("error").Count != 0) // Error.
            {
                // Throw the custom exception object.
                throw new WorldTimeEngineException(
                    int.Parse(xdocResponse.GetElementsByTagName("code")[0].InnerText), 
                    xdocResponse.GetElementsByTagName("message")[0].InnerText); 
            }
            else
            {
                throw new ArgumentException();
            }
        }

        private TimeSpan GetTimeSpan(string utcOffset)
        {
            // Calculate hour, minute from UTC offset, for example +5:30.
            string strTimeSpan = utcOffset.Substring(1);     
            string[] arrayTimeSpan = strTimeSpan.Split(':'); 
            int hour = int.Parse(arrayTimeSpan[0]);          
            int minute = int.Parse(arrayTimeSpan[1]);        

            TimeSpan ts = new TimeSpan(hour, minute, 0);

            return ts;
        }

        private DateTime GetLocalDateTime(DateTime utcDateTime, string utcOffset)
        {
            char sign = utcOffset.ToCharArray()[0];
            TimeSpan ts = GetTimeSpan(utcOffset);

            DateTime localDate;

            switch (sign)
            {
                case '+': // Before UTC time.
                    localDate = utcDateTime.Add(ts); 
                    break;

                case '-': // After UTC time.
                    localDate = utcDateTime.Add(-ts);
                    break;

                default:
                    throw new InvalidOperationException();
            }

            return localDate;
        }   
    }
}

Along with this calculation I am also doing some error handling and for that I am creating a custom exception class.

using System;

namespace UsingWorldTimeEngine.Codes
{
    public class WorldTimeEngineException : ApplicationException
    {
        public int Code { get; set; }

        public WorldTimeEngineException()
        {
            Code = 0;
        }

        public WorldTimeEngineException(int code, string message)
            : base(message)
        {
            Code = code;
        }
    }
}

You can take a different service but the basic concept will be same. Please let me know if you are using any other service to calculate date time and if you have any better approach.

Happy coding.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s