2013-06-12 14 views
10

Il nostro cliente voleva mostrare i valori di data e ora nel browser esattamente come sono nel database, e li stiamo memorizzando come UTC nel database.JavaScriptSerializer UTC DateTime problemi

All'inizio abbiamo avuto alcuni problemi con la serializzazione e il lato Javascript. I valori DateTime sono stati spostati due volte - all'inizio per abbinare il fuso orario locale della macchina e quindi per abbinare il fuso orario nel browser. L'abbiamo risolto aggiungendo un convertitore personalizzato a JavaScriptSerializer. Abbiamo contrassegnato il DateTime come DateTimeKind.Utc in Serialize override. È stato un po 'difficile alimentare i dati dal Serialize, ma abbiamo trovato alcuni Uri hack che hanno aiutato a restituire i valori DateTime nello stesso JavaScriptSerializer/Date (286769410010)/format ma senza passare all'ora locale. Sul lato Javascript abbiamo patchato la libreria JS KendoUI per bilanciare gli oggetti Date() costruiti in modo che appaiano come se fossero UTC.

Poi abbiamo iniziato a lavorare dall'altra parte, la deserializzazione. Ancora una volta, abbiamo dovuto modificare il nostro codice per utilizzare un stringify personalizzato anziché JSON.stringify, che compensa nuovamente i dati durante la conversione dall'ora locale a UTC. Tutto sembrava buono finora.

Ma guarda questo test:

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

Perché JavaScriptSerializer deserializzare \/Date(286769410010)\/ stringhe ora UTC ma 1979-02-02T02:10:10Z in ora locale?

abbiamo cercato di aggiungere un metodo Deserialize al nostro personalizzato JavascriptConverter ma il problema è che la Deserialize non viene mai chiamato se il nostro JavascriptConverter ha i seguenti tipi:

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

immagino, Deserialize sarebbe chiamato solo se SupportedTypes tipi contenuti di alcune entità complesse che hanno campi DateTime.

Così, JavaScriptSerializer e JavascriptConverter hanno due incongruenze:

  • Serialize prende in considerazione i tipi semplici in SupportedTypes per ogni elemento di dati, ma Deserialize ignora per i tipi semplici
  • Deserialize deserializza alcune date come UTC e un po ' - come ora locale.

C'è qualche modo semplice per risolvere questi problemi? Abbiamo un po 'paura di sostituire JavaScriptSerializer con qualche altro serializzatore perché forse alcune delle librerie di terze parti che stiamo usando si affidano ad alcune "caratteristiche/bug" di JavaScriptSerializer.

risposta

36

JavaScriptSerializer e DataContractJsonSerializer sono pieni di bug. Utilizzare invece json.net. Anche Microsoft ha fatto questo passaggio in ASP.Net MVC4 e altri progetti recenti.

Il formato /Date(286769410010)/ è proprietario e composto da Microsoft. Ha problemi e non è ampiamente supportato. Dovresti utilizzare il formato 1979-02-02T02:10:10Z ovunque. Questo è definito in ISO8601 e RF3339. È sia macchina che leggibile dall'uomo, lessicalmente ordinabile, cultura invariante e non ambigua.

In JavaScript, se si può garantire che si sarà in esecuzione nei browser più recenti, quindi utilizzare:

date.toISOString() 

Reference here.

Se si desidera il supporto completo per browser e browser precedenti, utilizzare invece moment.js.

UPDATE

Per inciso, se si vuole davvero continuare a utilizzare JavaScriptSerializer, si potrebbe deserializzare ad un DateTimeOffset, che avrebbe preservato l'ora esatta. Si potrebbe quindi ottenere l'UTC DateTime da lì, come segue:

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

Il test sarà ora passare.

+1

Ok, quindi tutto va bene, è utile e utile, ma qual è la scusa della cazzata per Msft, non solo per spedire la migliore libreria JSON e per lasciare solo il buggy JavaScriptSerializer? – nothingisnecessary

Problemi correlati