JavaScriptSerializer UTC DateTime 问题

Posted

技术标签:

【中文标题】JavaScriptSerializer UTC DateTime 问题【英文标题】:JavaScriptSerializer UTC DateTime issues 【发布时间】:2013-06-08 19:10:54 【问题描述】:

我们的客户希望在浏览器中显示与数据库中完全相同的日期和时间值,我们将它们作为 UTC 存储在数据库中。

起初我们在序列化和 javascript 方面遇到了一些问题。 DateTime 值移动了两次——首先匹配机器的本地时区,然后匹配浏览器中的时区。我们通过向 JavaScriptSerializer 添加自定义转换器来修复它。我们在 Serialize 覆盖中将 DateTime 标记为 DateTimeKind.Utc。从 Serialize 反馈数据有点困难,但我们发现了一些 Uri hack,它有助于以相同的 JavaScriptSerializer /Date(286769410010)/ 格式返回 DateTime 值,但无需转换到本地时间。在 Javascript 方面,我们修补了 KendoUI JS 库以偏移构造的 Date() 对象,使它们看起来好像是 UTC。

然后我们开始在另一边工作,反序列化。同样,我们不得不调整我们的代码以使用自定义 stringify 而不是 JSON.stringify,这在从本地时间转换为 UTC 时再次偏移数据。到目前为止,一切似乎都很好。

但是看看这个测试:

    public void DeserialiseDatesTest()
    
        var dateExpected = new DateTime(1979, 2, 2,
            2, 10, 10, 10, DateTimeKind.Utc);

        // this how the Dates look like after serializing
        // anothe issue, unrelated to the core problem, is that the "\" might get stripped out when dates come back from the browser
        // so I have to add missing "\" or else Deserialize will break
        string s = "\"\\/Date(286769410010)\\/\"";

        // this get deserialized to UTC date by default
        JavaScriptSerializer js = new JavaScriptSerializer();

        var dateActual = js.Deserialize<DateTime>(s);
        Assert.AreEqual(dateExpected, dateActual);
        Assert.AreEqual(DateTimeKind.Utc, dateActual.Kind);

        // but some Javascript components (like KendoUI) sometimes use JSON.stringify 
        // for Javascript Date() object, thus producing the following:
        s = "\"1979-02-02T02:10:10Z\"";

        dateActual = js.Deserialize<DateTime>(s);
        // If your local computer time is not UTC, this will FAIL!
        Assert.AreEqual(dateExpected, dateActual);

        // and the following fails always
        Assert.AreEqual(DateTimeKind.Utc, dateActual.Kind); 
    

为什么 JavaScriptSerializer 将 \/Date(286769410010)\/ 字符串反序列化为 UTC 时间,而将 1979-02-02T02:10:10Z 反序列化为本地时间?

我们尝试向自定义 JavascriptConverter 添加 Deserialize 方法,但问题是如果我们的 JavascriptConverter 具有以下类型,则永远不会调用 Deserialize:

    public override IEnumerable<Type> SupportedTypes
    
        get  return new List<Type>()  typeof(DateTime), typeof(DateTime?) ; 
    

我猜,只有当 SupportedTypes 包含一些具有 DateTime 字段的复杂实体类型时,才会调用 Deserialize。

所以,JavaScriptSerializerJavascriptConverter 有两个不一致:

Serialize 会为每个数据项考虑 SupportedTypes 中的简单类型,但 Deserialize 会忽略简单类型的简单类型 Deserialize 将某些日期反序列化为 UTC,将某些日期反序列化为本地时间。

有什么简单的方法可以解决这些问题吗? 我们有点害怕用其他序列化程序替换 JavaScriptSerializer,因为可能我们正在使用的一些 3rd 方库依赖于 JavaScriptSerializer 的某些“功能/错误”。

【问题讨论】:

【参考方案1】:

JavaScriptSerializerDataContractJsonSerializer 充满了漏洞。请改用json.net。甚至微软也在 ASP.Net MVC4 和其他最近的项目中进行了这种转换。

/Date(286769410010)/ 格式是专有的,由 Microsoft 制作。它有问题,并且没有得到广泛的支持。您应该在任何地方使用1979-02-02T02:10:10Z 格式。这是在ISO8601 和RF3339 中定义的。它是机器和人类可读的、词汇可排序的、文化不变的和明确的。

在 JavaScript 中,如果您能保证您将在较新的浏览器上运行,请使用:

date.toISOString()

Reference here.

如果您想要完整的跨浏览器和旧版浏览器支持,请改用moment.js。

更新

顺便说一句,如果你真的想继续使用JavaScriptSerializer,你可以反序列化为DateTimeOffset,这样可以保留正确的时间。然后,您可以从那里获取 UTC DateTime,如下所示:

// note, you were missing the milliseconds in your example, I added them here.
s = "\"1979-02-02T02:10:10.010Z\"";

dateActual = js.Deserialize<DateTimeOffset>(s).UtcDateTime;

您的测试现在将通过。

【讨论】:

好的 - 所以这一切都很好,很好,很有用,但是 Msft 的废话借口是什么,而不是仅仅提供更好的 json 库并让有问题的 JavaScriptSerializer 一个人呆着?

以上是关于JavaScriptSerializer UTC DateTime 问题的主要内容,如果未能解决你的问题,请参考以下文章

DataContractJsonSerializer 和 JavaScriptSerializer 有啥区别?

使用 LINQ/JavaScriptSerializer 创建 JSON 对象而不是数组

JavaScriptSerializer 可以排除具有空值/默认值的属性吗?

JavaScriptSerializer 正在从日期中减去一天

指定分配索引值时,JavaScriptSerializer 不使用枚举 [重复]

带有 Razor 和 ASPX 引擎的 JavaScriptSerializer