关于时区,第1篇介绍了Linux操作系统的时区,以及如何修改。对于MySQL来说的时区来说,是依赖于操作系统的,具体情况下面讲会展开介绍。
本文内容包括以下几方面:数据库时区查看、数据库时区修改、修改时区的影响及验证。
关于查看
时区的查看,可以通过
mysql> show variables like '%zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | CST |
| time_zone | SYSTEM |
+------------------+--------+
2 rows in set (0.03 sec)TEXT
这里与时区有关的参数,只有system_time_zone
和time_zone
system_time_zone
:全局变量,无会话级别,服务器系统时区,数据库启动时加载的值,依赖服务器。time_zone
:全局变量,数据库时区,如果为SYSTEM
,表示使用system_time_zone的值,也可以单独设置。
这里可以看得出,如果time_zone设置为SYSTEM
,上面两个值是相等的。
关于修改
1. system_time_zone
修改系统的时区由CTS到UTC,数据库重启后:
mysql> show variables like '%zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | UTC |
| time_zone | SYSTEM |
+------------------+--------+
2 rows in set (0.03 sec)TEXT
这时数据库的时区是完全有操作系统时区决定的!!!
2. time_zone
这个参数是真正决定数据的时区的参数,可动态设置。
1️⃣ 有三种方式可以设置:
启动 mysqld_safe 时加选项
--timezone
,或mysqld启动选项中加--default-time-zone
(永久生效)在数据库参数文件中加
default-time-zone
(永久生效)在数据库启动后,通过
set global time_zone=
的命令(临时生效)。
建议使用第二种方式!
2️⃣ 下面简单的对以上方法进行实验:
(系统默认时区为UTC)
方式一:
🌳 在mysqld启动项中加--default-time-zone
-bash-4.2$ date
Mon Nov 28 16:56:01 UTC 2022
-bash-4.2$ mysqld --default-time-zone='+08:00'TEXT
再开一个窗口,查询
mysql> show variables like '%zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | UTC |
| time_zone | +08:00 |
+------------------+--------+
2 rows in set (0.01 sec)TEXT
🌳 在mysqld_safe中开启--timezone
选项
由于我是通过RPM安装的数据库,没有mysqld_safe工具,过程和mysqld是一样的,这里不再演示。
方式二:
1)修改数据库参数/etc/my.cnf
增加default-time-zone='+08:00'
2)重启数据库验证
[root@postgresql ~]# systemctl restart mysqld
[root@postgresql ~]# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.31 MySQL Community Server - GPL
Copyright (c) 2000, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show variables like '%zone%';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | UTC |
| time_zone | +08:00 |
+------------------+--------+
2 rows in set (0.02 sec)TEXT
方式三:
这里需要注意是否使用global,不使用则表示会话级,使用则是全局级。二者的区别就在于生效范围上。会话级只针对当前会话,全局级则是针对所有新连接生效,已连接则不受影响。
【例】全局设置
set global time_zone='+00:00';TEXT
通过如下语句可以验证。
mysql> select @@global.time_zone,@@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| +00:00 | +08:00 |
+--------------------+---------------------+
1 row in set (0.00 sec)TEXT
【例】会话级设置
set time_zone='+00:00';TEXT
通过如下语句可以验证。
mysql> select @@global.time_zone,@@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| +00:00 | +00:00 |
+--------------------+---------------------+
1 row in set (0.00 sec)TEXT
影响及验证
这一节包括两方面,方面一:对时间影响范围及验证。方面二:命名时区问题。方面三:对主从复制的影响
从官方文档上查阅到,如果要变更时区,会有如下影响:
会影响区域敏感的时间值的显示和存储。这包括
NOW()
或CURTIME()
等函数显示的值,以及存储在TIMESTAMP
列中并从中检索的值。TIMESTAMP
列的值从会话时区转换为UTC进行存储,从UTC转换为会话时区进行检索。不影响
UTC_TIMESTAMP()
等函数或DATE
、TIME
或DATETIME
列中的值显示的值。这些数据类型的值也不存储在UTC中;时区仅适用于从TIMESTAMP
值转换时。如果您想要DATE
、TIME
或DATETIME
值的区域设置特定算法,请将它们转换为UTC,执行算术,然后转换回来
1)先验证对函数NOW()、CURTIME()和UTC_TIMESTAMP()的影响:
mysql> select @@global.time_zone,@@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| +00:00 | +08:00 |
+--------------------+---------------------+
1 row in set (0.00 sec)
mysql> select now(),curtime(),utc_timestamp();
+---------------------+-----------+---------------------+
| now() | curtime() | utc_timestamp() |
+---------------------+-----------+---------------------+
| 2022-11-29 01:25:34 | 01:25:34 | 2022-11-28 17:25:34 |
+---------------------+-----------+---------------------+
1 row in set (0.00 sec)
mysql> set time_zone='+00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> select now(),curtime(),utc_timestamp();
+---------------------+-----------+---------------------+
| now() | curtime() | utc_timestamp() |
+---------------------+-----------+---------------------+
| 2022-11-28 17:25:50 | 17:25:50 | 2022-11-28 17:25:50 |
+---------------------+-----------+---------------------+
1 row in set (0.00 sec)TEXT
很明显,改变了会话的时区后,NOW()、CURTIME()的时间日期显示变化了,而UTC_TIMESTAMP()则不变。
2)再验证数据类型TIMESTAMP、DATE、TIME、DATETIME的影响。
mysql> select @@global.time_zone,@@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| +00:00 | +00:00 |
+--------------------+---------------------+
1 row in set (0.00 sec)
mysql> insert into tz values('2022-11-28 10:24:48','2022-11-28 10:24:48','2022-11-28 10:24:48','2022-11-28 10:24:48');
Query OK, 1 row affected, 2 warnings (0.04 sec)
mysql> show warnings;
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------+
| Note | 1292 | Incorrect date value: '2022-11-28 10:24:48' for column 'b' at row 1 |
| Note | 1292 | Incorrect time value: '2022-11-28 10:24:48' for column 'c' at row 1 |
+-------+------+---------------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> select * from tz;
+---------------------+------------+----------+---------------------+
| a | b | c | d |
+---------------------+------------+----------+---------------------+
| 2022-11-28 10:24:48 | 2022-11-28 | 10:24:48 | 2022-11-28 10:24:48 |
+---------------------+------------+----------+---------------------+
1 row in set (0.00 sec)
mysql> set time_zone='+08:00';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tz;
+---------------------+------------+----------+---------------------+
| a | b | c | d |
+---------------------+------------+----------+---------------------+
| 2022-11-28 18:24:48 | 2022-11-28 | 10:24:48 | 2022-11-28 10:24:48 |
+---------------------+------------+----------+---------------------+
1 row in set (0.00 sec)TEXT
这里建了一张表,包含了所涉及的时间类型,在时区发生变化后,只有timestamp类型是随着时区而变化的。也就证明这个类型确实是存在UTC中的。
3)命名时区问题,其实是时区的别名问题。
比如,CST是"+08:00"的命名时区。默认情况下,是无法直接使用CST来指定时区的,这需要在数据库中填充时区表。
mysql> SET time_zone = 'UTC';
ERROR 1298 (HY000): Unknown or incorrect time zone: 'UTC'TEXT
如何填充时区表?
一个前提:操作系统需要支持zoneinfo数据库,如:Linux、macOS、FreeBSD和Solaris都有的。windows没有,需要去官网下载
[root@postgresql ~]# mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -uroot -p123456 mysql
mysql: [Warning] Using a password on the command line interface can be insecure.
Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/leapseconds' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/tzdata.zi' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it.TEXT
这时,我们可以通过查询mysql.time_zone_name
时区表是否已经填充了
mysql> SELECT COUNT(*) FROM mysql.time_zone_name;
+----------+
| COUNT(*) |
+----------+
| 1780 |
+----------+
1 row in set (0.06 sec)
mysql> SELECT * FROM mysql.time_zone_name;
+----------------------------------------+--------------+
| Name | Time_zone_id |
+----------------------------------------+--------------+
| Africa/Abidjan | 1 |
| Africa/Accra | 2 |
| Africa/Addis_Ababa | 3 |
| Africa/Algiers | 4 |
| Africa/Asmara | 5 |
| Africa/Asmera | 6 |
| Africa/Bamako | 7 |
| Africa/Bangui | 8 |
| Africa/Banjul | 9 |
| Africa/Bissau | 10 |
| Africa/Blantyre | 11 |
| Africa/Brazzaville | 12 |
| Africa/Bujumbura | 13 |
| Africa/Cairo | 14 |
| Africa/Casablanca | 15 |
| Africa/Ceuta | 16 |
| Africa/Conakry | 17 |
...TEXT
这时再通过命名时区去设置
mysql> set time_zone='UTC';
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.time_zone,@@session.time_zone;
+--------------------+---------------------+
| @@global.time_zone | @@session.time_zone |
+--------------------+---------------------+
| +00:00 | UTC |
+--------------------+---------------------+
1 row in set (0.00 sec)TEXT
4)主从复制的影响
引用官方原文
默认情况下,源服务器和副本服务器假设它们位于同一时区。如果您在不同时区的服务器之间复制,则必须在源服务器和副本上设置时区。否则,根据源上的本地时间而定的语句不会正确复制,例如使用
NOW()
或FROM_UNIXTIME()
函数的语句。验证源和副本上系统时区(
system_time_zone
)、服务器当前时区(time_zone
的全局值)和每个会话时区(time_zone
的会话值)的设置组合是否产生正确的结果。特别是,如果time_zone
系统变量设置为值SYSTEM
,表明服务器时区与系统时区相同,这可能会导致源和副本应用不同的时区。
为了避免因时区导致的异常,对于国内大多数企业的业务仍多以国内为主。建议将数据库的时区进行明确设置,且不依赖操作系统的设置。