Dot-Net

JavaScriptSerializer UTC DateTime 問題

  • June 12, 2013

我們的客戶希望在瀏覽器中顯示與數據庫中完全相同的日期和時間值,我們將它們作為 UTC 儲存在數據庫中。

起初,我們在序列化和 Javascript 方面遇到了一些問題。DateTime 值移動了兩次——首先匹配機器的本地時區,然後匹配瀏覽器中的時區。我們通過向 JavaScriptSerializer 添加自定義轉換器來修復它。我們在 Serialize 覆蓋中將 DateTime 標記為 DateTimeKind.Utc。從 Serialize 回饋數據有點困難,但我們發現了一些 Uri hack,它有助於以相同的 JavaScriptSerializer /Date(286769410010)/ 格式返回 DateTime 值,但無需轉換到本地時間。在 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但問題是如果我們的 JavascriptConverter 具有以下類型,則永遠不會呼叫 Deserialize:

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

SupportedTypes我猜,只有在包含某些具有 DateTime 欄位的複雜實體的類型時才會呼叫 Deserialize 。

所以,JavaScriptSerializerJavascriptConverter兩個不一致的地方:

  • Serialize 為每個數據項考慮 SupportedTypes 中的簡單類型,但 Deserialize 對於簡單類型忽略它
  • 反序列化將一些日期反序列化為 UTC 和一些 - 作為本地時間。

有什麼簡單的方法可以解決這些問題嗎?我們有點害怕JavaScriptSerializer用其他序列化程序替換,因為可能我們正在使用的一些 3rd 方庫依賴於JavaScriptSerializer.

JavaScriptSerializer,並且DataContractJsonSerializer充滿了錯誤。請改用json.net。甚至微軟也在 ASP.Net MVC4 和其他最近的項目中進行了這種轉換。

/Date(286769410010)/格式是專有的,由 Microsoft 製作。它有問題,並且沒有得到廣泛的支持。您應該在1979-02-02T02:10:10Z任何地方使用該格式。這在ISO8601RF3339中定義。它是機器和人類可讀的、詞彙可排序的、文化不變的和明確的。

在 JavaScript 中,如果您可以保證您將在較新的瀏覽器上執行,請使用:

date.toISOString()

參考這裡

如果您想要完整的跨瀏覽器和舊瀏覽器支持,請改用moment.js

更新

順便說一句,如果您真的想繼續使用JavaScriptSerializer,可以反序列化為 a DateTimeOffset,這樣可以保留正確的時間。然後,您可以DateTime從那裡獲取 UTC,如下所示:

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

您的測試現在將通過。

引用自:https://stackoverflow.com/questions/17069460