ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

5.1.15 MySQL 服务器时区支持

2021-02-03 16:01:01  阅读:275  来源: 互联网

标签:5.1 00 15 zone mysql MySQL 59 时区 服务器时


文章目录


官方文档地址: 5.1.15 MySQL Server Time Zone Support


本节介绍 MySQL 维护的时区设置,如何加载支持命名时间所需的系统表,如何跟上时区的变化,以及如何启用跳秒支持。

从 MySQL 8.0.19 开始,时区偏移也支持插入DATETIME值;参见 11.2.2 DATE,DATETIME,和 TIMESTAMP 类型

有关复制设置中的时区设置的信息,参见 17.5.1.14 复制与系统功能17.5.1.33 复制和时区

时区变量

MySQL 服务器维护几个时区设置:

1、系统时区。当服务器启动时,它尝试自动确定主机的时区,并使用它来设置system_time_zone系统变量。此后该值不会改变。

要在启动时显式地指定 MySQL 服务器的系统时区,在启动mysqld之前设置环境变量TZ。如果使用mysqld_safe启动服务器,它的--timezone选项提供了另一种设置系统时区的方法。TZ--timezone的允许值依赖于系统。参考操作系统文档,看看哪些值是可以接受的。

2、服务器当前时区。全局系统变量time_zone表示服务器当前运行的时区。初始的time_zone值为'SYSTEM',表示服务器时区与系统时区一致。

注意

如果设置为 SYSTEM,每个需要时区计算的 MySQL 函数调用都会调用一个系统库来确定当前的系统时区。这个调用可能被一个全局互斥锁保护,从而导致争用。

全局服务器的初始时区值可以在启动时通过命令行上的--default time-zone选项显式指定,也可以在选项文件中(比如my.cnf(Unix 操作系统)或者my.ini(Windows))添加以下行:

default-time-zone='timezone'

如果你有SYSTEM_VARIABLES_ADMIN权限(或者已弃用的SUPER权限),你可以在运行时使用下面语句设置全局服务器时区值:

SET GLOBAL time_zone = timezone;

3、每个会话的时区。每个连接的客户端都有自己的会话时区设置,由会话time_zone变量给出。最初,会话变量的值来自全局time_zone变量,但是客户端可以通过以下语句更改自己的时区:

SET time_zone = timezone;

设置会话时区会影响时区敏感的时间值的显示和存储。这包括NOW()CURTIME()等函数显示的值,以及TIMESTAMP列中存储的值和从TIMESTAMP列中检索的值。TIMESTAMP列的值将从会话时区转换为 UTC 用于存储,从 UTC 转换为会话时区用于检索。

会话时区设置不影响UTC_TIMESTAMP()等函数显示的值,也不影响DATETIMEDATETIME列中的值。这些数据类型的值也不存储 UTC;只有在TIMESTAMP值转换时用到时区。如果需要DATETIMEDATETIME值的特定于区域设置的计算,请将它们转换为 UTC,执行计算,然后再转换回来。

当前全局时区和会话时区值可以这样检索:

SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;

时区值可以用几种格式给出,但都不区分大小写:

  • 一个'SYSTEM'值,表示服务器时区与系统时区一致。
  • 一个表示相对于 UTC 的偏移量的字符串,格式为[H]H:MM,前缀为+-,例如'+10:00''-6:00',或'+05:30'。前导零可用于小时值小于 10 的情况;在这种情况下,MySQL在存储和检索值时,前面加一个 0。MySQL 将'-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''UTC'

注意

只有已经创建并填充了mysql数据库中的时区信息表,才可以使用命名时区。否则,使用指定时区会导致错误:

mysql> SET time_zone = 'UTC';
ERROR 1298 (HY000): Unknown or incorrect time zone: 'UTC'

填充时区表

