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