Getting NodaTime.Serialization.JsonNet to work with a custom date format

I'm trying to deserialize a JSON, which contains dates in the format 2017-10-26 13:32:11 Etc/GMT. NodaTime seems to support this when combined with Json.NET and NodaTime.Serialization.JsonNet. I found some info here on StackOverflow that made me get this working, using only NodaTime:

var date = "2017-10-26 13:32:11 Etc/GMT";

var pattern = ZonedDateTimePattern.CreateWithInvariantCulture(
    "yyyy'-'MM'-'dd HH':'mm':'ss z", 
    DateTimeZoneProviders.Tzdb
);

var result = pattern.Parse(date);

However when extending this example into deserializing a JSON using the other two packages, I can't get it to work. From reading documentation and other resources, I believe this is supposed to work:

public class DateObj
{
    public ZonedDateTime Date { get; set; }
}

void Main()
{
    var date = "2017-10-26 13:32:11 Etc/GMT";
    var json = $"{{\"Date\": \"{date}\"}}";

    var pattern = ZonedDateTimePattern.CreateWithInvariantCulture(
        "yyyy-MM-dd HH:mm:ss z",
        DateTimeZoneProviders.Serialization
    );

    var settings = new JsonSerializerSettings();
    settings.ConfigureForNodaTime(pattern.ZoneProvider);
    var dateObj = JsonConvert.DeserializeObject<DateObj>(json, settings);
}

But it throws an exception on the last line saying The value string does not match a quoted string in the pattern. Value being parsed: '2017-10-26^ 13:32:11 Etc/GMT'. (^ indicates error position.). What am I doing wrong?

Jon Skeet
people
quotationmark

Currently, you're creating a specific ZonedDateTime pattern - but you're not actually telling Json.NET about that pattern anywhere.

If this is the only Noda Time type that you need to use, I'd advise that you don't call ConfigureForNodaTime at all - because that will configure a ZonedDateTime converter with the wrong pattern. We have code internal to NodaTime.Serialization.JsonNet to replace a converter, but that's not exposed at the moment. (Maybe we should do that...)

It's simple to just create a single converter though, and configure the settings for that:

var settings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
    Converters = { new NodaPatternConverter<ZonedDateTime>(pattern) }
};

Here's what that looks like in the context of a full program. Note that I'm explicitly using DateTimeZoneProviders.Tzdb here, rather than DateTimeZoneProviders.Serialization, given that you definitely have TZDB identifiers:

using Newtonsoft.Json;
using NodaTime;
using NodaTime.Serialization.JsonNet;
using NodaTime.Text;
using System;

public class DateObj
{
    public ZonedDateTime Date { get; set; }
}

class Program
{
    static void Main()
    {
        var date = "2017-10-26 13:32:11 Etc/GMT";
        var json = $"{{\"Date\": \"{date}\"}}";

        var pattern = ZonedDateTimePattern.CreateWithInvariantCulture(
            "yyyy-MM-dd HH:mm:ss z",
            DateTimeZoneProviders.Tzdb
        );

        var settings = new JsonSerializerSettings
        {
            DateParseHandling = DateParseHandling.None,
            Converters = { new NodaPatternConverter<ZonedDateTime>(pattern) }
        };
        var dateObj = JsonConvert.DeserializeObject<DateObj>(json, settings);
        Console.WriteLine(dateObj.Date);
    }
}

people

See more on this question at Stackoverflow