2013-06-12 14 views
10

Unser Client wollte die Datums- und Uhrzeitwerte im Browser genauso anzeigen, wie sie in der Datenbank sind, und wir speichern sie als UTC in der Datenbank.JavaScriptSerializer UTC DateTime-Ausgaben

Zuerst hatten wir einige Probleme mit der Serialisierung und Javascript Seite. Die DateTime-Werte wurden zweimal verschoben - zunächst so, dass sie mit der lokalen Zeitzone der Maschine übereinstimmen und dann mit der Zeitzone im Browser übereinstimmen. Wir haben es behoben, indem wir dem JavaScriptSerializer einen benutzerdefinierten Konverter hinzugefügt haben. Wir haben die DateTime als DateTimeKind.Utc in der Serialize-Überschreibung markiert. Es war ein bisschen schwierig, die Daten aus dem Serialize zurückzugeben, aber wir fanden einen Uri-Hack, der dazu half, DateTime-Werte im selben JavaScriptSerializer/Date-Format (286769410010) zurückzugeben, aber ohne auf die Ortszeit zu wechseln. Auf der Javascript-Seite haben wir die KendoUI JS-Bibliothek gepatcht, um die konstruierten Date() -Objekte so zu versetzen, als ob sie UTC wären.

Dann begannen wir auf der anderen Seite zu arbeiten, Deserialisierung. Auch hier mussten wir unseren Code so anpassen, dass anstelle von JSON.stringify ein benutzerdefinierter Stringify verwendet wurde, der die Daten beim Konvertieren von der Ortszeit in UTC erneut ausgleicht. Alles schien bisher gut zu sein.

Aber schauen Sie auf diesen 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); 
    } 

Warum JavaScriptSerializer \/Date(286769410010)\/ Strings in UTC-Zeit deserialisieren aber 1979-02-02T02:10:10Z in der lokalen Zeit?

Wir haben versucht, eine Deserialize Methode unserer Gewohnheit hinzuzufügen JavascriptConverter aber das Problem ist, dass die Deserialize nie, wenn unsere Javascript die folgenden Typen hat aufgerufen:

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

ich denke, würde Deserialize nur, wenn SupportedTypes genannt werden enthält Typen einiger komplexer Entitäten, die DateTime-Felder enthalten.

So JavaScriptSerializer und JavascriptConverter haben zwei Widersprüche:

  • Serialize berücksichtigt einfache Typen in SupportedTypes für jeden Datenpunkt, aber Deserialize ignoriert es für einfache Typen
  • Deserialize deserialisiert einige Daten als UTC und einige - als Ortszeit.

Gibt es eine einfache Möglichkeit, diese Probleme zu beheben? Wir haben ein wenig Angst, JavaScriptSerializer mit einem anderen Serializer zu ersetzen, weil einige der Bibliotheken von Drittanbietern, die wir verwenden, auf bestimmte "features/bugs" von JavaScriptSerializer angewiesen sind.

Antwort

36

JavaScriptSerializer und DataContractJsonSerializer sind mit Bugs gespickt. Verwenden Sie stattdessen json.net. Sogar Microsoft hat diesen Wechsel in ASP.Net MVC4 und anderen aktuellen Projekten vorgenommen.

Das Format /Date(286769410010)/ ist proprietär und besteht aus Microsoft. Es hat Probleme und wird nicht allgemein unterstützt. Sie sollten das Format 1979-02-02T02:10:10Z überall verwenden. Dies ist definiert in ISO8601 und RF3339. Es ist sowohl maschinen- als auch menschenlesbar, lexikalisch sortierbar, kulturinvariant und eindeutig.

In JavaScript, wenn Sie garantieren können Sie auf neueren Browsern ausgeführt werden soll, verwenden Sie dann:

date.toISOString() 

Reference here.

Wenn Sie vollständige browserübergreifende und ältere Browser-Unterstützung wünschen, verwenden Sie stattdessen moment.js.

UPDATE

Als Nebenwirkung, wenn Sie wirklich JavaScriptSerializer behalten möchten verwenden, können Sie zu einem DateTimeOffset deserialisieren könnte, was die richtige Zeit erhalten würde. Sie könnten dann die UTC DateTime von dort erhalten, wie folgt:

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

Ihr Test wird nun passieren.

+1

Ok - also ist das alles gut und gut und nützlich, aber was ist die Schwachsinn Entschuldigung für Msft nicht nur die Lieferung der besseren JSON-Bibliothek und die Buggy JavaScriptSerializer allein lassen? – nothingisnecessary