深入詳解SpringBoot中時(shí)區(qū)問題解決與配置方案
在日常開發(fā)中,時(shí)區(qū)問題常常像潛伏的地雷,平時(shí)看似沒事,一旦跨地區(qū)、跨服務(wù)器或跨系統(tǒng)部署,就可能出現(xiàn)日志錯(cuò)亂、數(shù)據(jù)庫(kù)時(shí)間偏移、定時(shí)任務(wù)不準(zhǔn)等問題。作為程序員,你必須了解 Spring Boot 應(yīng)用中 時(shí)區(qū)配置的各個(gè)層面,才能避免凌晨三點(diǎn)被時(shí)間問題追著哭。
本文我將從服務(wù)器、Spring Boot、數(shù)據(jù)庫(kù)、容器以及前端交互五個(gè)方面詳細(xì)講解,順便分享常用時(shí)區(qū)對(duì)照表和踩坑經(jīng)驗(yàn)。
1. 服務(wù)器時(shí)區(qū):?jiǎn)栴}的源頭
服務(wù)器時(shí)區(qū)是所有時(shí)間問題的起點(diǎn)。很多開發(fā)者只關(guān)注應(yīng)用代碼,卻忽略了服務(wù)器系統(tǒng)的時(shí)區(qū)配置。
- Linux 系統(tǒng):時(shí)區(qū)信息存放在
/etc/localtime,可以通過timedatectl查看和修改。 - Windows 系統(tǒng):使用系統(tǒng)自帶時(shí)區(qū)設(shè)置,但在遠(yuǎn)程服務(wù)器或容器中可能與開發(fā)環(huán)境不一致。
踩坑案例:我曾經(jīng)遇到過一個(gè)定時(shí)任務(wù),每天凌晨 0 點(diǎn)在開發(fā)機(jī)上執(zhí)行正常,但部署到美國(guó)東部的服務(wù)器后,實(shí)際執(zhí)行時(shí)間變成了前一天的上午 11 點(diǎn),因?yàn)榉?wù)器默認(rèn)時(shí)區(qū)是 CST(美國(guó)中部時(shí)間),而開發(fā)機(jī)是北京時(shí)間。
解決方法有兩種,一是修改服務(wù)器時(shí)區(qū),而是修改 JVM 時(shí)區(qū)。
# 查看當(dāng)前服務(wù)器時(shí)區(qū) timedatectl # 設(shè)置為北京時(shí)間(Asia/Shanghai) sudo timedatectl set-timezone Asia/Shanghai
當(dāng)然很多時(shí)候,服務(wù)器的時(shí)區(qū)不是我們能改得,我們就只能通過下面方式修改 JVM 時(shí)區(qū)。
同時(shí),JVM 啟動(dòng)時(shí)也要指定時(shí)區(qū):
java -jar -Duser.timezone=Asia/Shanghai app.jar
這樣,服務(wù)器系統(tǒng)時(shí)間和 JVM 默認(rèn)時(shí)間就統(tǒng)一,避免時(shí)間偏差。
2. Spring Boot 全局時(shí)區(qū)配置
即使服務(wù)器時(shí)區(qū)正確,如果 Spring Boot 沒有顯式指定時(shí)區(qū),部分組件仍然可能使用 JVM 默認(rèn)時(shí)區(qū),導(dǎo)致日志、定時(shí)任務(wù)或數(shù)據(jù)序列化出現(xiàn)偏差。
方法1 全局默認(rèn)時(shí)區(qū)
在啟動(dòng)類中設(shè)置全局時(shí)區(qū):
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
// 設(shè)置全局默認(rèn)時(shí)區(qū)
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
SpringApplication.run(MyApplication.class, args);
}
}
注意:TimeZone.setDefault 必須在 SpringApplication.run 前調(diào)用,否則應(yīng)用組件初始化時(shí)可能仍使用 JVM 原始時(shí)區(qū)。
方法2 Jackson 序列化時(shí)區(qū)
Spring Boot 默認(rèn)使用 Jackson 將 Java 日期對(duì)象序列化為 JSON,如果不設(shè)置時(shí)區(qū),返回給前端的時(shí)間可能是 CST(美國(guó)時(shí)間),導(dǎo)致用戶看到的時(shí)間不對(duì)。
配置方法:
spring:
jackson:
time-zone: Asia/Shanghai
或者在代碼中:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
return mapper;
}
3. 數(shù)據(jù)庫(kù)時(shí)區(qū):MySQL 配置
數(shù)據(jù)庫(kù)是時(shí)區(qū)問題的高發(fā)區(qū)。MySQL 的 TIMESTAMP 類型會(huì)自動(dòng)根據(jù)服務(wù)器時(shí)區(qū)進(jìn)行存儲(chǔ)轉(zhuǎn)換,如果 JDBC 連接沒有指定時(shí)區(qū),寫入的數(shù)據(jù)就可能與預(yù)期不一致。
方法1 數(shù)據(jù)庫(kù)層配置
# 查看當(dāng)前時(shí)區(qū) SELECT @@global.time_zone, @@session.time_zone; # 設(shè)置數(shù)據(jù)庫(kù)時(shí)區(qū)為北京時(shí)間 SET GLOBAL time_zone = '+08:00'; SET time_zone = '+08:00';
方法2 JDBC 層配置
Spring Boot 配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/testdb?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf-8
username: root
password: 123456
serverTimezone 參數(shù)指定 JDBC 連接使用的時(shí)區(qū),避免報(bào) The server time zone value is unrecognized 錯(cuò)誤,并確保寫入和讀取的時(shí)間一致。
踩坑案例:有一次,我的定時(shí)任務(wù)寫入數(shù)據(jù)庫(kù)的 LocalDateTime,在開發(fā)機(jī)上顯示正確,但生產(chǎn)數(shù)據(jù)庫(kù)顯示早了 8 小時(shí),原因是生產(chǎn)服務(wù)器和 MySQL 容器默認(rèn)時(shí)區(qū)是 CST(美國(guó)時(shí)間)。
4. 容器化部署時(shí)區(qū)配置
在 Docker 或 Kubernetes 環(huán)境中,容器默認(rèn)使用鏡像系統(tǒng)的時(shí)區(qū),和宿主機(jī)或 JVM 時(shí)區(qū)可能不同,因此必須顯式配置。
方法1 Docker 環(huán)境變量
environment: - TZ=Asia/Shanghai - JAVA_OPTS=-Duser.timezone=Asia/Shanghai
方法2 掛載宿主機(jī)時(shí)區(qū)
volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro
優(yōu)點(diǎn):確保容器和宿主機(jī)時(shí)區(qū)一致,避免跨容器時(shí)間偏差。
方法3 Dockerfile 內(nèi)固定時(shí)區(qū)
FROM openjdk:17-jdk-slim ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone COPY target/myapp.jar /app.jar ENTRYPOINT ["java","-Duser.timezone=Asia/Shanghai","-jar","/app.jar"]
5. 常用時(shí)區(qū)對(duì)照
在 Spring Boot、數(shù)據(jù)庫(kù)、Docker 等場(chǎng)景中,推薦使用 IANA 時(shí)區(qū) ID,避免縮寫歧義。
中國(guó)及東亞
| 時(shí)區(qū) ID | 描述 | UTC 偏移 |
|---|---|---|
| Asia/Shanghai | 北京時(shí)間 | UTC+08:00 |
| Asia/Urumqi | 烏魯木齊時(shí)間 | UTC+06:00 |
| Asia/Tokyo | 日本時(shí)間 | UTC+09:00 |
北美
| 時(shí)區(qū) ID | 描述 | UTC 偏移 |
|---|---|---|
| America/New_York | 美國(guó)東部時(shí)間 | UTC-05:00 / UTC-04:00 |
| America/Chicago | 美國(guó)中部時(shí)間 | UTC-06:00 / UTC-05:00 |
歐洲
| 時(shí)區(qū) ID | 描述 | UTC 偏移 |
|---|---|---|
| Europe/London | 英國(guó)時(shí)間 | UTC+0 / UTC+1 |
| Europe/Paris | 法國(guó)時(shí)間 | UTC+1 / UTC+2 |
5. 總結(jié)
時(shí)區(qū)配置雖然看似簡(jiǎn)單,但涉及的層面很多,需要在操作系統(tǒng)、JVM、Spring Boot應(yīng)用、數(shù)據(jù)庫(kù)等各個(gè)層面保持一致。
到此這篇關(guān)于深入詳解SpringBoot中時(shí)區(qū)問題解決與配置方案的文章就介紹到這了,更多相關(guān)SpringBoot時(shí)區(qū)配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot常用的參數(shù)驗(yàn)證技巧和使用方法
Spring Boot是一個(gè)使用Java編寫的開源框架,用于快速構(gòu)建基于Spring的應(yīng)用程序,這篇文章主要介紹了Spring?Boot常用的參數(shù)驗(yàn)證技巧和使用方法,需要的朋友可以參考下2023-09-09
SpringBoot自定義注解實(shí)現(xiàn)Token校驗(yàn)的方法
這篇文章主要介紹了SpringBoot自定義注解實(shí)現(xiàn)Token校驗(yàn)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Java JDK11基于嵌套的訪問控制的實(shí)現(xiàn)
這篇文章主要介紹了Java JDK11基于嵌套的訪問控制的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2019-01-01
Java實(shí)現(xiàn)SHA1加密代碼實(shí)例
這篇文章給大家分享了Java實(shí)現(xiàn)SHA1加密的相關(guān)實(shí)例代碼,有興趣的朋友可以測(cè)試參考下。2018-07-07
解決IDEA2020.1.2IDEA打不開的問題(最新分享)
由于idea安裝多了某個(gè)jar,點(diǎn)擊出現(xiàn)讀條后閃退情況,接下來(lái)通過本文給大家分享解決IDEA2020.1.2IDEA打不開的問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,感興趣的朋友跟隨小編一起看看吧2020-07-07
解決SpringBoot jar包中的文件讀取問題實(shí)現(xiàn)
這篇文章主要介紹了解決SpringBoot jar包中的文件讀取問題實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
java web中圖片驗(yàn)證碼功能的簡(jiǎn)單實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇java web 驗(yàn)證碼的簡(jiǎn)單實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2016-06-06

