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

3

19

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.

Gym answered 30/10, 2013 at 21:51 Comment(0)
F
21

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);
};
Finer answered 31/10, 2013 at 13:53 Comment(4)
Any ideas on why the JsConfig<DateTime>.SerializeFn delegate is not being called on my setup?Transmission
Are you calling serialize with a nullable DateTime?Finer
For those like me who did not found the ygormutti solution immediately... all you just have to do is : JsConfig<DateTime?>.SerializeFnTuggle
@Finer Is there a way to use an array of DateTime formats which changes JsConfig<DateTime> for each request only? I have broken my system due to this overriding global defaults. JsConfig.With is not good enough as you can only have one DateTimeFormatGrigson
M
7

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;
Malignancy answered 31/10, 2013 at 1:32 Comment(3)
Plugged in your suggestion and it was very close to working for me. First call example: "2013-07-19T12:49:52.5570000", second call example: "2013-07-19T19:49:52.5570000Z". Technically both should be parseable by any language and I assume come out the same, but I have a strong desire to make them match perfectly.Gym
What if I want them to be assumed as DateTimeKind.Local? The DateTime serializer is returning the local time as if it was UTC. JsConfig.AssumeUtc = false; didn't work.Transmission
This works for JSON - is there an equivalent for JSV?Musaceous
C
6

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.

Couteau answered 4/6, 2015 at 16:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.