一、如何設置 mysql 時區
1、命令
1)查時區:show variables like '%time_zone%'
返回有2行記錄,要看time_zone變量的值,不需要看system_time_zone。
若值爲SYSTEM表示取值跟system_time_zone保持一致。
system_time_zone的值是啓動mysql服務的時候讀取了操作系統的值,除非重新啓動mysql服務重讀否則這個值不變
還有一種查時區的方法,
select @@GLOBAL.time_zone,@@SESSION.time_zone
可以查出全局的時區以及會話時區。
2)設置會話時區:set time_zone='+8:00'
僅對當前會話有效,在當前窗口立即生效,關閉會話窗口後設置失效。無需重新登錄會話生效,也無需關閉窗口再開窗口
-
執行後如果不確定是否設置成功,可以用上面提到過的語句查查看
-
允許取值:
'+08:00'
,兼容了多0As a string indicating an offset from UTC of the form
[*
H*]*
H*:*
MM*
, prefixed with a+
or-
, such as'+10:00'
,'-6:00'
, or'+05:30'
. A leading zero can optionally be used for hours values less than 10; MySQL prepends a leading zero when storing and retriving the value in such cases. -
允許取值SYSTEM:
set time_zone='SYSTEM'
3)設置全局時區:set global time_zone='+8:00
全局會話有效。必須重新連接才生效(比如exit後重新mysql -uroot -p
進行連接)。無需重啓mysql服務,重啓 mysql 服務後丟失。
網上貼的是兩個語句,需要 flush privileges,但是實際測試即使flush了也還是需要重新連接會話纔會生效,而且看了下官網,沒flush的語句。而且實測不需要flush只需要重連
4)修改 mysql 的配置文件永久設置時區
需要重啓 mysql 服務後才能生效,這個對比上面的全局設置,即使服務重啓也是能保持配置。配置後跟數據庫所在的操作系統的時區就獨立開了。
// 下面是5.7的mysql配置,我看了一下8.x版本的mysql也是同樣的配置,不過mysql 8.x 默認就是 utf8mb4了,所以字符設置的那行就不需要了
// 配置的位置,無論5.7還是8.x版本,都必須配在 [mysqld] 下面
[mysqld]
default-time-zone=+08:00
character-set-server=utf8mb4
2、如何查看並讀懂這些命令
1)、解讀查時區的命令返回的結果
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | CST |
| time_zone | +08:00 |
+------------------+--------+
-
要知道mysql當前在什麼時區,看哪個變量?
看 time_zone。不看 system_time_zone。如要修改時區,直接修改 time_zone,無視 system_time_zone
-
time_zone 的值如果是 SYSTEM 表示什麼?
表示跟 system_time_zone 取值一樣。安裝MySQL後默認就是SYSTEM。
有些地方會表述成 SYSTEM 的含義是 "時區跟隨操作系統","跟隨操作系統和跟隨system_time_zone" 其實是一樣的意思,因爲 system_time_zone 就是啓動mysql服務的時候讀取了操作系統的時區的值。
-
建議time_zone不要設置成 SYSTEM
因爲如果 system_time_zone 的值是CST,CST被Java認爲是美國的時間,造成混亂。參考後面由此引起的bug
-
system_time_zone 的值是怎麼來的?
它的值來自mysql服務啓動時讀取操作系統時區,讀取後即使修改操作系統的時區,它的值也不會再改變了,除非重啓mysql 服務變量重新讀取
-
system_time_zone 的值能改變嗎?
不能通過命令改變
mysql> set system_time_zone='JST'; ERROR 1238 (HY000): Variable 'system_time_zone' is a read only variable
-
如何確定CST代表什麼時區?
由於中國和美國的時區同名,要知道CST究竟代表什麼時區,最簡單的方法是
select now()
跟你手機的時間對比一下
3、探討一個問題
如果OS是東八區,mysql服務起來了,time_zone值是SYSTEM,system_time_zone值是CST(東八區),此時連上mysql獲取的時間是東八區的,接着修改OS的時區爲東九區,斷開mysql連接的會話並重新連接會話,問此時獲取的時間是什麼時區的? (實測還是東八區)
這個問題的本質就是:time_zone的SYSTEM的值的含義,究竟是跟隨啓動mysql服務就確定下來的system_time_zone的值呢? 還是跟隨操作系統的變化而變化。
實際測試,是跟隨system_time_zone變量的變化而變化,而非系統,也就是說time_zone是SYSTEM值,只跟system_time_zone變量有關。而system_time_zone僅僅是啓動mysql服務的時候操作系統的一個時區的快照值而已(那一瞬間的值)
4、噁心的CST(修改time_zone改成非SYSTEM!)
CST同名的有4個時區
- Central Standard Time (USA) UT-6:00 美國標準時間
- Central Standard Time (Australia) UT+9:30 澳大利亞標準時間
- China Standard Time UT+8:00 中國標準時間
- Cuba Standard Time UT-4:00 古巴標準時間
這個不僅僅是重名的問題,而且在某些情況下會造成bug,詳細看另一篇博文。這裏簡單說一下
CST 時區是個非常坑的概念,因爲在 mysql 裏被理解爲 China Standard Time
(GMT+8),但是在Java裏被理解爲Central Standard Time (USA)
(GMT-6),這就是造成坑的原因。解決辦法是mysql就別用CST時區,改成 +08:00
以免造成誤解。(肯定改mysql啦,你改得了jdk源碼嗎?)
如果mysql的time_zone變量是SYSTEM,而system_time_zone是CST的值,system_time_zone的CST這個字符串會造成bug。
mysql的jdbc驅動的代碼裏會設置時區,這個時區是通過 TimeZone.getTimeZone(canonicalTimezone)
讀取,其中 canonicalTimezone
是字符串, TimeZone.getTimeZone("CST")
返回-6時區,即美國的時區。
解決辦法:
- 數據庫設置time_zone的值爲非SYSTEM,比如+08:00
- spring/springboot等程序連接的時候,jdbcUrl帶上時區,比如
jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
參考資料
https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html
https://dev.mysql.com/doc/refman/5.7/en/datetime.html