Проблемы JavaScriptSerializer UTC DateTime

Наш клиент хотел, чтобы значения даты и времени отображались в браузере точно так же, как они находятся в базе данных, и мы сохраняем их как UTC в базе данных.

Сначала у нас были проблемы со стороной сериализации и Javascript. Значения DateTime были сдвинуты дважды - сначала в соответствии с местным часовым поясом компьютера, а затем в соответствии с часовым поясом в браузере. Мы исправили это, добавив специальный конвертер в JavaScriptSerializer. Мы отметили DateTime как DateTimeKind.Utc в переопределении Serialize. Было немного сложно передать данные обратно из Serialize, но мы нашли хак Uri, который помог вернуть значения DateTime в том же формате JavaScriptSerializer / Date (286769410010) /, но без перехода на местное время. На стороне 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 по местному времени?

Мы пытались добавить метод Deserialize к нашему пользовательскому JavascriptConverter, но проблема в том, что Deserialize никогда не вызывается, если наш JavascriptConverter имеет следующие типы:

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

Я предполагаю, что Deserialize будет вызываться только в том случае, если SupportedTypes содержит типы некоторых сложных сущностей, которые имеют поля DateTime.

Итак, JavaScriptSerializer и JavascriptConverter имеют два несоответствия:

  • Serialize учитывает простые типы в SupportedTypes для каждого элемента данных, но Deserialize игнорирует его для простых типов.
  • Deserialize десериализует некоторые даты как UTC, а некоторые - как местное время.

Есть ли простой способ исправить эти проблемы? Мы немного боимся заменять JavaScriptSerializer каким-либо другим сериализатором, потому что, возможно, некоторые из сторонних библиотек, которые мы используем, полагаются на некоторые определенные «особенности / ошибки» JavaScriptSerializer.


person JustAMartin    schedule 12.06.2013    source источник


Ответы (1)


JavaScriptSerializer и DataContractJsonSerializer изобилуют ошибками. Вместо этого используйте json.net. Даже Microsoft сделала этот переход в ASP.Net MVC4 и других недавних проектах.

Формат /Date(286769410010)/ является проприетарным и разработан Microsoft. У него есть проблемы, и он не пользуется широкой поддержкой. Вы должны везде использовать формат 1979-02-02T02:10:10Z. Это определено в ISO8601 и RF3339. Он читается как машиной, так и человеком, лексически сортируется, инвариантен к культуре и однозначен.

В JavaScript, если вы можете гарантировать, что будете работать в более новых браузерах, используйте:

date.toISOString()

Ссылка здесь.

Если вам нужна полная кросс-браузерная поддержка и поддержка старых браузеров, используйте вместо этого 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;

Теперь ваш тест пройден.

person Matt Johnson-Pint    schedule 12.06.2013
comment
Хорошо, так что все это хорошо, хорошо и полезно, но какое дерьмовое оправдание для Msft, не просто поставляя лучшую библиотеку json и оставляя в покое глючный JavaScriptSerializer? - person nothingisnecessary; 17.11.2016