Friday, February 1, 2008

Serializing DateTime values using JavaScriptSerializer class.

I use this class intensively in my current AJAX-enabled web application. The class serializes/deserializes C# class into JSON, and can deserialize it back.

To see more details about the class you can see my previous post: Passing complex objects between client browser and web server using ASP.NET AJAX framework or official ASP.NET AJAX documentation.

I have the problem with correct deserialization of DateTime types in my custom serializable classes.

To show the problem clearly I created the following test console application:

public class SimpleClass
{
    private DateTime m_Date;

    public DateTime Date
    {
        get { return m_Date; }
        set { m_Date = value; }
    }
}
 class Program
 {
     static void Main(string[] args)
     {
         JavaScriptSerializer serializer = new JavaScriptSerializer();

         DateTime dt = DateTime.Now;
         SimpleClass instance = new SimpleClass();
         instance.Date = dt;

         Console.WriteLine(
             "Before serialization: {0}",
             instance.Date);

         string jsonStr = serializer.Serialize(instance);

         SimpleClass newInstance = serializer.Deserialize<SimpleClass>(jsonStr);

         Console.WriteLine(
             "After serialization: {0}", 
             newInstance.Date);
     }
 }

We should expect to see the same value for instance.Date and newInstance.Date objects, but instead we see that:

image

for initial DateTime value equal to DateTime.MinValue:

image

DateTime.MaxValue:

image

So do you see the problem? Dates are different before and after serialization. This is definitely wrong.

I downloaded source code for ASP.NET AJAX library and created a simple library to debug the issue with comfort. And I found the line creating the wrong DateTime value. This is done during deserialization. The following listing shows the code:

image

The problem exists because of different Kind's of DateTime objects before and after serialization. As you see in last picture, AJAX framework deserializes DateTime value assuming it's Kind property should be set to Utc format. But the original DateTime value was set to Local format. It means that for me (I am in +4:00 time zone) the local time is greater then a universal time by 4 hours, and this makes a big difference. If a user tries to select 2/2/2008 at 1:00 AM - he gets 2/1/2008. This is bad.

Lets explore how the Kind property of a DateTime values are assigned.

By default when you assign a value by using statement:

DateTime dt = DateTime.Now;

The Kind property of a dt variable is set to Local:

image

And if we assign it in the way:

DateTime dt = DateTime.UtcNow;

We get:

image

And finally if we assign the value:

DateTime dt = DateTime.MinValue;

image

You can see more information about DateTime.Kind property on MSDN.

So I started to think (the good thing) about why AJAX library deserializes DateTime's to Utc without persisting the Kind property.

The first idea was this is a bug.

Then I started to think how to persist the Kind property in the class to be able to assign it to DateTime value after deserialization have occurred, but in this case we must implement also a functionality to create a Date object in javascript from custom serialized data ourselves.

Anyway what is the purpose of JSON? JSON gives the ability to pass objects to the client side, thus it will be sent over the network. The size of JSON is also critical, it must be as small as possible.

I changed the class we serialize as follows:

public class SimpleClass
    {
        private DateTime m_Date;

        public DateTime Date
        {
            get { return m_Date; }
            set { m_Date = DateTime.SpecifyKind(value, DateTimeKind.Utc); }
        }
    }

Using this syntax I force the m_Date field to contain only Utc dates. And it works!

image

DateTime.MinValue:

image

DateTime.MaxValue:

image

One thing we should keep in mind - with this change we are forcing Date property of state class to contain Utc date kind, this means we will not be able to know real initial Kind of date. and if you need some manipulation over Date values converting them to Utc or Local in deserialized object, - this might not work for you.

Technorati Tags:

3 comments:

Anonymous said...

hii, i like the article you wrote. i also wrote an article on Serialization here : http://kaniks.blogspot.com
feel free to post your comments

thanks
cheers

Anonymous said...

Thanks for the explanation.. fixed my issue as well..

Anonymous said...

If you convert object datetime after deserializing to ToLocalTime() it will change the timezone info. Here is complete detailed information available: http://conceptf1.blogspot.com/2013/11/c-javascriptserializer-and-time-zone.html