JSON.DateTime值的Net反序列化在某些情况下失败

本文关键字:情况下 失败 反序列化 DateTime 值的 Net JSON | 更新日期: 2023-09-27 18:13:38

使用JSON。在以下5个测试中,第一个和最后一个通过,而其他的失败:

[Test, Sequential]
public void WhyCantIDeserializeThisDateWhen2011Works(
    [Values(1980, 1980, 1980, 1980, 1980, 1980, 1980)] Int32 year,
    [Values(10,   10,   10,   10,   11,   11,   11)] Int32 month,
    [Values(26,   27,   30,   31,   1,    2,    3)] Int32 day)
{
    var obj = new {
        Title = "Will this be able to serialize the DateTime field?",
        Timestamp = new DateTime(year, month, day)
    };
    var type = obj.GetType();
    var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
    dynamic deserialized = Newtonsoft.Json.JsonConvert.DeserializeObject(serialized, type);
    Assert.AreEqual(obj.Title, deserialized.Title);
    Assert.AreEqual(obj.Timestamp, deserialized.Timestamp);
}

下面是一些输出:

[snip]
Test 'Rds.Infrastructure.Serializers.Tests.JsonSerializerTests.WhyCantIDeserializeThisDateWhen2011Works(1980,11,2)' failed:
Expected: 1980-11-02 00:00:00.000
But was: 1980-11-01 23:00:00.000
at CallSite.Target(Closure , CallSite , Type , DateTime , Object )
    UnitTests'Rds.Infrastructure'Serializers'JsonSerializerTests.cs(141,0): at     Rds.Infrastructure.Serializers.Tests.JsonSerializerTests.WhyCantIDeserializeThisDateWhen2011Works(Int32 year, Int32 month, Int32 day)
2 passed, 5 failed, 0 skipped, took 627.55 seconds (NUnit 2.5.5).

这个错误是所有错误中的典型错误——当它重新加载日期时,它不是指定的日期,而是前一天的晚上11点。这特别奇怪,因为如果我把年份改为2011年,所有这些测试都通过了。

我已经深入研究了JSON。JsonTextReader类的ParseDate方法读入值。以2011年10月27日为例,我的评论是:

private void ParseDate(string text)
{
  string value = text.Substring(6, text.Length - 8);
  DateTimeKind kind = DateTimeKind.Utc;
  int index = value.IndexOf('+', 1);
  if (index == -1)
    index = value.IndexOf('-', 1);
  TimeSpan offset = TimeSpan.Zero;
  if (index != -1)
  {
    kind = DateTimeKind.Local;
    offset = ReadOffset(value.Substring(index));
    value = value.Substring(0, index);
  }
  long javaScriptTicks = long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
  // The date time gets loaded here, as Oct 27 2011 3am
  DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
#if !NET20
  if (_readType == ReadType.ReadAsDateTimeOffset)
  {
    SetToken(JsonToken.Date, new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset));
  }
  else
#endif
  {
    DateTime dateTime;
    switch (kind)
    {
      case DateTimeKind.Unspecified:
        dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
        break;
      case DateTimeKind.Local:
        // Here, it gets converted to local time, Oct 26 2011 at 11pm!
        dateTime = utcDateTime.ToLocalTime();
        break;
      default:
        dateTime = utcDateTime;
        break;
    }
    SetToken(JsonToken.Date, dateTime);
  }
}

如上所述,错误仅发生在1980年10月27日至1980年11月2日之间的日期。我没有运行测试来确定哪些年份有问题,但如果使用2011年,测试确实通过了。

我猜这与夏令时的变化有关?

有人知道这里发生了什么吗?

JSON.DateTime值的Net反序列化在某些情况下失败

我已经深入研究了。

当JSON。Net序列化一个DateTime实例,它调用TimeZone.CurrentTimeZone.GetUtcOffset(dt)。当它重新加载DateTime时,它假设一个UTC日期,并调用utcDateTime.ToLocalTime()将其转换为本地时间。这两个方法似乎并不总是使用相同的偏移量:

(注:我在大西洋时区)

[Test, Sequential]
public void AnotherTest(
    [Values(2006, 2006, 2006, 2006, 2006, 2006, 2006)] Int32 year,
    [Values(10, 10, 10, 10, 11, 11, 11)] Int32 month,
    [Values(26, 27, 30, 31, 1, 2, 3)] Int32 day)
{
    var dt = new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Local);
    var utcOffset1 = TimeZone.CurrentTimeZone.GetUtcOffset(dt);
    var utcOffset2 = dt.Subtract(dt.ToUniversalTime());
    Assert.AreEqual(utcOffset1, utcOffset2);
}

这些在10月26日到11月3日期间通过,在此期间不通过。我还对早些年的情况进行了测试,结果相同。从2007年到2011年,这些都通过了。对于我发现的所有失败,utcOffset1是-3小时,而utcOffset2是-4小时。(根据http://www.timeanddate.com/library/abbreviations/timezones/,大西洋标准时间应该是UTC-4,而大西洋夏令时应该是UTC-3。)一些快速测试显示,在2007年之前,夏令时开始时也存在问题。

(我在https://connect.microsoft.com/VisualStudio/feedback/details/699491/timezone-getutcoffset-and-datetime-touniversaltime-not-consistent-for-brief-period-for-atlantic-time-zone上打开了一个问题。)

同时,为了解决这个问题,只需要在序列化和反序列化日期时保持UTC偏移量一致,这意味着要摆脱对TimeZone.CurrentTimeZone.GetUtcOffset的调用。

JamesNK更新JSON。Net在2011年11月1日,使时区转换使用TimeZoneInfo而不是TimeZone,这似乎已经解决了这个问题。

更新2

感谢@derekhat提供以下额外信息:

今晚有空。对我来说,所有的测试都通过了,使用64位的Windows 7,并在命令行中使用。net 2.0 SDK进行编译(必须将var更改为显式类型声明)。

在Visual Studio 2010和。net 4中,7个测试中有5个失败。

然后我找到了下面的文档。

GetUtcOffset方法只识别当前的日光节约时间本地时区的调整规则。因此,它保证准确地返回UTC的本地时间偏移量吗在最新调整规则生效的期间内。它如果Time是历史日期和时间,可能会返回不准确的结果

根据先前的调整规则设置的值。

另一篇文档使问题变得更加复杂:"在Windows XP系统上,当从本地时间转换为UTC时,tuniversaltime方法只识别当前的调整规则。"还有一条评论说,WinXP的行为在Windows Server 2003上也存在。这意味着tuniveraltime可以在较新版本的Windows上正确地处理历史日期,这似乎与您的结果相匹配。

相关文章: