【經驗分享】為什麼後臺取到的時間和前臺差8個小時?
發現問題
單元格編輯時,你可能會遇到前臺傳入的時間,後臺通過C#獲取時差8個小時,這是怎麼回事呢?
這個問題可能會困擾一些同學,我也不止一次的收到這樣的問題,這個是昨天一個網友的提問:
之前還有網友在 【三石和他的朋友們】知識星球 內發表類似的問題(歡迎大家加入我的星球^>^):
為了演示這一過程,我通過一個簡單的例子來說明問題,首先新建一個頁面:
@(F.DatePicker().DateFormatString("yyyy-MM-dd HH:mm:ss").Label("開始日期").ID("DatePicker1").ShowTime(true).SelectedDate(DateTime.Now)) @(F.Button().ID("btnSubmit").Text("提交表單").OnClick(Url.Action("btnSubmit_Click"), "DatePicker1")) @(F.Label().ID("labResult"))
後臺程式碼:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult btnSubmit_Click(FormCollection values) { UIHelper.Label("labResult").Text("開始日期:" + values["DatePicker1"]); return UIHelper.Result(); }
因為後臺直接從請求表單中讀取的字串,所以沒有問題,前臺引數傳入:
DatePicker1: 2019-03-21 14:48:55
頁面上顯示:
開始日期:2019-03-21 14:48:55
現在前臺新增一個按鈕,並通過自定義回發的形式傳入後臺:
@(F.Button().ID("btnSubmit2").Text("自定義回發").OnClientClick("btnSubmit2Click();")) function btnSubmit2Click() { F.doPostBack('@Url.Action("btnSubmit2_Click")', { values: F.toJSON({ DatePicker1: F.ui.DatePicker1.getValue() }) }); }
後臺直接從JSON物件中讀取資料,並顯示:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult btnSubmit2_Click(JObject values) { UIHelper.Label("labResult").Text("開始日期:" + values["DatePicker1"].ToString()); return UIHelper.Result(); }
此時再看回發後的前臺顯示:
開始日期:2019/3/21 6:45:44
好嘛!剛好差8個小時,逮個正著!
分析問題
可能有人會說了,是不是前臺傳入的資料有誤?其實不是的,開啟瀏覽器除錯工具,看下傳入的引數:
values: {"DatePicker1":"2019-03-21T06:48:55.000Z"}
可以發現,前臺 F.toJSON 之後,原來的字串 2019-03-21 14:48:55 被轉化為標準時間:2019-03-21T06:48:55.000Z
這個轉化是沒問題的,因為它(2019-03-21T06:48:55.000Z)描述的是標準零時區的時間,和我們的本地時間(北京時間,東八區)剛好差了8個小時。
問題出在後臺JSON格式轉化,JSON.NET會識別含有類似 2019-03-21T06:48:55.000Z 的字串,並將之轉化為時間格式!!
在VS中除錯,可以看到 values["DatePicker1"] 其實是 Date 型別,並非我們所期望的 string 型別:
解決問題
其實這個時間物件也沒問題,只不過它表示的是標準零時區時間,我們只需將其轉化為本地時間就可以了,所以正確的程式碼應該是這樣的:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult btnSubmit2_Click(JObject values) { UIHelper.Label("labResult").Text("開始日期:" + values.Value<DateTime>("DatePicker1").ToLocalTime().ToString()); return UIHelper.Result(); }
現在前臺顯示:
開始日期:2019/3/21 14:48:55
還可以將字串格式化為需要的格式:
UIHelper.Label("labResult").Text("開始日期:" + values.Value<DateTime>("DatePicker1").ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"));
此時前臺顯示:
開始日期:2019-03-21 14:48:55
深入問題
還有觀眾說了,JSON.NET的這個自動轉化我不需要,能不能直接拿到這個字串,然後我自己通過 DateTime.Parse 來轉換呢?
我在網上搜索了一下,發現如下兩個解決辦法,供參考:
辦法一:
JsonReader reader = new JsonTextReader(new StringReader(values.ToString())); reader.DateParseHandling = DateParseHandling.None; JObject o = JObject.Load(reader); // 2019/3/21 14:48:55 var result1 = DateTime.Parse(o.Value<string>("DatePicker1")).ToString();
辦法二:
JsonSerializerSettings settings = new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None }; JObject j2 = JsonConvert.DeserializeObject<JObject>(values.ToString(), settings);
說白了就是告訴 JSON.NET,不要自作主張的幫我把字串轉換為日期物件(DateParseHandling.None),我要取得原始的字串。
並且由於上面需要把 values 先轉換為字串,既然如此,還不如直接使用 string 來接受引數(少了一次引數自動型別轉換和一次強制型別轉換):
[HttpPost] [ValidateAntiForgeryToken] public ActionResult btnSubmit2_Click(string values) { JsonSerializerSettings settings = new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None }; JObject j2 = JsonConvert.DeserializeObject<JObject>(values, settings); return UIHelper.Result(); }
只不過這個路子繞的有點遠。