JSON日期轉換的時區陷阱

本文探讨了使用KendoUIDatePicker时,出现的日期转换时区问题,解释了问题根源并提供了从客户端和服务器端解决此问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JSON日期轉換的時區陷阱  

2014-02-12 17:20:56|  分类: HTML/CSS/JS |举报 |字号 订阅

文章来源: http://blog.darkthread.net/post-2013-06-25-json-date-timezone-issue.aspx?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A%2BDarkthread%2B(Darkthread)

使用Kendo UI DatePicker時,出現選好日期送至後端卻變成前一天的狀況。

以下程式可重現問題,kendoDatePicker所選日期透過.value()可得到一個JavaScript Date物件,JSON.stringify()後傳至Server端,使用Json.NET還原回DateTime後,以ToString("yyyy-MM-dd HH:mm:ss")方式傳回Client端alert顯示。

<%@ Page Language="C#" %> <%@ Import Namespace="Newtonsoft.Json" %>   <!DOCTYPE html>   <script runat="server"> protected void Page_Load(object sender, EventArgs e) { if (Request["m"] == "post") { var p = Request["d"]; var nd = JsonConvert.DeserializeObject<DateTime>(p); Response.Write(string.Format("{0}->{1:yyyy-MM-dd HH:mm:ss}", p, nd)); Response.End(); } } </script>   <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title></title> <link href="../Content/kendo/2013.1.319/kendo.common.min.css" rel="stylesheet" /> <link href="../Content/kendo/2013.1.319/kendo.bootstrap.min.css" rel="stylesheet" /> </head> <body> <form id="form1" runat="server"> <input data-bind="kendoDatePicker: { value: TheDate, format: 'yyyy-MM-dd' }" /> <br /> <span data-bind="text: TheDate"></span> <input type="button" data-bind="click: post" value="POST" /> </form> <script src="../Scripts/jquery-1.9.1.min.js"></script> <script src="../Scripts/kendo/2013.1.319/kendo.web.min.js"></script> <script src="../Scripts/knockout-2.2.1.js"></script> <script src="../Scripts/knockout-kendo.min.js"></script> <script> function myViewModel() { var self = this; self.TheDate = ko.observable("2012-12-21"); self.post = function () { $.post("", { m: "post", d: JSON.stringify(self.TheDate()) }, function (r) { alert("Result = " + r); }); }; } var vm = new myViewModel(); ko.applyBindings(vm); </script> </body> </html>

測試結果如下:

JSON日期轉換的時區陷阱 - 123木头人 - 微笑着过好每一天

 明明選了12/22日,但傳到.NET端ToString後卻是12/21日! 問題出在12/22的本地時間在JSON.stringify時被轉成UTC,12/22凌晨0點減去8小時,於是.NET端得到 DateTimeKind = UTC 的DateTime -- 12/21 16:00 UTC。

依據Telerik RD的說法,kendo.stringify跟JSON.stringify一樣,會將本地時間轉換成UTC時間,而kendoDatePicker .value()傳回的是JavaScript Date物件時區則會以本地時間為準,JSON轉成UTC後,若.NET處理時沒轉回本地時間或UTC時間,就會出問題。

知道原委,我理解到這個問題與Kendo UI無關,而是JSON具有全球化觀點,.NET端沒跟上造成的。在一個全球化網站,傳送時間需反應使用者所在時區,Server端才能精準掌握真正時點,但前提是.NET端應將來自各地的時間一律轉為UTC時間或本地時間才合理,直接ToString()看到的是當地時間,忽略時區差異便會衍生問題。

因此,我們可以重塑一個與Kendo UI無關的精簡範例:

<%@ Page Language="C#" %> <%@ Import Namespace="Newtonsoft.Json" %>   <!DOCTYPE html>   <script runat="server"> protected void Page_Load(object sender, EventArgs e) { if (Request["m"] == "post") { var p = Request["d"]; var nd = JsonConvert.DeserializeObject<DateTime>(p); Response.Write(string.Format("{0}->{1:yyyy-MM-dd HH:mm:ss}", p, nd)); Response.End(); } } </script>   <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <span id="sTime"></span> </form> <script src="../Scripts/jquery-1.9.1.min.js"></script> <script> var d = new Date(); $("#sTime").text(d.toString()); $.post("", { m: "post", d: JSON.stringify(d) }, function (r) { alert("Result = " + r); }); </script> </body> </html>

在早上8:00以前,將new Date()經JSON轉換後送到.NET,還原回DateTime再ToString(),看到的日期會是前一天!!

JSON日期轉換的時區陷阱 - 123木头人 - 微笑着过好每一天

 面對這個問題有兩個解決方向:

  1. 在Server端落實全球化概念,所有來自Client端的JSON時間,一律轉為UTC保存,顯示呈現時再視需求決定時區。
  2. 如果只是本土小公司使用的內網系統,所有Client端座落在方圓100公尺內,只因為用了JSON就要在系統推行全球化有點小題大作。而且,Server未必能配合修改,此時就要考慮由Client端解決。

要從Client端解決,我想到的做法是讓JSON.stringify()忽略時區差異,轉成"2013-06-22T07:18:48"(最後不加Z或+0800,對應成.NET DateTime相當於Kind = Unspecified)。實作技巧是偷偷將Date.prototype.toISOString()改成我們自訂的版本:

