Search code examples
c#jsonserializer

JsonSerializer: discriminate the Kind of DateTime objects


I have a dynamic object that contains dates (among other data).

Some of these dates have Kind UTC and others have Kind Local, eg:

var dynamicObject = new 
{
     utcDate = DateTime.UtcNow,    //This one has Kind = DateTimeKind.Utc
     localDate = DateTime.Now      //This one has Kind = DateTimeKind.Local
}

And then I have a JsonSerializer that works as follows:

var isoDateTimeConverter = new IsoDateTimeConverter();
isoDateTimeConverter.DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'";
                                            //                       ^
                                            //                  Notice this

var serializerSettings = new JsonSerializerSettings();
SerializerSettings.Converters.Add(isoDateTimeConverter);

var response = context.HttpContext.Response;
var writer = new JsonTextWriter(response.Output) { Formatting = Formatting };

var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, dynamicObject);

writer.Flush();

Which creates a JSON string as this:

{
     "utcDate":"2019-05-02T19:52:20Z",
     "localDate":"2019-05-02T15:52:20Z"
}

This is expected because of how my isoDateTimeConverter is currently defined.

But I'd like to serialize as follows:

{
     "utcDate":"2019-05-02T19:52:20Z",
     "localDate":"2019-05-02T15:52:20"    // <--- no Z
}

Meaning that I'd like to add a 'Z' only when the Kind of the DateTime is Utc.

Is this possible with IsoDateTimeConverter and/or JsonSerializerSettings ?


Solution

  • I would either use IsoDateTimeConverter without setting the DateTimeFormat, which outputs the full DateTime value, with fractions of seconds, and a "Z" if the DateTimeKind is UTC or a time zone offset in "+-HH:mm" format if Local (and the empty string if None).

    Otherwise, the K specifier will format the value as you desire, appending the same values for the offset from UTC.

    using System;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;
    
    public class Program
    {
        public static void Main(string[] args)
        {
            var dynamicObject = new
            {
                utcDate = DateTime.UtcNow, //This one has Kind = DateTimeKind.Utc
                localDate = DateTime.Now //This one has Kind = DateTimeKind.Local
            }
    
            ;
            var isoDateTimeConverter = new IsoDateTimeConverter();
            isoDateTimeConverter.DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK";
            var serializerSettings = new JsonSerializerSettings();
            serializerSettings.Converters.Add(isoDateTimeConverter);
            var s = new System.Text.StringBuilder();
            using (var w = new System.IO.StringWriter(s))
            using (var writer = new JsonTextWriter(w)
                       {Formatting = Formatting.Indented})
            {
                var serializer = JsonSerializer.Create(serializerSettings);
                serializer.Serialize(writer, dynamicObject);
                writer.Flush();
            }
    
            Console.WriteLine(s.ToString());
        }
    }
    

    dotnetfiddle.

    The ISO 8601 format allows for many variations of dates and times, to arbitrary precision. From Wikipedia's article on ISO 8601:

    There is no limit on the number of decimal places for the decimal fraction. However, the number of decimal places needs to be agreed to by the communicating parties.