Java 日期 解析減少一天問題。神奇

覆盤
  今天在做一個入庫項目,shuju信息入庫後,重新編輯發現出生日期減少了一天,比如1987-08-04,轉換到界面後卻變成了1987-08-03,

  問題發現過程
     1 先看前端是否有特殊處理,經過檢查發現沒有問題。
     2 找到獲取專家基本信息接口,調用發現返回的日期的確是少了一天,這裏可以判斷問題出在後端
     3 然後斷點,發現在接口最終返回的是一個date類型,這可以斷定問題出在最後一步,就是數據轉換這塊
     4  經過spring-mvc.xml的配置文件,發現轉換器是jackson,MappingJackson2HttpMessageConverter類,
        看了一下實體對象birthday,的定義,@JsonFormat都是jackson包的註解,然後就就基本上確定是這裏出了問題


    @JSONField (format="yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern ="yyyy-MM-dd")
    private Date birthday; 

5  然後就搜索 JsonFormat 少了一天的關鍵信息,發現搜索的結果是,他們沒有加時區timezone造成的。這裏我也加了的啊?然後就重新試了註釋掉JsonFormat結果返回時毫秒數。然後把它加上。同樣的問題我在160服務器上試了卻發現結果是對的,而我本地和測試環境確都是有問題。然後我開始懷疑是不是數據庫有時區。

     6  就開始搜索數據庫有關時區的問題。結果嘗試了,還是一樣的結果,現在想想這應該更數據庫時區沒有多大的關係。因爲它只是做一個存儲,你存了什麼時間就返回什麼時間。又回到代碼層面。
 7 就開始斷點MappingJackson2HttpMessageConverter代碼,通過層層的跟進。最後發現了日期轉換的類
DateSerializer,customFormat.format(value), 其實就是SimpleDateFormat的format。
@Override
    public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonGenerationException
    {
        if (_useTimestamp) {
            jgen.writeNumber(_timestamp(value));
        } else if (_customFormat != null) {
            // 21-Feb-2011, tatu: not optimal, but better than alternatives:
            synchronized (_customFormat) {
                jgen.writeString(_customFormat.format(value));
            }
        } else {
            provider.defaultSerializeDateValue(value, jgen);
        }
    }
}

    用兩個日期斷點發現了一些端倪。
 
    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"));
 
public Date getBirthday() {
        //這裏由於有夏令營時間存在 這是要默認設置時區,@see http://www.cnblogs.com/memory4young/p/java-timezone.html
        TimeZone.setDefault(TimeZone.getTimeZone("GMT+08"));
        return birthday;
    }
    

  經過測試,真的起了效果。反正這玩意也是稍微理解了下,發現問題都出現在1987到1991年的四月,
這期間,這是一個神奇的問題。
   
   問題原因
     經過思考我發覺應該是時區的問題,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。是什麼類型用什麼類型。這裏改變不了上面的 問題,但是應該要按照實際需求對應
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章