【问题标题】:Parse String as UTC DateTimeOffset When No TZ Specified未指定 TZ 时将字符串解析为 UTC DateTimeOffset
【发布时间】:2018-12-16 18:43:42
【问题描述】:

我正在开发一个简单的 Web API 客户端。我的模型中有一个ExpiresOn 属性,我已将其指定为DateTimeOffset

public class Model {
    public DateTimeOffset ExpiresOn { get; set; }
}

我使用 JsonConvert.DeserializeObject<T>() 从 JSON 字符串反序列化 API 响应。我注意到,由于我检索的 JSON 没有指定任何时区信息,因此 JSON.Net 将假定它是本地时间,并根据我的设置返回一个调整为 UTC 的DateTimeOffset。我尝试了不同的组合,但我似乎无法“强制”JsonConvert 像读取 UTC 一样读取字符串。

最好有一个使用JsonConvert.DefaultSettings 全局设置该设置的选项,因为我在多个位置进行了类似的解析。

另外,我正在为 ASP.Net Core 2.1 编码,并且我的主机的 tz 设置为 CEST (+02:00)。在主机上设置不同的时区不是一种选择。

附录:

要解析的string 示例是"2018-07-09T11:22:33.45678"。我尝试了DateParseHandlingDateTimeZoneHandling 的排列,但都返回一个日期:2018-07-09T11:22:33.45768+02:00

显然,反序列化器假定字符串是本地时间的。因此,有没有办法让它像AssumeUniversal 那样对待DateTimeOffset.TryParse

【问题讨论】:

  • Can you tell JSON.Net to serialize DateTime as Utc even if unspecified? 的可能重复项。这当然只有在你所有的日期都是 UTC 时才有效。
  • 序列化不是问题。它正在反序列化。
  • 那么,您是否尝试过这些反序列化设置?它们也适用于反序列化。此外,提供一些您想要反序列化的日期字符串也会有所帮助。
  • 我编辑了问题以反映我的问题。
  • 有趣的是,它确实对 DateTimeOffset 没有任何作用(请参阅我的回答)。不管你想解决这个问题,你可能不会使用JsonConverter

标签: c# json.net datetimeoffset


【解决方案1】:

似乎DateTimeZoneHandling 不适用于DateTimeOffset

作为一种解决方法,您必须定义一个 JsonConverter 来规避此问题:

public class DateTimeOffsetConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTimeOffset);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null) return null;
        DateTime dateTime = (DateTime)reader.Value;
        return new DateTimeOffset(dateTime);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(value);
    }
}

然后像这样使用它:

public class Model
{
    [JsonConverter(typeof(DateTimeOffsetConverter))]
    public DateTimeOffset ExpiresOn { get; set; }
}

然后这样称呼它:

var str = "{\"ExpiresOn\":\"2018-07-09T11:22:33.45678\"}";
Model test = JsonConvert.DeserializeObject<Model>(str, new JsonSerializerSettings()
{
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,
    DateParseHandling = DateParseHandling.DateTime
});
Assert(test.ExpiresOn.Offset.Ticks == 0);

您还可以在JsonConvert.DefaultSettings 中定义JsonSerializerSettings,而不是每次转换调用。

【讨论】:

  • 非常感谢! #abolish-timezones :)
  • 顺便说一句,将这个 JsonConverter 放在 Converts 中,为所有 DateTimeOffset 对象工作的 DefaultSettings 回调工作?
【解决方案2】:

根据文档,您可以通过JsonSerializerSettings 传递DateTimeZoneHandling

Here 就是这个例子,虽然这里的例子是用于序列化的,但是你可以通过相同的设置来反序列化也看看this

示例:-

string jsonWithUtcTimeZone = JsonConvert.SerializeObject(flight, Formatting.Indented, new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.Utc
});

反序列化示例:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    DateTimeZoneHandling = DateTimeZoneHandling.Utc,
};

var json = JsonConvert.SerializeObject(a, settings);

var newA = JsonConvert.DeserializeObject<A>(json, settings);

【讨论】:

    猜你喜欢
    • 2020-11-19
    • 2020-09-27
    • 2014-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 2014-11-25
    相关资源
    最近更新 更多