Lately I’m working together with Daniela on a news feed reader for WP7 and one of its features is Google Reader synchronization. (It is called FeedTso, and if you are interested in knowing when it will be released I recommend you go and subscribe to the newsletter on the FeedTso site.)

I’ll talk about the Google Reader API in a future post (I’ll opensource the API when FeedTso is on the MarketPlace) but for the moment I want to discuss how I deserialize the JSON Date that I receive from the Google Reader API (and from what I’ve been told, Facebook API behaves the same) using Json.NET.

Json.NET has two converters to DateTime:

But unfortunately the Google Reader and Facebook APIs, instead of returning the date in one of the two aforementioned formats, they just return the number representing the unix time: 1290774032435185.

The easy solution would have been to just deserialize to an integer and then expose another property that converted the unix time timestamp to a DateTime, but I wanted something cleaner, so I decided to create a UnixDateTimeConverter for Json.NET.

I looked at the code of the original JavaScriptDateTimeConverter and I basically created mine removing all the parts that were parsing the “new Data(“ part of the value.

public class UnixDateTimeConverter: DateTimeConverterBase
{
    public override object ReadJson(JsonReader reader, Type objectType,
                         object existingValue, JsonSerializer serializer)
    {
        Type t = ReflectionUtils.IsNullableType(objectType)
                  ? Nullable.GetUnderlyingType(objectType) : objectType;
        if (reader.TokenType == JsonToken.Null)
        {
            if (!ReflectionUtils.IsNullableType(objectType))
            {
                throw new Exception(String.Format("Cannot convert null value to {0}.",
                                   CultureInfo.InvariantCulture, new object[] { objectType }));
            }
            return null;
        }
        if (reader.TokenType != JsonToken.Integer)
        {
            throw new Exception(String.Format("Unexpected token parsing date. Expected Integer, got {0}.",
                                         CultureInfo.InvariantCulture, new object[] { reader.TokenType }));
        }
        long ticks = (long)reader.Value;
        DateTime d = ticks.ConvertFromUnixTimestamp();
        if (t == typeof(DateTimeOffset))
        {
            return new DateTimeOffset(d);
        }
        return d;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        long ticks = 0;
        if (value is DateTime)
        {
            ticks = ((DateTime)value).ToUniversalTime().ConvertToUnixTimestamp();
        }
        else
        {
            if (!(value is DateTimeOffset))
            {
                throw new Exception("Expected date object value.");
            }
            DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
            ticks = dateTimeOffset.ToUniversalTime().UtcDateTime.ConvertToUnixTimestamp();
        }
        writer.WriteValue(ticks);
    }
}

I also had to implement a few utility functions because the original converter uses some private methods: a small helper to check whether a type is Nullable and an updated version of my UnixTime to DateTime conversion methods (now implemented as extension methods):

static class DateTimeUtils
{
    public static DateTime ConvertFromUnixTimestamp(this long timestamp)
    {
        DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
        return origin.AddSeconds(timestamp);
    }

    public static long ConvertToUnixTimestamp(this DateTime date)
    {
        DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
        TimeSpan diff = date - origin;
        return (long)Math.Floor(diff.TotalSeconds);
    }
}

This the first time I extended Json.NET and I’ve to say I’m impressed: the parsing is extremely well thought, no wonder why Json.NET is faster than the official DataContractJsonSerializer serializer.

In case you needed something similar, I packaged the 3 classes in zip file to make it easier to download.