<script> //將原本的函數保留起來,必要時可以換回去 var _toIsoDate = Date.prototype.toISOString; //借用kendo.toString做出Unspecified Kind的ISO8601格式 Date.prototype.toISOString = function () { return kendo.toString(this, "yyyy-MM-ddTHH:mm:ss"); };   function myViewModel() { var self = this; self.TheDate = ko.observable("2012-12-21"); //...以下略...

重新評估後,改寫.toJSON()只會針對JSON轉換調整邏輯,較置換.toISOString()更符合目的,感謝Kuo-Chun Su提醒。

<script> //借用kendo.toString做出Unspecified Kind的ISO8601格式 Date.prototype.toJSON = function () { return kendo.toString(this, "yyyy-MM-ddTHH:mm:ss"); };   function myViewModel() { var self = this;

如此,應該就能避開惱人的JSON日期時差問題囉~

JSON日期轉換的時區陷阱 - 123木头人 - 微笑着过好每一天
<think>好的,用户想了解@JsonFormat和@DateTimeFormat的用法和区别。首先,我需要回忆这两个注解的相关知识。记得这两个都是处理时间格式的,但属于不同的框架,应用场景不同。根据之前看到的引用资料,@DateTimeFormat是Spring框架的,处理前端传到后端的数据,比如表单或者URL参数。而@JsonFormat来自Jackson库,主要用于JSON数据的序列化和反序列化,比如后端返回给前端的数据格式。 接下来,我需要整理它们的用法。比如,@DateTimeFormat通常用在实体类的字段或者方法的参数上,指定pattern。而@JsonFormat除了pattern,还需要考虑时区,避免时间转换错误。用户可能在实际开发中遇到时间转换的问题,比如前端传的时间字符串后端无法解析,或者后端返回的时间格式不符合前端要求,这时候这两个注解就能派上用场。 然后要比较它们的区别,包括所属框架、处理方向、使用场景、参数要求等。比如,@DateTimeFormat处理的是String到Date的转换,适用于入参;@JsonFormat处理的是Date到String的序列化和反序列化,适用于出参和入参。同时,时区设置也是关键点,Jackson默认可能使用UTC时间,不设置时区会导致时间偏差,所以需要特别提醒用户注意。 用户可能还会想知道实际应用中的例子,比如在实体类中如何同时使用这两个注解,或者在不同场景下的配置。需要给出代码示例,说明如何正确使用注解的参数,比如pattern和timezone。同时,要强调常见的错误,比如忘记设置时区导致时间不对,或者注解位置不正确导致不生效。 最后,生成的相关问题需要覆盖常见的疑惑点,比如为什么需要两个注解、如何选择使用哪个注解、常见的配置错误等。确保用户通过这些问题能进一步深入理解,并解决实际开发中的问题。</think>### @JsonFormat和@DateTimeFormat注解的用法与区别 #### 一、核心功能对比 | 特性 | @DateTimeFormat | @JsonFormat | |--------------|-------------------------------|-------------------------------| | **所属框架** | Spring Framework | Jackson库 | | **处理方向** | 前端→后端(请求参数解析) | 双向处理(请求参数+响应数据) | | **格式参数** | `pattern` | `pattern`+`timezone` | | **典型场景** | 表单提交/URL参数 | JSON数据交互 | #### 二、具体使用方法 1. **@DateTimeFormat(Spring注解)** - **功能**:将字符串转换为`Date`类型 - **应用场景**: ```java public class User { @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") private Date createTime; // 接收如"2023-08-15 14:30"的输入 } ``` - **生效位置**:字段、setter方法、控制器方法参数 2. **@JsonFormat(Jackson注解)** - **功能**:双向格式化JSON中的时间 - **必填参数**: ```java public class Order { @JsonFormat(pattern = "yyyy/MM/dd", timezone = "GMT+8") private Date deliveryDate; // 输出为"2023/08/15" } ``` - **时区陷阱**:未指定时区可能导致时间偏移4小时(Jackson默认UTC时区) #### 三、联合使用示例 ```java public class Event { // 请求参数解析 @DateTimeFormat(pattern = "yyyy-MM-dd") // 响应数据格式化 @JsonFormat(pattern = "dd/MM/yyyy", timezone = "Asia/Shanghai") private Date eventDate; } ``` 此配置实现: - 接收`"2023-12-31"`格式输入 - 返回`"31/12/2023"`格式输出 #### 四、常见问题解决方案 1. **时间格式不匹配**:检查pattern中的大小写(`mm`表示分钟,`MM`表示月份) 2. **时区不一致**:中国地区推荐`timezone = "GMT+8"` 3. **注解失效**:确保Spring配置了`@EnableWebMvc`和`MappingJackson2HttpMessageConverter` #### 五、选择依据 - 仅处理表单提交 → `@DateTimeFormat` - 处理JSON接口 → `@JsonFormat` - 混合场景 → 同时使用两个注解 [^1]: @JsonFormat 和 @DateTimeFormat 时间格式化注解详解(一篇带你解决问题) [^2]: @JsonFormat 和 @DateTimeFormat 的区别 [^3]: @DateTimeFormat 和 @JsonFormat 注解详解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值