关于.net:JavaScriptSerializer UTC DateTime问题

JavaScriptSerializer UTC DateTime issues

我们的客户希望在浏览器中显示日期和时间值,就像它们在数据库中一样,我们将它们作为UTC存储在数据库中。

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

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

但看看这个测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    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到本地时间?

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

1
2
3
4
    public override IEnumerable<Type> SupportedTypes
    {
        get { return new List<Type>() { typeof(DateTime), typeof(DateTime?) }; }
    }

我想,只有当SupportedTypes包含一些具有DateTime字段的复杂实体的类型时,才会调用反序列化。

所以,JavaScriptSerializerJavascriptConverter有两个不一致:

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

有没有简单的方法来解决这些问题?
我们有点害怕将JavaScriptSerializer替换为其他一些序列化程序,因为我们使用的某些第三方库可能依赖于JavaScriptSerializer的某些"功能/错误"。


JavaScriptSerializerDataContractJsonSerializer充满了错误。 请改用json.net。 甚至微软也在ASP.Net MVC4和其他近期项目中进行了这一切换。

/Date(286769410010)/格式是专有的,由Microsoft制作。 它有问题,并没有得到广泛的支持。 你应该到处使用1979-02-02T02:10:10Z格式。 这在ISO8601和RF3339中定义。 它既具有机器可读性,又具有词汇可读性,文化不变性,且无歧义。

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

1
date.toISOString()

参考这里。

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

UPDATE

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

1
2
3
4
// 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;

您的测试现在将通过。