Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ServiceStack - Is there a way to force all serialized Dates to use a specific DateTimeKind?

I have a POCO like this:

public class BlogEntry
{
    public string Title { get; set; }
    public DateTime Date { get; set; }
}

Most of the time it's being hydrated from Entity Framework, but it can and will be used outside of Entity Framework.

The DateTimeKind for the Date from EF is Unspecified, which from what I read is normal.

When I cache this POCO in Redis (using the ServiceStack Redis client), it comes back with a DateTimeKind of Local.

So there is a jitter with the returned objects. The first pass (uncached) has ISO-8601 with no offset (DateTimeKind.Unspecified). The second pass (cached) is ISO-8601 with an offset (from Redis with DateTimeKind.Local).

Any way to force the ServiceStack JSON serializer to always interpret dates as a given DateTimeKind? (I know there is a "JsConfig.AppendUtcOffset" property, but whether it's true or false, the values never change for me?)

Or somewhere in the deserialization process from my typed RedisClient to make the DateTimeKind local?

I can manually change my POCO's to enforce the DateTimeKind - and that works - but I was hoping for something less error prone.

like image 360
ryan1234 Avatar asked Oct 30 '13 21:10

ryan1234


3 Answers

If you need something more configurable than @mythz's answer, you can force the serialization or deserialization of DateTimes to have a certain DateTimeKind by overriding the DateTime and optionally DateTime? serialization and/or deserialization methods.

Force all serialized DateTimes to be interpreted as UTC

JsConfig<DateTime>.SerializeFn = time => new DateTime(time.Ticks, DateTimeKind.Local).ToString();

You can then take this one step farther and error on deserialization if the DateTime isn't in a specified format. I started using this when I wanted to force clients to specify the timezone in all requests, but not necessarily require that it always be Utc.

JsConfig<DateTime>.DeSerializeFn = time =>
{
  if (!IsInCorrectDateFormat(time))
    throw new System.Runtime.Serialization.SerializationException(BadDateTime);

  return ServiceStack.Text.Common.DateTimeSerializer.ParseDateTime(time);
};
like image 95
bpruitt-goddard Avatar answered Nov 12 '22 04:11

bpruitt-goddard


The DateTimeKind offset does not get stored with Date's so by default ServiceStack serializers make the assumption that the date is local, which gets serialized as UTC and deserialized back out as Local.

You can get DateTimeKind.Unspecified to be assumed as UTC with:

JsConfig.AssumeUtc = true;
like image 26
mythz Avatar answered Nov 12 '22 05:11

mythz


A tweak to bpruitt-goddard solution. All credit goes to him.

JsConfig<DateTime>.SerializeFn = time => new DateTime(time.Ticks, DateTimeKind.Local).ToString("o");
        JsConfig<DateTime?>.SerializeFn = 
            time => time != null ? new DateTime(time.Value.Ticks, DateTimeKind.Local).ToString("o") : null;
        JsConfig.DateHandler = DateHandler.ISO8601;

So any dates going out from service stack will be forced into a ISO8601 date format and any dates coming in will be converted automatically to C# date from the ISO8601 string.

like image 28
Kamalpreet Avatar answered Nov 12 '22 05:11

Kamalpreet