【问题标题】:json.net serialization/deserialization of datetime 'unspecified'json.net 日期时间“未指定”的序列化/反序列化
【发布时间】:2015-01-26 10:28:27
【问题描述】:

在常规 .net 中, 如果我们有一个具有 DateTimeKind.Unspecified 的时间 如果我们转换 ToLocal - 它假定转换时输入日期是 UTC。 如果我们转换 ToUniversal - 它假定转换时输入日期是本地日期

但是,在 JSON.Net 中,如果我们在 JSON.Net 中的字符串日期是未指定的,它似乎没有这个逻辑?看看我下面的测试用例——我做错了什么吗?或者这是设计使然?还是 JSON.Net 中的错误? 谢谢!

    // TODO: This Fails with output
    //      date string: "2014-06-02T21:00:00.0000000"
    //      date serialized: 2014-06-02T21:00:00.0000000Z
    //      Expected date and time to be <2014-06-03 04:00:00>, but found <2014-06-02 21:00:00>.
    [TestMethod]
    public void NEW_Should_deserialize_unspecified_datestring_to_utc_date()
    {
        string dateString = "\"2014-06-02T21:00:00.0000000\"";
        DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified);
        DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc);
        dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime());

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
        settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
        DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);                

        Console.WriteLine("date string: " + dateString);
        Console.WriteLine("date serialized: " + dateSerialized.ToString("o"));

        dateSerialized.Kind.Should().Be(DateTimeKind.Utc); 
        dateSerialized.Should().Be(dateRaw.ToUniversalTime());
        dateSerialized.Should().Be(dateRawAsUtc);
    }

    // TODO: This Fails with output
    //      date string: "2014-06-02T21:00:00.0000000"
    //      date serialized: 2014-06-02T21:00:00.0000000-07:00
    //      Expected date and time to be <2014-06-02 14:00:00>, but found <2014-06-02 21:00:00>.
    [TestMethod]
    public void NEW_Should_deserialize_unspecified_datestring_to_local_date()
    {
        string dateString = "\"2014-06-02T21:00:00.0000000\"";
        DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified);
        DateTime dateRawAsLocal = new DateTime(2014, 6, 2, 14, 0, 0, 0, DateTimeKind.Local);
        dateRawAsLocal.Should().Be(dateRaw.ToLocalTime());

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
        settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
        DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);

        Console.WriteLine("date string: " + dateString);
        Console.WriteLine("date serialized: " + dateSerialized.ToString("o"));

        dateSerialized.Kind.Should().Be(DateTimeKind.Local);
        dateSerialized.Should().Be(dateRaw.ToLocalTime());
        dateSerialized.Should().Be(dateRawAsLocal);
    }

    [TestMethod]
    public void NEW_Should_deserialize_unspecified_datestring_to_unspecified_date() 
    {
        string dateString = "\"2014-06-02T21:00:00.0000000\""; // unspecified, does not have the 'Z'
        DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified);

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified;
        settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
        DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);                

        Console.WriteLine("date string: " + dateString);
        Console.WriteLine("date serialized: " + dateSerialized.ToString("o"));
        dateSerialized.Kind.Should().Be(DateTimeKind.Unspecified); 
        dateSerialized.Should().Be(dateRaw);
    }

【问题讨论】:

  • 尝试使用DateTimeOffset 而不是DateTime。它应该正确反序列化。

标签: c# datetime serialization timezone json.net


【解决方案1】:

我不是 100% 确定您在这里寻找什么,但我认为假设 JSON.Net 无需一点帮助就能满足您的所有需求是不安全的。作为Mr. Newton says

JSON 中的日期很难。

首先要确定是否要支持接受未指定的日期,或者是否要假设所有传入的日期都是通用的,即使它们缺少尾随 Z。

如果你假设所有传入的日期都是通用的,你可以看看它们是否有一个尾随 Z,如果没有,添加它(不完全是生产代码,但你明白了):

if (!dateString.EndsWith("Z\"", StringComparison.InvariantCultureIgnoreCase))
{
    dateString = dateString.Substring(0, dateString.LastIndexOf("\"", StringComparison.InvariantCultureIgnoreCase)) + "Z\"";
}

此假设更改确实需要将您要测试的日期修改为 Utc。

如果您不想假设传入的日期是通用的,而是将它们视为未指定,则需要通过替换来更改转换传入 JSON 的方式:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings);                

与:

var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter();
DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter);

这将产生一个与 dateString 完全匹配的未指定日期。这里是你的帮助之手发挥作用的地方:

if (dateSerialized.Kind == DateTimeKind.Unspecified)
{
    dateSerialized = dateSerialized.ToUniversalTime();
}

这意味着完整的、修改后的第一个测试将如下所示并且它将通过:

    string dateString = "\"2014-06-02T21:00:00.0000000\"";
    DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified);
    DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc);
    dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime());

    var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter();
    DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter);
    if (dateSerialized.Kind == DateTimeKind.Unspecified)
    {
        dateSerialized = dateSerialized.ToUniversalTime();
    }

    Console.WriteLine("date string: " + dateString);
    Console.WriteLine("date serialized: " + dateSerialized.ToString("o"));

    dateSerialized.Kind.Should().Be(DateTimeKind.Utc); 
    dateSerialized.Should().Be(dateRaw.ToUniversalTime());
    dateSerialized.Should().Be(dateRawAsUtc);

【讨论】:

  • UGH!...但绝对感谢您的帖子/信息...听起来这是“设计使然”...现在,关于支持未指定的评论,这确实经常发生 -例如开发人员只是 myDate = new DateTime(2014, 10, 23);我想对这意味着什么或发生什么有可预测的行为,如果 .Net 以一种方式做到这一点,而 JSON.Net 以另一种方式做到这一点,那就……丑陋……顺便说一句,我以前使用过 ServiceStack,他们做的和.Net一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多