首頁 > 軟體

SpringBoot中 Jackson 日期的時區和日期格式問題解決

2022-12-11 14:01:44

1、使用場景

        因為最近專案需要國際化,需要能夠支援多種國際化語言,目前需要支援三種(法語、英語、簡體中文)。我們的專案部署環境為使用阿里雲(德國)節點。以前我們專案主要使用者在中國國內。一部署到德國伺服器節點就出現一個問題了。資料返回的時間不對,儲存的時間也有時差了。此文章僅僅與UTC標準時間與北京時間(東八區時間)作為實際例子。最後跟蹤發現,如下幾點原因:

1.1、資料庫MySQL也是有時區的概念的。

     通過如下命令可以查詢MySQL的時區

 show variables like "%time_zone%";

如果未做任何修改會顯示如下:

+------------------+--------+
| Variable_name  | Value |
+------------------+--------+
| system_time_zone | CST  |
| time_zone    | SYSTEM |
+------------------+--------+
2 rows in set (0.00 sec)
#time_zone說明mysql使用system的時區,system_time_zone說明system使用CST時區

1.2、系統時區意味著與計算機的作業系統時區相同。

以下是我們北京時間使用MySQL時區

 set global time_zone = '+8:00'; ##修改mysql全域性時區為北京時間,即我們所在的東8區
> set time_zone = '+8:00'; ##修改當前對談時區
> flush privileges; #立即生效

或者通過修改MySQL的 my.cnf組態檔來修改時區
# vim /etc/my.cnf ##在[mysqld]區域中加上
default-time_zone = '+8:00'
# /etc/init.d/mysqld restart ##重啟mysql使新時區生效

1.2、SpringBoot預設的Json檢視轉換框架Jackson也有時區概念

     Jackson日期反序列化時區問題,Jackson裡 JsonFormat的原始碼

/**
 * Value that indicates that default {@link java.util.TimeZone}
 * (from deserialization or serialization context) should be used:
 * annotation does not define value to use.
 *<p>
 * NOTE: default here does NOT mean JVM defaults but Jackson databindings
 * default, usually UTC, but may be changed on <code>ObjectMapper</code>.
 */
public final static String DEFAULT_TIMEZONE = "##default";
 
值,該值指示預設{@連結java.util.TimeZone}
 
(來自反序列化或序列化上下文)應使用:
 
註釋未定義要使用的值。
 
注意:這裡的預設值並不意味著JVM預設值,而是Jackson資料繫結。
 
預設值,通常為UTC,但可以在<code>objectmapper<code>上更改。

那個註釋已經說明預設情況下會將 時區設定為UTC ,Jackson反序列化時間型別的底層實際上呼叫的是Java的 SimpleDateFormat#parse() 方法,而JVM中的時區則會根據你的作業系統來獲取,所以JVM認為你的時區應該是 GMT+8 時區,
而要將 UTC 時區的時間轉成 GMT+8 時區的時間,就會將你傳進來的時間+8個小時。

2、程式碼實現

2.1、解決方案一下在每個帶有日期地方加上註解

日期型別的欄位上的 @JsonFormat 加上屬性 timezone="GMT+8"

@NotNull
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
@JsonProperty("start_time")
private Date startTime;

此類方法需要在包含日期地方都加上註解屬性,比較繁瑣。

2.2、只需要設定一個bean實現整體修改

@Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
        return jacksonObjectMapperBuilder ->
                jacksonObjectMapperBuilder.timeZone(TimeZone.getTimeZone("GMT+8"));
    }

參考文章:Jackson日期反序列化時區問題

2.3、使用SpringBoot的設定方式

spring.jackson.default-property-inclusion=NON_NULL
spring.jackson.time-zone=GMT+8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

第一個設定就是使用24小時的時間格式;第二個設定就是設定時區為東八區。

但是設定時候需要注意

Finally, if you opt out of the Spring Boot default MVC configuration by providing your own @EnableWebMvc configuration, you can take control completely and do everything manually by using getMessageConverters from WebMvcConfigurationSupport.

《Spring Boot Reference Guide》 

需要去掉

@EnableWebMvc

參考文章:

SpringBoot升級指定jackson版本的問題

SpringBoot利用jackson格式化時間的三種方法

java如何利用FastJSON、Gson、Jackson三種Json格式工具自定義時間序列化

3、成果展現

        經過轉換設定轉換之後;能夠正確的獲得和存取對應的時間,建議使用

        2.2、只需要設定一個bean實現整體修改 或者使用 2.3、使用SpringBoot的設定方式 方式比較通用性。

其中如果使用FastJson轉換也可能遇見同樣的問題,可以參考文章

springmvc fastjson 反序列化時間格式化方法(推薦)

4、總結

       在SpringBoot開發應用之中設計日期轉換需要特別注意,能夠主要目標能夠是的儲存和返回的日期能夠對應上自己的本地時區。同時能夠使用較為通用的轉換方式解決相關問題。


IT145.com E-mail:sddin#qq.com