Problemas com JavaScriptSerializer UTC DateTime

Nosso cliente queria mostrar os valores de data e hora no navegador exatamente como eles estão no database, e nós os armazenamos como UTC no database.

No começo, tivemos alguns problemas com o lado de serialização e Javascript. Os valores DateTime foram deslocados duas vezes – primeiro para corresponder ao fuso horário local da máquina e depois para corresponder ao fuso horário no navegador. Corrigimos isso adicionando um Conversor personalizado ao JavaScriptSerializer. Marcamos o DateTime como sendo de DateTimeKind.Utc na substituição Serialize. Foi um pouco difícil para alimentar os dados de volta do Serialize, mas encontramos alguns hack Uri que ajudou a retornar valores DateTime no mesmo JavaScriptSerializer / Date (286769410010) / formato, mas sem mudar para a hora local. No lado do Javascript, fizemos o patch da biblioteca JS do KendoUI para compensar os objects Date () construídos, para que eles apareçam como se fossem UTC.

Então começamos a trabalhar no outro lado, a desserialização. Novamente, tivemos que ajustar nosso código para usar um stringify personalizado em vez de JSON.stringify, que novamente compensa os dados ao converter da hora local para UTC. Tudo parecia bem até agora.

Mas olhe para este teste:

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(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(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); } 

Por que o JavaScriptSerializer desserializa \/Date(286769410010)\/ strings para a hora UTC, mas 1979-02-02T02:10:10Z para a hora local?

Tentamos adicionar um método Deserialize ao nosso JavascriptConverter personalizado, mas o problema é que o Deserialize nunca é chamado se o nosso JavascriptConverter tiver os seguintes tipos:

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

Eu acho que, Deserialize seria chamado apenas se SupportedTypes continha tipos de algumas entidades complexas que possuem campos DateTime.

Portanto, JavaScriptSerializer e JavascriptConverter possuem duas inconsistências:

  • Serialize leva em conta tipos simples em SupportedTypes para cada item de dados, mas Deserialize o ignora para tipos simples
  • Desserialize desserializa algumas datas como UTC e algumas – como hora local.

Existe alguma maneira simples de corrigir esses problemas? Temos um pouco de medo de replace o JavaScriptSerializer por outro serializador porque talvez algumas das bibliotecas de terceiros que estamos usando dependam de alguns “resources / bugs” do JavaScriptSerializer .

JavaScriptSerializer e DataContractJsonSerializer estão cheios de erros. Use json.net em vez disso. Até mesmo a Microsoft fez essa mudança no asp.net MVC4 e outros projetos recentes.

O formato /Date(286769410010)/ é proprietário e feito pela Microsoft. Tem problemas e não é amplamente suportado. Você deve usar o formato 1979-02-02T02:10:10Z todos os lugares. Isso é definido em ISO8601 e RF3339 . É legível por máquina e humana, lexicamente ordenável, cultura invariável e inequívoca.

Em JavaScript, se você puder garantir que estará sendo executado em navegadores mais recentes, use:

 date.toISOString() 

Referência aqui .

Se você quiser suporte completo a vários navegadores e navegadores antigos, use moment.js .

ATUALIZAR

Como um aparte, se você realmente quiser continuar usando o JavaScriptSerializer , você pode desserializar para um DateTimeOffset , o que preservaria o horário correto. Você pode então obter o UTC DateTime de lá, da seguinte maneira:

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

Seu teste agora passará.