JavaScriptSerializer UTC日期时间问题

JavaScriptSerializer UTC日期时间问题

问题描述:

我们的客户希望显示在浏览器中,正是因为他们在数据库中的日期和时间值,我们将它们保存为UTC在数据库中。

Our client wanted to show the date and time values in the browser exactly as they are in the database, and we are storing them as UTC in the database.

起初,我们有一些问题,序列化和Javascript的一面。的日期时间值得到移位两次 - 起初以匹配机的本地时区,然后,以匹配在浏览器中的时区。我们通过添加自定义转换器的JavaScriptSerializer固定它。我们打​​上日期时间为DateTimeKind.Utc在序列化覆盖。这是一个有点硬数据馈送从序列化回来,但我们发现了一些乌里破解这有助于在同一个JavaScriptSerializer /日(286769410010)/格式,但没有转移到本地时间返回DateTime值。在JavaScript端,我们修补的KendoUI JS库来弥补所构建的日期()对象,使它们看起来好像他们是UTC。

At first we had some problems with the serialization and Javascript side. The DateTime values got shifted twice - at first to match the local time zone of the machine and then to match the time zone in the browser. We fixed it by adding a custom Converter to the JavaScriptSerializer. We marked the DateTime to be of DateTimeKind.Utc in the Serialize override. It was a bit hard to feed the data back from the Serialize but we found some Uri hack which helped to return DateTime values in the same JavaScriptSerializer /Date(286769410010)/ format but without shifting to the local time. On the Javascript side we patched the KendoUI JS library to offset the constructed Date() objects so they appear as if they are UTC.

然后我们开始工作的另一边,反序列化。同样,我们必须调整我们的code使用自定义字符串化,而不是JSON.stringify,从本地时间转换为UTC时,这又抵消了数据。一切都显得那么好远。

Then we started to work on the other side, deserialization. Again, we had to adjust our code to use a custom stringify instead of JSON.stringify, which again offsets the data when converting from the local time to UTC. Everything seemed good so far.

不过看这测试:

    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反序列化 \ /日(286769410010)\ / 字符串UTC时间,但 1979-02-02T02:10:10Z 本地时间?

Why does JavaScriptSerializer deserialize \/Date(286769410010)\/ strings to UTC time but 1979-02-02T02:10:10Zto local time?

我们试图将反序列化方法添加到我们的自定义 JavascriptConverter 但问题是,反序列化是从来没有所谓的,如果我们的JavascriptConverter有以下几种类型:

We tried to add a Deserialize method to our custom JavascriptConverter but the problem is that the Deserialize is never called if our JavascriptConverter has the following types:

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

我想,反序列化将被称为只有当 SupportedTypes 包含类型具有日期时间字段一些复杂的实体。

I guess, Deserialize would be called only if SupportedTypes contained types of some complex entities which have DateTime fields.

因此​​, JavaScriptSerializer JavascriptConverter 有两个矛盾:

So, JavaScriptSerializer and JavascriptConverter have two inconsistencies:

  • 在序列化考虑简单类型SupportedTypes为每个数据项,但反序列化会忽略它的简单类型
  • 在反序列化反序列化一些日期为UTC有的 - 为本地时间

有没有解决这些问题,没有简单的方法? 我们都有点不敢替换 JavaScriptSerializer 与其他一些序列化,因为也许有些我们使用的是第三方库,都依赖于一些特定的功能/错误 JavaScriptSerializer

Is there any simple way to fix these issues? We are a bit afraid to replace JavaScriptSerializer with some other serializer because maybe some of the 3rd party libraries we are using, are relying upon some certain "features/bugs" of JavaScriptSerializer.

JavaScriptSerializer DataContractJsonSerializer 上布满错误。使用 json.net 代替。即使微软已经取得了这个开关在ASP.Net MVC4和其他最近的项目。

JavaScriptSerializer, and DataContractJsonSerializer are riddled with bugs. Use json.net instead. Even Microsoft has made this switch in ASP.Net MVC4 and other recent projects.

/日(286769410010)/ 格式是专有的,由微软。它具有的问题,并且不广泛支持。你应该使用 1979-02-02T02:10:10Z 格式无处不在。这在 ISO8601 和的 RF3339 。它既是机器和人类可读的,词法排序,文化不变,并毫不含糊。

The /Date(286769410010)/ format is proprietary and made up by Microsoft. It has problems, and is not widely supported. You should use the 1979-02-02T02:10:10Z format everywhere. This is defined in ISO8601 and RF3339. It is both machine and human readable, lexically sortable, culture invariant, and unambiguous.

在JavaScript中,如果你能保证你会在新的浏览器运行,则使用:

In JavaScript, if you can guarantee you will be running on newer browsers, then use:

date.toISOString()

这里 参考。

Reference here.

如果你想完全跨浏览器和较旧的浏览器支持,使用 moment.js 代替。

If you want full cross-browser and older-browser support, use moment.js instead.

更新

顺便说一句,如果你真的想继续使用 JavaScriptSerializer ,你可以反序列化到的DateTimeOffset ,其中将preserve正确的时间。然后,您可以得到UTC 的DateTime 从那里,如下:

As an aside, if you really want to keep using JavaScriptSerializer, you could deserialize to a DateTimeOffset, which would preserve the correct time. You could then get the UTC DateTime from there, as follows:

// 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;

您的测试现在通过。