MySQL服务器时区支持
MySQL维护的时区设置,如何加载命名时间支持所需的系统表,如何在时区更改时保持最新状态以及如何启用leap秒支持。
从MySQL 8.0.19开始,插入的datetime值也支持时区偏移量。有关更多信息,请参见“ DATE,DATETIME和TIMESTAMP类型”。
- 时区变量
- 填充时区表
- 与时区变化保持同步
- 时区闰秒支持
有关复制设置中时区设置的信息,请参见“MySQL复制数据”和“复制和时区”。
时区变量
MySQL Server维护几个时区设置:
系统时区。服务器启动时,它将尝试自动确定主机的时区,并使用它来设置
system_time_zone
系统变量。此后该值不变。要在启动时为MySQL Server明确指定系统时区,请在启动mysqld
TZ
之前设置环境变量。如果使用mysqld_safe启动服务器,则其选项提供了另一种设置系统时区的方法。对于允许值和是取决于系统。请查阅操作系统文档,以了解可以接受的值。--timezone
TZ
--timezone
服务器当前时区。全局
time_zone
系统变量指示服务器当前正在运行的时区。初始time_zone
值为'SYSTEM'
,指示服务器时区与系统时区相同。注意
如果设置为
SYSTEM
,则每个需要时区计算的MySQL函数调用都会进行系统库调用以确定当前系统时区。此调用可能受到全局互斥锁的保护,从而导致争用。初始全局服务器时区值可以在启动时通过
--default-time-zone
命令行中的选项显式指定,也可以在选项文件中使用以下行:default-time-zone='timezone'
如果具有
SYSTEM_VARIABLES_ADMIN
或SUPER
特权,则可以使用以下语句在运行时设置全局服务器时区值:SET GLOBAL time_zone = timezone;每会话时区。每个连接的客户端都有自己的会话时区设置,由会话
time_zone
变量指定。最初,会话变量从全局变量获取其值time_zone
,但是客户端可以使用以下语句更改其自己的时区:SET time_zone = timezone;
会话时区设置会影响对时区敏感的时间值的显示和存储。这包括由诸如NOW()
或的函数显示的值CURTIME()
,以及存储在TIMESTAMP
列中并从列中检索的值。TIMESTAMP
列的值从会话时区转换为UTC以进行存储,并从UTC转换为会话时区以进行检索。
会话时区设置不会影响功能显示的UTC_TIMESTAMP()
值DATE
,例如TIME
,或DATETIME
列中的或值。这些数据类型中的值也不会存储在UTC中;时区仅在从TIMESTAMP
值转换时适用。如果要针对DATE
,TIME
或DATETIME
值进行语言环境特定的算术,请将其转换为UTC,执行该算术,然后再转换回去。
可以像这样检索当前的全局和会话时区值:
SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;
timezone
值可以以几种格式给出,都不区分大小写:
- 作为值
'SYSTEM'
,指示服务器时区与系统时区相同。 作为表示一个字符串的从表单的UTC偏移,带有前缀或,例如,,或。前导零可用于小于10的小时值;在这种情况下,MySQL在存储和检索该值时会加上前导零。MySQL的转换或者到。
[H]H:MM
+
-
' +10:00'
'-6:00'
' +05:30'
'-00:00'
'-0:00'
' +00:00'
在此之前的MySQL 8.0.19,该值必须在范围
'-12:59'
到' +13:00'
,包容性;与MySQL 8.0.19开始,允许范围为'-14:00'
到' +14:00'
,包容性。- 作为命名的时区,例如
'Europe/Helsinki'
,'US/Eastern'
或'MET'
。仅当mysql
已经创建并填充了数据库中的时区信息表时,才能使用命名时区。
填充时区表
mysql
系统架构中存在几个表来存储时区信息(请参见“ mysql系统架构”)。MySQL安装过程会创建时区表,但不会加载它们。为此,请按照以下说明进行操作。
注意加载时区信息不一定是一次性操作,因为该信息会偶尔更改。当发生此类更改时,使用旧规则的应用程序将过时,您可能会发现有必要重新加载时区表,以使MySQL服务器使用的信息保持最新。请参阅时区更改以保持最新。
如果您的系统有自己的 zoneinfo数据库(描述时区的文件集),请使用mysql_tzinfo_to_sql程序加载时区表。此类系统的示例是Linux,macOS,FreeBSD和Solaris。这些文件的一个可能位置是/usr/share/zoneinfo
目录。如果您的系统没有zoneinfo数据库,则可以使用可下载的程序包,如本节后面所述。
要从命令行加载时区表,请将zoneinfo目录路径名传递给mysql_tzinfo_to_sql并将输出发送到mysql程序。例如:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
此处显示的mysql命令假定您使用一个帐户(例如root
具有修改mysql
系统架构中的表的特权)连接到服务器。根据需要调整连接参数。
mysql_tzinfo_to_sql读取系统的时区文件并从中生成SQL语句。mysql处理这些语句以加载时区表。
mysql_tzinfo_to_sql也可以用于加载单个时区文件或生成leap秒信息:
要加载
tz_file
与时区名称相对应的单个时区文件,请像这样tz_name
调用mysql_tzinfo_to_sql:mysql_tzinfo_to_sql tz_file tz_name | mysql -u root -p mysql
使用这种方法,您必须执行一个单独的命令来为服务器需要知道的每个命名区域加载时区文件。
如果您的时区必须占leap秒,请像这样初始化leap秒信息,其中
tz_file
时区文件的名称为:mysql_tzinfo_to_sql --leap tz_file | mysql -u root -p mysql
运行mysql_tzinfo_to_sql后,重新启动服务器,以使其不再继续使用任何以前缓存的时区数据。
如果您的系统没有zoneinfo数据库(例如Windows),则可以使用包含SQL语句的软件包,该软件包可从MySQL开发人员区域下载:
https://dev.mysql.com/downloads/timezones.html
警告难道不,如果你的系统有一个数据库时区信息使用一个可下载的时区包。请改用mysql_tzinfo_to_sql实用程序。否则,可能会导致MySQL和系统上其他应用程序之间在日期时间处理上有所不同。
要使用已下载的SQL语句时区包,请对其进行解压缩,然后将解压缩的文件内容加载到时区表中:
mysql -u root -p mysql < file_name
然后重新启动服务器。
警告千万不能使用包含一个可下载的时区包
MyISAM
桌。这适用于较旧的MySQL版本。MySQL现在InnoDB
用于时区表。尝试用MyISAM
表替换它们会引起问题。
与时区变化保持同步
当时区规则更改时,使用旧规则的应用程序将过时。要保持最新状态,必须确保您的系统使用了当前时区信息。对于MySQL,要保持最新状态,需要考虑多个因素:
- 如果MySQL服务器的时区设置为,则操作系统时间会影响MySQL服务器使用的时间值
SYSTEM
。确保您的操作系统使用最新的时区信息。对于大多数操作系统,最新的更新或Service Pack可为您的系统做好时间变化的准备。请在您的操作系统供应商的网站上参见有关时间更改的更新。 - 如果将系统的
/etc/localtime
时区文件替换为使用不同于mysqld启动时生效的规则的版本,请重新启动mysqld,以使其使用更新后的规则。否则,mysqld可能不会注意到系统何时更改其时间。 如果您将命名时区与MySQL一起使用,请确保
mysql
数据库中的时区表是最新的:- 如果您的系统具有自己的zoneinfo数据库,则每当zoneinfo数据库更新时,都重新加载MySQL时区表。
- 对于没有自己的zoneinfo数据库的系统,请检查MySQL开发人员区域以获取更新。当有新更新可用时,请下载并使用它来替换当前时区表的内容。
有关这两种方法的说明,请参见填充时区表。mysqld缓存它查找的时区信息,因此在更新时区表之后,请重新启动mysqld以确保它不会继续提供过时的时区数据。
如果不确定命名时区是否可用,用作服务器的时区设置还是由设置自己的时区的客户端使用,请检查时区表是否为空。以下查询确定包含时区名称的表是否具有任何行:
mysql>SELECT COUNT(*)FROM mysql.time_zone_name; +---------- + | COUNT(*) | +---------- + | 0 | +---------- +
计数为零表示该表为空。在这种情况下,当前没有应用程序在使用命名时区,并且您不需要更新表(除非您要启用命名时区支持)。大于零的计数表示该表不为空,并且其内容可用于命名时区支持。在这种情况下,请确保重新加载时区表,以便使用命名时区的应用程序将获得正确的查询结果。
要检查您的MySQL安装是否已正确更新以适应夏令时规则,请使用以下测试。本示例使用的值适用于3月11日凌晨2点在美国发生的2007 DST 1小时更改
测试使用以下查询:
SELECT CONVERT_TZ('2007-03-11 2:00:00','US/Eastern','US/Central')AS time1, CONVERT_TZ('2007-03-11 3:00:00','US/Eastern','US/Central')AS time2;
这两个时间值指示DST发生更改的时间,并且使用命名时区要求使用时区表。期望的结果是两个查询返回相同的结果(输入时间,在“美国/中央”时区中转换为等效值)。
在更新时区表之前,您会看到如下错误结果:
+--------------------- +--------------------- + | time1 | time2 | +--------------------- +--------------------- + | 2007 -03 -11 01:00:00 | 2007 -03 -11 02:00:00 | +--------------------- +--------------------- +
更新表后,您应该看到正确的结果:
+--------------------- +--------------------- + | time1 | time2 | +--------------------- +--------------------- + | 2007 -03 -11 01:00:00 | 2007 -03 -11 01:00:00 | +--------------------- +--------------------- +
时区闰秒支持
返回的second秒值的时间部分以结尾:59:59
。这意味着as之类的函数NOW()
可以在the秒内连续两三秒返回相同的值。具有时间部分结尾:59:60
或被:59:61
认为无效的文字时间值仍然是正确的。
如果有必要TIMESTAMP
在the秒之前一秒钟搜索值,则如果将值与值进行比较,则可能会获得异常结果。以下示例对此进行了演示。它将会话时区更改为UTC,因此内部值(位于UTC中)和显示的值(已应用时区校正)之间没有区别。'YYYY-MM-DD hh:mm:ss'
TIMESTAMP
mysql>CREATE TABLE t1 ( a INT, ts TIMESTAMPDEFAULT CURRENT_TIMESTAMP ,PRIMARY KEY (ts) ); Query OK, 0 rows affected (0.01 sec) mysql> -- change to UTC mysql>SET time_zone = ' +00:00'; Query OK, 0 rows affected (0.00 sec) mysql> -- Simulate NOW() = '2008-12-31 23:59:59' mysql>SET timestamp = 1230767999; Query OK, 0 rows affected (0.00 sec) mysql>INSERT INTO t1 ( )VALUES (1); Query OK, 1 row affected (0.00 sec) mysql> -- Simulate NOW() = '2008-12-31 23:59:60' mysql>SET timestamp = 1230768000; Query OK, 0 rows affected (0.00 sec) mysql>INSERT INTO t1 ( )VALUES (2); Query OK, 1 row affected (0.00 sec) mysql> -- values differ internally but display the same mysql>SELECT , ts, UNIX_TIMESTAMP(ts)FROM t1; +------ +--------------------- +-------------------- + | a | ts | UNIX_TIMESTAMP(ts) | +------ +--------------------- +-------------------- + | 1 | 2008 -12 -31 23:59:59 | 1230767999 | | 2 | 2008 -12 -31 23:59:59 | 1230768000 | +------ +--------------------- +-------------------- + 2 rows in set (0.00 sec) mysql> -- only the non-leap value matches mysql>SELECT *FROM t1WHERE ts = '2008-12-31 23:59:59'; +------ +--------------------- + | a | ts | +------ +--------------------- + | 1 | 2008 -12 -31 23:59:59 | +------ +--------------------- + 1 row in set (0.00 sec) mysql> -- the leap value with seconds=60 is invalid mysql>SELECT *FROM t1WHERE ts = '2008-12-31 23:59:60';Empty set , 2warnings (0.00 sec)
要解决此问题,您可以基于实际存储在列中的UTC值进行比较,并应用comparison秒校正:
mysql> -- selecting using UNIX_TIMESTAMP value return leap value mysql>SELECT *FROM t1WHERE UNIX_TIMESTAMP(ts) = 1230768000; +------ +--------------------- + | a | ts | +------ +--------------------- + | 2 | 2008 -12 -31 23:59:59 | +------ +--------------------- + 1 row in set (0.00 sec)