如何将 "uuuu'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFFFo<Z+HHmm>" 解析为 Instant?

How to parse "uuuu'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFFFo<Z+HHmm>" into an Instant?

我有这个class:

public class Test
{
    public Instant I { get; set;}
}

I 是一个 Instant 因为它在语义上是有意义的。

但是,我必须从这里反序列化它:

{
    "i": "2018-10-25T18:34:11.911+00:00"
}

从这里:

{
    "i": "2018-10-25T18:34:11.911+0000"
}

我该怎么做?

我已经能够使用特殊模式解决它:

public class CustomInstantPattern : IPattern<Instant>
{
    public StringBuilder AppendFormat(Instant value, StringBuilder builder)
    {
        return builder.AppendFormat("s", value.ToDateTimeOffset());
    }

    public string Format(Instant value)
    {
        return value.ToDateTimeOffset().ToString("s");
    }

    public ParseResult<Instant> Parse(string text)
    {
        try
        {
            return ParseResult<Instant>.ForValue(DateTimeOffset.Parse(text, CultureInfo.InvariantCulture).ToInstant());
        }
        catch (Exception ex)
        {
            return ParseResult<Instant>.ForException(() => throw ex);
        }
    }
}

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

var converted = JsonConvert.DeserializeObject<Test>("{ 'I': '2018-10-25T18:34:11.911+0000'}", settings);

这是正确的方法吗?

使用 CustomInstantPattern 的方法很好,但我会完全避免使用任何 .NET date/time 类型。

相反,我会使用两个 OffsetDateTimePattern 实例,一个有冒号,一个没有,并将它们与 CompositePattern 结合起来。然后,您可以委托给它,在需要时转换 to/from Instant

完整的示例代码如下:

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

class CustomInstantPattern : IPattern<Instant>
{
    private readonly IPattern<OffsetDateTime> offsetDateTimePattern;

    public CustomInstantPattern()
    {
        // Pattern explanation:
        // - o<G> means "use the G Offset pattern" (to hour, minute or second, with colons, format +00 as Z)
        // - o<I> means "use the I Offset pattern" (to hour, minute or second, without colons, format +00 as Z)
        var patternWithColon = OffsetDateTimePattern.CreateWithInvariantCulture("uuuu'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFFFo<G>");
        var patternWithoutColon = OffsetDateTimePattern.CreateWithInvariantCulture("uuuu'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFFFo<I>");
        offsetDateTimePattern = new CompositePatternBuilder<OffsetDateTime>()
        {
            // The predicates here are for formatting. As the first always
            // returns true, it doesn't really matter what the second does.
            // The intention is that some values might not be formattable with
            // all patterns, but that doesn't apply here.
            { patternWithColon, _ => true },
            { patternWithoutColon, _ => true }
        }.Build();
    }

    public StringBuilder AppendFormat(Instant value, StringBuilder builder) =>
        offsetDateTimePattern.AppendFormat(value.WithOffset(Offset.Zero), builder);

    public string Format(Instant value) =>
        offsetDateTimePattern.Format(value.WithOffset(Offset.Zero));

    public ParseResult<Instant> Parse(string text) =>
        offsetDateTimePattern.Parse(text).Convert(odt => odt.ToInstant());
}

class Entity
{
    public Instant I { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var settings = new JsonSerializerSettings
        {
            DateParseHandling = DateParseHandling.None,
            Converters = { new NodaPatternConverter<Instant>(new CustomInstantPattern()) }
        };
        string json = " { \"i\": \"2018-10-25T18:34:11.911+0000\" }";
        Entity entity = JsonConvert.DeserializeObject<Entity>(json, settings);
        Console.WriteLine(entity.I);
        // Check it works with colons too
        json = " { \"i\": \"2018-10-25T18:34:11.911+00:00\" }";
        entity = JsonConvert.DeserializeObject<Entity>(json, settings);
        Console.WriteLine(entity.I);
    }
}

(Noda Time 有两个问题——一个是嵌入式部分的文档对于 OffsetDateTime 模式不正确,一个是我们没有 [=24] 的标准模式=] 这很烦人。我会尽力解决这两个问题。)