mysql系统模式中存在几个表来存储时区信息(参见 5.3 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也可以用于加载单个时区文件或生成跳秒信息:

1、要加载与时区名称tz_name对应的单个时区文件tz_file,请如下所示调用mysql_tzinfo_to_sql

mysql_tzinfo_to_sql tz_file tz_name | mysql -u root -p mysql

使用这种方法,您必须执行一个单独的命令,为服务器需要了解的每个指定区域加载时区文件。

2、如果你的时区必须考虑跳秒,初始化闰秒信息如下,其中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

警告

如果你的系统有 zoneinfo 数据库,不要使用可下载的时区包。请使用 mysql_tzinfo_to_sql 加载程序。否则,您可能会导致 MySQL 和系统上其他应用程序之间的日期时间处理不同。

要使用已经下载的 SQL 语句时区包,先解压它,然后将解压后的文件内容加载到时区表中:

mysql -u root -p mysql < file_name

然后重新启动服务器。

警告

不要使用包含 MyISAM 表的可下载时区包。这适用于较老的 MySQL 版本。MySQL 现在使用 InnoDB 来处理时区表。尝试用 MyISAM 表替换它们会导致问题。

及时了解时区的变化

当时区规则改变时,使用旧规则的应用程序将过时。为了保持最新,有必要确保您的系统使用的是当前时区信息。对于MySQL来说,有很多因素需要考虑:

  • 如果 MySQL 服务器的时区设置为SYSTEM,操作系统的时间会影响 MySQL 服务器的时间。确保您的操作系统使用最新的时区信息。对于大多数操作系统,最新的更新或服务包会让系统为时间更改做好准备。请查看您的操作系统供应商的网站,以获取处理时间变化的更新。
  • 如果您将系统的/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;

这两个时间值表示夏令时发生变化的时间,使用命名时区需要使用时区表。期望的结果是两个查询返回相同的结果(输入时间,在'US/Central'时区转换为等效的值)。

在更新时区表之前,你会看到这样一个不正确的结果:

+---------------------+---------------------+
| 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 |
+---------------------+---------------------+

时区跳秒支持

跳秒值返回的时间部分以:59:59结尾。这意味着像NOW()这样的函数可以在跳跃秒的时间内连续返回两到三次相同的值。时间部分以:59:60:59:61结尾的文字时间值仍然被认为是无效的。

如果需要在跳秒前一秒搜索TIMESTAMP值,则使用'YYYY-MM-DD hh:mm:ss'值进行比较,可能会得到异常结果。下面的示例演示了这一点。它将会话时区更改为 UTC,这样内部TIMESTAMP值(在 UTC)和显示值(应用了时区校正)之间就没有区别了。

mysql> CREATE TABLE t1 (
         a INT,
         ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
         PRIMARY KEY (ts)
       );
Query OK, 0 rows affected (0.01 sec)

mysql> -- 改为 UTC
mysql> SET time_zone = '+00:00';
Query OK, 0 rows affected (0.00 sec)

mysql> -- 模拟 NOW() = '2008-12-31 23:59:59'
mysql> SET timestamp = 1230767999;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t1 (a) VALUES (1);
Query OK, 1 row affected (0.00 sec)

mysql> -- 模拟 NOW() = '2008-12-31 23:59:60'
mysql> SET timestamp = 1230768000;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t1 (a) VALUES (2);
Query OK, 1 row affected (0.00 sec)

mysql> -- 值内部不同,但显示相同
mysql> SELECT a, 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> -- 只有非跳跃值匹配
mysql> SELECT * FROM t1 WHERE ts = '2008-12-31 23:59:59';
+------+---------------------+
| a    | ts                  |
+------+---------------------+
|    1 | 2008-12-31 23:59:59 |
+------+---------------------+
1 row in set (0.00 sec)

mysql> -- 秒=60 的跳跃值无效
mysql> SELECT * FROM t1 WHERE ts = '2008-12-31 23:59:60';
Empty set, 2 warnings (0.00 sec)

为了解决这个问题,你可以使用一个基于 UTC 值的比较,该值实际上存储在列中,并应用了跳秒校正:

mysql> -- 选择使用 UNIX_TIMESTAMP 函数返回跳跃值
mysql> SELECT * FROM t1 WHERE UNIX_TIMESTAMP(ts) = 1230768000;
+------+---------------------+
| a    | ts                  |
+------+---------------------+
|    2 | 2008-12-31 23:59:59 |
+------+---------------------+
1 row in set (0.00 sec)

标签:5.1,00,15,zone,mysql,MySQL,59,时区,服务器时
来源: https://blog.csdn.net/wb1046329430/article/details/113599628

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有