NodaTime UnparsableValueException 由于在模式中使用 "Z"

NodaTime UnparsableValueException due to usage of "Z" in pattern

我正在 Java 和 C# 之间交换 JSON 消息(反之亦然)。

在 Java 中,我使用 java.time.Instant (JSR-310) 表示全局时间轴上的一个时间点。为了在 JSON 中创建一个人类可读的 date/time 字符串,我按如下方式转换我的 Instant:

private static final DateTimeFormatter FORMATTER = ofPattern("yyyy-MM-dd'T'HH:mm:ssZ").withZone(ZoneId.systemDefault());

生成以下输出:

2017-04-28T19:54:44-0500

现在,在消息消费者方面 (C#),我编写了一个自定义 Newtonsoft.Json.JsonConverter,它扩展了包含以下重写的 ReadJson() 方法的抽象 JsonCreationConvert class:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        if (reader.TokenType == JsonToken.StartArray)
        {
            return JToken.Load(reader).ToObject<string[]>();
        }

        reader.DateParseHandling = DateParseHandling.None; // read NodaTime string Instant as is
        serializer.Converters.Add(NodaConverters.InstantConverter);

        // Load JObject from stream
        var jObject = JObject.Load(reader);

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        // Populate the object properties
        var writer = new StringWriter();
        serializer.Serialize(writer, jObject);
        using (var newReader = new JsonTextReader(new StringReader(writer.ToString())))
        {
            newReader.Culture = reader.Culture;
            newReader.DateParseHandling = reader.DateParseHandling;
            newReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
            newReader.FloatParseHandling = reader.FloatParseHandling;
            serializer.Populate(newReader, target);
        }

        return target;
    }

Create() 是一个抽象方法。

当我现在通过调用将此 JSON 字符串转换为 NodaTime.Instant (v2.0.0) 时:

InstantPattern.General.Parse(creationTime).Value;

我得到这个异常:

NodaTime.Text.UnparsableValueException: The value string does not match a quoted string in the pattern. Value being parsed: '2017-04-28T19:54:44^-0500'. (^ indicates error position.)

如果我传递文本文字 "Z"(因此没有输出偏移量“-0500”并且 Z 被解释为 0 偏移量)NodaTime.Serialization.JsonNet.NodaConverters.InstantConverter 正确读取而不会引发异常。

查看 GeneralPatternImpl 我看到:

internal static readonly InstantPattern GeneralPatternImpl = InstantPattern.CreateWithInvariantCulture("uuuu-MM-ddTHH:mm:ss'Z'");

为什么 InstantConverter 要求偏移量是文本文字?发生这种情况是因为 Instant 与偏移量无关吗?如果是这种情况,那么 InstantConverter 为什么不忽略偏移量而不是抛出异常?我需要编写自定义转换器来解决这个问题吗?

这就像要求将 2017-04-28T19:54:44 解析为 LocalDate - 我们会悄悄删除额外的信息。从根本上说,您在 Java 中从 InstantString 的转换是 "adding" 信息,它在原始瞬间并不真正存在。您最终得到的实际上是 OffsetDateTime,而不是 Instant - 它比 Instant 包含更多信息。

您应该决定您真正关心的信息是什么。如果您只关心时间的瞬间,那么将您的 Java 序列化更改为使用 UTC,它应该以序列化形式以 Z 结束,一切都会好起来的。这就是我建议你做的 - 传播不相关的信息是误导,IMO。

如果您真的关心系统默认时区的偏移量,您对 .withZone(ZoneId.systemDefault()) 的调用暗示了 您这样做,那么您应该将其解析为 OffsetDateTime 在 .NET 方面。如果需要,您可以随后将其转换为 Instant(只需调用 ToInstant())。