關(guān)于mysql的時區(qū)問題
查看及設(shè)置時區(qū)
查看時區(qū)
命令:
mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | SYSTEM | +------------------+--------+
返回:
system_time_zone:mysql啟動的時候,mysql服務(wù)所在服務(wù)器(主機)的時區(qū),windows系統(tǒng)返回為空,linux系統(tǒng)一般會返回CST,個人認(rèn)為,這里的CST表示的是中國標(biāo)準(zhǔn)時間(CST還可以表示其它某些地區(qū)的標(biāo)準(zhǔn)時間),有時可能會返回UTC(協(xié)調(diào)世界時),粗略理解為0時區(qū)時間(GMT,格林尼治標(biāo)準(zhǔn)時間),個人認(rèn)為,system_time_zone并不是很重要
time_zone:當(dāng)前會話使用的時區(qū),可以在配置文件中指定默認(rèn)值,如果不指定,默認(rèn)值是SYSTEM,表示使用system_time_zone指向的時區(qū),time_zone可以使用時調(diào)整,一些函數(shù)(比如:now())返回的值就和time_zone有關(guān),但一般不要輕易改變
配置文件設(shè)置time_zone默認(rèn)值
在my.ini文件(linux系統(tǒng)是my.cnf文件)的[mysqld]節(jié)點設(shè)置,設(shè)置后重啟服務(wù)
[mysqld] default-time-zone = '+0:00'
命令設(shè)置時區(qū)
mysql> set time_zone='+08:00'; Query OK, 0 rows affected (0.00 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec) mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +08:00 | +------------------+--------+ 2 rows in set (0.04 sec)
datetime和timestamp的區(qū)別
結(jié)論:datetime可以理解為保存的是一個字符串,切換時區(qū)后,不會有變化,timestamp可以理解為保存的是距離1970-01-01 08:00:00的時間差(將當(dāng)前時區(qū)的時間轉(zhuǎn)換成0時區(qū)的時間,然后計算與1970-01-01 08:00:00的時間差),切換時區(qū)后,顯示的是當(dāng)前時區(qū)所對應(yīng)的時間
創(chuàng)建一個表
CREATE TABLE `time_zone_test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `create_time` datetime NOT NULL, `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入數(shù)據(jù)并根據(jù)在不同時區(qū)查看數(shù)據(jù)
mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | |
| time_zone | SYSTEM |
+------------------+--------+
2 rows in set (0.03 sec)
mysql> INSERT INTO `test`.`time_zone_test` (`create_time`, `update_time`) VALUES ('2022-09-03 23:01:14', '2022-09-03 23:01:14');
Query OK, 1 row affected (0.01 sec)
mysql> select * from time_zone_test;
+----+---------------------+---------------------+
| id | create_time | update_time |
+----+---------------------+---------------------+
| 1 | 2022-09-03 23:01:14 | 2022-09-03 23:01:14 |
+----+---------------------+---------------------+
1 row in set (0.04 sec)
mysql> set time_zone='+08:00';
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | |
| time_zone | +08:00 |
+------------------+--------+
2 rows in set (0.03 sec)
mysql> select * from time_zone_test;
+----+---------------------+---------------------+
| id | create_time | update_time |
+----+---------------------+---------------------+
| 1 | 2022-09-03 23:01:14 | 2022-09-03 23:01:14 |
+----+---------------------+---------------------+
1 row in set (0.06 sec)
mysql> set time_zone='-05:00';
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | |
| time_zone | -05:00 |
+------------------+--------+
2 rows in set (0.05 sec)
mysql> select * from time_zone_test;
+----+---------------------+---------------------+
| id | create_time | update_time |
+----+---------------------+---------------------+
| 1 | 2022-09-03 23:01:14 | 2022-09-03 10:01:14 |
+----+---------------------+---------------------+
1 row in set (0.04 sec)
可以發(fā)現(xiàn)
- create_time的類型是datetime,切換時區(qū)后,顯示的時間不變,update_time的類型是timestamp,切換時區(qū)后,顯示的時間有變化
- 同時可以發(fā)現(xiàn),我的電腦默認(rèn)的時區(qū)就是 +08:00
mybatis連接mysql的問題
java程序,以springboot程序為例,使用mybatis操作mysql時,會涉及到時區(qū)轉(zhuǎn)換的問題
連接mysql并設(shè)置時區(qū)
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
username: xxxx
password: xxxx
driver-class-name: com.mysql.cj.jdbc.Driver
serverTimezone表示當(dāng)前會話的時區(qū),這句話應(yīng)該是錯誤的,但是現(xiàn)在大部分文章都這樣寫
serverTimezone表示的是希望用戶配置一個與mysql服務(wù)器默認(rèn)的time_zone相同的值,我們一般設(shè)置為serverTimezone=GMT%2B8或serverTimezone=Asia/Shanghai,因為我們的mysql服務(wù)器time_zone一般就是+08:00
serverTimezone的作用是將程序的時區(qū)與serverTimezone設(shè)置的時區(qū)做比較,然后將時區(qū)轉(zhuǎn)換后的時間字符串發(fā)送給mysql服務(wù)器,到mysql服務(wù)器上,使用的會話時區(qū)就是默認(rèn)的會話時區(qū),就是默認(rèn)查詢出來的time_zone的值
修改windows系統(tǒng)的時區(qū)(附加)
使用tzutil工具
C:\Users\yangs>tzutil /g China Standard Time C:\Users\yangs>tzutil /s "Greenwich Standard Time" C:\Users\yangs>tzutil /g Greenwich Standard Time
添加數(shù)據(jù)時,程序時區(qū)、serverTimezone時區(qū)不一致導(dǎo)致的時間轉(zhuǎn)換問題
實際這里涉及到三個時區(qū)
- 程序時區(qū):springboot的時區(qū)
serverTimezone設(shè)置的時區(qū)- mysql服務(wù)默認(rèn)的時區(qū)
time_zone
springboot程序默認(rèn)使用所在系統(tǒng)的時區(qū)
例1
springboot項目所在操作系統(tǒng)的時區(qū)為:(UTC+08:00) 北京,重慶,香港特別行政區(qū),烏魯木齊
連接mysql設(shè)置serverTimezone=GMT%2B9,(UTC+09:00) 大阪,札幌,東京
當(dāng)前時間:
- (UTC+08:00) 北京 10:30(電腦時間)
- (UTC+09:00) 大阪 11:30
mysql的默認(rèn)時區(qū):+08:00
代碼邏輯:獲取當(dāng)前時間并存入到數(shù)據(jù)庫(createTime、updateTime)
程序的入?yún)ⅲú榭创蛴〉娜罩荆?/strong>
JDBC Connection [HikariProxyConnection@886416306 wrapping com.mysql.cj.jdbc.ConnectionImpl@25baed04] will not be managed by Spring ==> Preparing: insert into time_zone_test (id, create_time, update_time ) values (?, ?, ? ) ==> Parameters: null, 2022-09-04 10:22:33.937(Timestamp), 2022-09-04 10:22:33.937(Timestamp) <== Updates: 1
日志打印的是當(dāng)前時間,但是這個并不是發(fā)給mysql的時間,發(fā)給mysql的時間會做時區(qū)轉(zhuǎn)換
發(fā)給mysql的時間:
可在org.apache.ibatis.executor.SimpleExecutor#doUpdate查看
HikariProxyPreparedStatement@1978182025 wrapping com.mysql.cj.jdbc.ClientPreparedStatement: insert into time_zone_test (id, create_time, update_time
)
values (null, '2022-09-04 11:22:33.937', '2022-09-04 11:22:33.937'
)
數(shù)據(jù)庫中查看:
mysql> set time_zone='+08:00'; Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +08:00 | +------------------+--------+ 2 rows in set (0.03 sec) mysql> select * from time_zone_test where id = 8; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 8 | 2022-09-04 11:22:34 | 2022-09-04 11:22:34 | +----+---------------------+---------------------+ 1 row in set (0.04 sec) mysql> set time_zone='+09:00'; Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +09:00 | +------------------+--------+ 2 rows in set (0.03 sec) mysql> select * from time_zone_test where id = 8; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 8 | 2022-09-04 11:22:34 | 2022-09-04 12:22:34 | +----+---------------------+---------------------+ 1 row in set (0.04 sec)
由此可以看出:
- springboot程序是
+08:00,serverTimezone設(shè)置的是+09:00,所以在組裝sql語句的時候會給時間+1 - 執(zhí)行sql語句時,就是在mysql服務(wù)器默認(rèn)時區(qū)(
+08:00)進行的,可以根據(jù)update_time的值判斷,如果是在+09:00執(zhí)行的,那么在+08:00查詢出來的時間就會少1小時,實際上并沒有 - 從2也能看出,
serverTimezone設(shè)置的值并不是當(dāng)前會話的時區(qū),只是告訴程序應(yīng)該怎么轉(zhuǎn)換這個時間
例2
springboot項目所在操作系統(tǒng)的時區(qū)為:(UTC+08:00) 北京,重慶,香港特別行政區(qū),烏魯木齊
連接mysql設(shè)置serverTimezone=GMT%2B9,(UTC+09:00) 大阪,札幌,東京
當(dāng)前時間:
- (UTC+08:00) 北京 16:40(電腦時間)
- (UTC+09:00) 大阪 17:40
mysql的默認(rèn)時區(qū):+08:00
代碼邏輯:使用now()函數(shù)存入到數(shù)據(jù)庫(createTime、updateTime)
發(fā)給mysql的語句:
HikariProxyPreparedStatement@764739765 wrapping com.mysql.cj.jdbc.ClientPreparedStatement: insert into time_zone_test (id, create_time, update_time
)
values (null, now(), now()
)
數(shù)據(jù)庫中查看:
mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | SYSTEM | +------------------+--------+ 2 rows in set (0.05 sec) mysql> select * from time_zone_test where id = 15; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 15 | 2022-09-04 16:45:30 | 2022-09-04 16:45:30 | +----+---------------------+---------------------+ 1 row in set (0.05 sec) mysql> set time_zone='+08:00'; Query OK, 0 rows affected (0.01 sec) mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +08:00 | +------------------+--------+ 2 rows in set (0.05 sec) mysql> select * from time_zone_test where id = 15; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 15 | 2022-09-04 16:45:30 | 2022-09-04 16:45:30 | +----+---------------------+---------------------+ 1 row in set (0.07 sec) mysql> set time_zone='+09:00'; Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +09:00 | +------------------+--------+ 2 rows in set (0.07 sec) mysql> select * from time_zone_test where id = 15; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 15 | 2022-09-04 16:45:30 | 2022-09-04 17:45:30 | +----+---------------------+---------------------+ 1 row in set (0.08 sec)
由此可以看出:
now()函數(shù)是在+08:00時區(qū)執(zhí)行的,與serverTimezone設(shè)置的值無關(guān) 例3
springboot項目所在操作系統(tǒng)的時區(qū)為:(UTC+00:00) 蒙羅維亞,雷克雅未克
連接mysql設(shè)置serverTimezone=GMT%2B8,(UTC+08:00) 北京,重慶,香港特別行政區(qū),烏魯木齊
當(dāng)前時間:
- (UTC+00:00) 蒙羅維亞 9:00(電腦時間)
- (UTC+08:00) 北京 17:00
mysql的默認(rèn)時區(qū):+00:00
代碼邏輯:獲取當(dāng)前時間并存入到數(shù)據(jù)庫(createTime、updateTime)
程序的入?yún)ⅲú榭创蛴〉娜罩荆?/strong>
JDBC Connection [HikariProxyConnection@1876572082 wrapping com.mysql.cj.jdbc.ConnectionImpl@584e44f5] will not be managed by Spring ==> Preparing: insert into time_zone_test (id, create_time, update_time ) values (?, ?, ? ) ==> Parameters: null, 2022-09-04 09:10:41.157(Timestamp), 2022-09-04 09:10:41.157(Timestamp) <== Updates: 1
日志打印的是當(dāng)前時間,但是這個并不是發(fā)給mysql的時間,發(fā)給mysql的時間會做時區(qū)轉(zhuǎn)換
發(fā)給mysql的時間:
HikariProxyPreparedStatement@419867077 wrapping com.mysql.cj.jdbc.ClientPreparedStatement: insert into time_zone_test (id, create_time, update_time
)
values (null, '2022-09-04 17:10:41.157', '2022-09-04 17:10:41.157'
)
數(shù)據(jù)庫中查看:
mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +00:00 | +------------------+--------+ 2 rows in set (0.05 sec) mysql> select * from time_zone_test where id = 16; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 16 | 2022-09-04 17:10:41 | 2022-09-04 17:10:41 | +----+---------------------+---------------------+ 1 row in set (0.05 sec) mysql> set time_zone='+08:00'; Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +08:00 | +------------------+--------+ 2 rows in set (0.07 sec) mysql> select * from time_zone_test where id = 16; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 16 | 2022-09-04 17:10:41 | 2022-09-05 01:10:41 | +----+---------------------+---------------------+ 1 row in set (0.09 sec)
由此可以看出:
- springboot程序是
+00:00,serverTimezone設(shè)置的是+08:00,所以在組裝sql語句的時候會給時間+8 - 執(zhí)行sql語句時,在mysql服務(wù)器默認(rèn)時區(qū)(
+00:00)進行的,將發(fā)送過來的17點,在+00:00時區(qū)執(zhí)行保存 - 將時區(qū)調(diào)整為
+08:00進行查詢,會發(fā)現(xiàn)update_time時間又增加了8,這是正確的
例4
springboot項目所在操作系統(tǒng)的時區(qū)為:(UTC+00:00) 蒙羅維亞,雷克雅未克
連接mysql設(shè)置serverTimezone=GMT%2B8,(UTC+08:00) 北京,重慶,香港特別行政區(qū),烏魯木齊
當(dāng)前時間:
- (UTC+00:00) 蒙羅維亞 9:30(電腦時間)
- (UTC+08:00) 北京 17:30
mysql的默認(rèn)時區(qū):+00:00
代碼邏輯:使用now()函數(shù)存入到數(shù)據(jù)庫(createTime、updateTime)
發(fā)給mysql的語句:
HikariProxyPreparedStatement@1333630381 wrapping com.mysql.cj.jdbc.ClientPreparedStatement: insert into time_zone_test (id, create_time, update_time
)
values (null, now(), now()
)
數(shù)據(jù)庫中查看:
mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +00:00 | +------------------+--------+ 2 rows in set (0.03 sec) mysql> select * from time_zone_test where id = 17; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 17 | 2022-09-04 09:30:26 | 2022-09-04 09:30:26 | +----+---------------------+---------------------+ 1 row in set (0.06 sec) mysql> set time_zone='+08:00'; Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | | | time_zone | +08:00 | +------------------+--------+ 2 rows in set (0.06 sec) mysql> select * from time_zone_test where id = 17; +----+---------------------+---------------------+ | id | create_time | update_time | +----+---------------------+---------------------+ | 17 | 2022-09-04 09:30:26 | 2022-09-04 17:30:26 | +----+---------------------+---------------------+ 1 row in set (0.06 sec)
由此可以看出:
now()函數(shù)是在+00:00時區(qū)執(zhí)行的,與serverTimezone設(shè)置的值無關(guān)
查詢數(shù)據(jù)時,程序時區(qū)、serverTimezone時區(qū)不一致導(dǎo)致的時間轉(zhuǎn)換問題
查詢數(shù)據(jù)時,也會對時間類型的數(shù)據(jù)進行轉(zhuǎn)換
例5
環(huán)境與例4一致,進行查詢操作
從mysql查詢回來的數(shù)據(jù)(查看打印的日志):
JDBC Connection [HikariProxyConnection@1625710104 wrapping com.mysql.cj.jdbc.ConnectionImpl@109c4ff6] will not be managed by Spring ==> Preparing: select id, create_time, update_time from time_zone_test where id = ? ==> Parameters: 17(Integer) <== Columns: id, create_time, update_time <== Row: 17, 2022-09-04 09:30:26, 2022-09-04 09:30:26 <== Total: 1
實際展示的數(shù)據(jù)(接口進行查詢操作)
{
"id": 17,
"createTime": "2022-09-04T01:30:26.000+00:00",
"updateTime": "2022-09-04T01:30:26.000+00:00"
}
由此可以看出:
- mysql從
+00:00時區(qū)查詢出數(shù)據(jù)給到springboot程序 - 由于設(shè)置了
serverTimezone=GMT%2B8,程序會認(rèn)為這是+08:00時區(qū)的數(shù)據(jù),程序本身的時區(qū)是+00:00,所以轉(zhuǎn)換成java對象數(shù)據(jù)時進行了-8操作,展示的數(shù)據(jù)也是-8后的結(jié)果 - 添加數(shù)據(jù)時,是
9:30 +00:00,查出來的數(shù)據(jù)是1:30 +00:00,這明顯是不一致的,原因就在于serverTimezone設(shè)置的時區(qū)與mysql默認(rèn)的時區(qū)不一致,其實使用now()函數(shù),可以理解為保存的數(shù)據(jù)庫中的數(shù)據(jù)是準(zhǔn)確的。如果由程序生成時間,那么發(fā)送給mysql時會發(fā)生時間轉(zhuǎn)換,此時保存到mysql中的數(shù)據(jù)就可以理解為是不準(zhǔn)確的,即使查詢展示的數(shù)據(jù)可能是對的
如果進行格式化,顯示+08:00的時間
配置:
spring:
jackson:
time-zone: GMT+8
顯示:
{
"id": 17,
"createTime": "2022-09-04T09:30:26.000+08:00",
"updateTime": "2022-09-04T09:30:26.000+08:00"
}
總結(jié)
避免數(shù)據(jù)錯誤,serverTimezone設(shè)置的值一定要和mysql默認(rèn)的時區(qū)一致(可以通過配置文件指定)
不要輕易切換mysql的默認(rèn)時區(qū),如果程序不對應(yīng)修改serverTimezone的值會造成數(shù)據(jù)錯誤
建議mysql默認(rèn)時區(qū)使用+08:00時區(qū),datetime類型的數(shù)據(jù)實際可以理解為是字符串,是不變的,比如,保存數(shù)據(jù)時,使用的是+00:00時區(qū),保存的就是+00:00時區(qū)的對應(yīng)的值,雖然如果配置正確,程序可以轉(zhuǎn)換成我們想要的+08:00時區(qū)的值,但是畢竟數(shù)據(jù)庫中存儲的就是當(dāng)時+00:00時區(qū)的對應(yīng)的值,所以還是建議mysql默認(rèn)時區(qū)使用+08:00時區(qū),儲存的時間就是我們用的時間
程序所在的時區(qū)應(yīng)該可以任意,但是用戶輸入的時間類型的數(shù)據(jù)就需要特殊關(guān)注,比如,程序的時區(qū)是+00:00時區(qū),用戶輸入的數(shù)據(jù)是+08:00時區(qū)數(shù)據(jù),那就得明確告訴程序用戶輸入的是+08:00時區(qū)的數(shù)據(jù)。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
MySQL數(shù)據(jù)庫遷移到Oracle數(shù)據(jù)庫的完整步驟記錄
在研發(fā)過程中可能會用到將表數(shù)據(jù)庫中的表結(jié)構(gòu)及數(shù)據(jù)遷移到另外一種數(shù)據(jù)庫中,比如說從mysql中遷移到oracle中,這篇文章主要給大家介紹了關(guān)于MySQL數(shù)據(jù)庫遷移到Oracle數(shù)據(jù)庫的完整步驟,需要的朋友可以參考下2024-06-06
MySQL 5.7并發(fā)復(fù)制隱式bug實例分析
這篇文章主要給大家介紹了關(guān)于MySQL 5.7并發(fā)復(fù)制隱式bug的相關(guān)資料,文中介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用mysql5.7具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11
MySql用DATE_FORMAT截取DateTime字段的日期值
MySql截取DateTime字段的日期值可以使用DATE_FORMAT來格式化,使用方法如下2014-08-08
MySQL因大事務(wù)導(dǎo)致的Insert慢實例分析
這篇文章主要給大家介紹了關(guān)于MySQL因大事務(wù)導(dǎo)致Insert慢的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10

