解決辦法:
@JsonFormat(pattern="yyyy-MM-dd") private Date birth;
改成
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private Date birth;
加上時區即可,中國是東八區
But 有時就算加上還是少一天,那就需要用到下面了:
數據信息入庫后,重新編輯發現出生日期減少了一天,比如1987-08-04,轉換到界面后卻變成了1987-08-03;
問題發現過程
1 先看前端是否有特殊處理,經過檢查發現沒有問題。
2 找到獲取專家基本信息接口,調用發現返回的日期的確是少了一天,這里可以判斷問題出在后端
3 然后斷點,發現在接口最終返回的是一個date類型,這可以斷定問題出在最后一步,就是數據轉換這塊
4 經過spring-mvc.xml的配置文件,發現轉換器是jackson,MappingJackson2HttpMessageConverter類,
看了一下實體對象birthday,的定義,@JsonFormat都是jackson包的注解,然后就就基本上確定是這里出了問題
1 @JSONField (format="yyyy-MM-dd") 2 @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") 3 @DateTimeFormat(pattern ="yyyy-MM-dd") 4 private Date birthday;
5 然后就搜索 JsonFormat 少了一天的關鍵信息,發現搜索的結果是,他們沒有加時區timezone造成的。這里我也加了的啊?然后就重新試了注釋掉JsonFormat結果返回時毫秒數。然后把它加上。同樣的問題我在160服務器上試了卻發現結果是對的,而我本地和測試環境確都是有問題。然后我開始懷疑是不是數據庫有時區。
6 就開始搜索數據庫有關時區的問題。結果嘗試了,還是一樣的結果,現在想想這應該更數據庫時區沒有多大的關系。因為它只是做一個存儲,你存了什么時間就返回什么時間。又回到代碼層面。
7 就開始斷點MappingJackson2HttpMessageConverter代碼,通過層層的跟進。最后發現了日期轉換的類
DateSerializer,customFormat.format(value), 其實就是SimpleDateFormat的format。
1 @Override 2 public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) 3 throws IOException, JsonGenerationException 4 { 5 if (_useTimestamp) { 6 jgen.writeNumber(_timestamp(value)); 7 } else if (_customFormat != null) { 8 // 21-Feb-2011, tatu: not optimal, but better than alternatives: 9 synchronized (_customFormat) { 10 jgen.writeString(_customFormat.format(value)); 11 } 12 } else { 13 provider.defaultSerializeDateValue(value, jgen); 14 } 15 } 16 }
用兩個日期斷點發現了一些端倪。
1987-08-04T00:00:00.000+0900
2017-12-19T00:00:00.000+0800
經過猜測,最后+后面的應該是時區吧。這里覺得很奇怪,為什么明明設置了GTM+8時區的為什么這里不起作用了。
8 最后就斷點到getBirthDay()方法,發現這里返回的就是這個。所以問題就應該出現這里。兩個日期為什么就不相同了。此刻 沒找到答案,就在segmentfault上發了一個問題。結果沒多久,發現就有個人回復我。
是夏令時,給我了一個連接。發現也是遇到同樣的問題。它給出了三個解決方案,我選擇了第二種試試
就是在取日期之前,加入如下代碼:
TimeZone.setDefault(TimeZone.getTimeZone("GMT+08"));
1 public Date getBirthday() { 2 //這里由於有夏令營時間存在 這是要默認設置時區,@see http://www.cnblogs.com/memory4young/p/java-timezone.html 3 TimeZone.setDefault(TimeZone.getTimeZone("GMT+08")); 4 return birthday; 5 }
經過測試,真的起了效果。反正這玩意也是稍微理解了下,發現問題都出現在1987到1991年的四月,(不僅僅從1987開始 ,194幾也都有這個問題)
這期間,這是一個神奇的問題。
問題原因 :
經過思考我發覺應該是時區的問題,1987默認為東9區,我現在設置為東8區,應該是少一個小時。1987-08-04 00:00:00,少一小時 就是 1987-08-03,Oh yeah !
上面就是問題的復盤:
后記 :
在搜索過程中發現了一個問題。就是timestap和datetime區別,其中一點就是有時區差。
TIMESTAMP
1、4個字儲存(Time stamp value is stored in 4 bytes)
2、值以UTC格式保存( it stores the number of milliseconds)
3、時區轉化 ,存儲時對當前的時區進行轉換,檢索時再轉換回當前的時區。
datetime
1、8個字節儲存(8 bytes storage)
2、實際格式儲存(Just stores what you have stored and retrieves the same thing which you have stored.)
3、與時區無關(It has nothing to deal with the TIMEZONE and Conversion.)
我看了下birthday 數據庫中是datetime ,而在Mapper映射文件中是JDBCTYPE="TIMESTAMP",
所以在xml中,也修改了下,將JDBCTYPE改為DATETIME。是什么類型用什么類型。這里改變不了上面的 問題,但是應該要按照實際需求對應
to:https://blog.csdn.net/samz5906/article/details/79421051?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control