MySQL NOW() 是否包含夏令时

文章目录

  • 总结
  • 实验过程
    • 环境
    • NOW()会自动计算夏令时吗?
    • 关于美国的夏令时
    • 主机时区设置为Asia/Shanghai
      • 结论
    • 主机时区设置为America/Los_Angeles
      • 结论
    • time_zone设置为时区名称
      • 填充时区表
      • 结论
  • 遗留问题
  • 参考文献

总结

MySQL的timezone值可以多种格式给出,都不区分大小写:1

  • 时区偏移量,表示从UTC开始的偏移量 [H]H:MM,带有前缀+- ,例如,+[H]H:MM-[H]H:MM 。当小时值小于10,可选择使用前导零;在这种情况下,MySQL在存储和检索时会预先设置前导零,将"-00:00"或"-0:00"转换为"+00:00"。时区偏移必须在"-12:59"到"13:00"的范围内。NOW()不包含夏令时

  • 时区名称,例如'UTC''Asia/Shanghai''America/Los_Angeles''Europe/Helsinki''US/Eastern''MET'。当且仅当mysql已经创建并填充了数据库中的时区信息表时,才能使用时区名称 。NOW()包含夏令时

  • SYSTEM,指示服务器时区与主机系统时区相同。取决于MySQL Server所在主机的时间

mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | CST    |
| time_zone        | -08:00 |——时区偏移量,NOW()不含夏令时
+------------------+--------+
2 rows in set (0.01 sec)

mysql> show variables like '%time_zone%';
+------------------+---------------------+
| Variable_name    | Value               |
+------------------+---------------------+
| system_time_zone | CST                 |
| time_zone        | America/Los_Angeles |——时区名称,NOW()含夏令时
+------------------+---------------------+
2 rows in set (0.00 sec)

mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | PDT    |——PDT太平洋时区,代表城市"洛杉矶"
| time_zone        | SYSTEM |——表示MySQL Server时区与主机系统时区相同
+------------------+--------+
2 rows in set (0.01 sec)

下方的实验过程可以忽略。

实验过程

环境

主机:CentOS 7.2
数据库:MySQL 5.7.22

查看MySQL基本信息(MySQL版本、操作系统、主机时区、数据库时区)。

mysql> SELECT @@global.version, @@global.version_compile_os, @@global.system_time_zone, @@global.time_zone, @@session.time_zone\G
*************************** 1. row ***************************
           @@global.version: 5.7.22
@@global.version_compile_os: Linux
  @@global.system_time_zone: CST
         @@global.time_zone: SYSTEM
        @@session.time_zone: SYSTEM
1 row in set (0.00 sec)

mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | CST    |
| time_zone        | SYSTEM |
+------------------+--------+
2 rows in set (0.01 sec)

度娘说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

看到system_time_zone: CST和4种解释,我一脸懵逼。

NOW()会自动计算夏令时吗?

夏令时的开始时间与结束时间,以2020年为例:
  2020年美国夏令时将于2020年3月8日凌晨2点开始,至2020年11月1日凌晨2点夏令时结束。

关于美国的夏令时

  美国夏时制的实行与否,完全由各州、各县自行决定,不由联邦政府统一规定。目前美国绝大部分地区实行夏令时,美国不实行夏令时地区包括:

  1. 亚利桑那州的绝大部分地区(除东北一小区块);
  2. 夏威夷州;
  3. 波多黎各和维京群岛;
  4. 美属萨摩亚、关岛和北马里亚纳群岛。

  美国和加拿大原本于每年10月的最后一个星期日凌晨2时起实施冬令时间;4月的第一个星期日凌晨2时起,恢复夏令时间。
  但是根据美国国会最新通过的能源法案,为加强日光节约,自2007年起延长夏令时间,从每年3月的第二个星期日开始,至每年11月的第一个星期日结束,因此,冬令时间将缩短约一个月。之所以安排在周日,是为了便于生活的调整不至于受到较大的影响。
——来自百度百科

美国时间与中国时间时差(夏令时):

  • UTC-5 EST 东部时区:代表城市纽约、华盛顿,与北京相差12小时;
  • UTC-6 CST 中部时区:代表城市芝加哥,与北京相差13小时;
  • UTC-7 MST 山地时区:代表城市盐湖城,与北京相差14小时;
  • UTC-8 PST 太平洋时区:代表城市洛杉矶,与北京相差15小时;
  • UTC-9 AKST 阿拉斯加时区:代表城市:费尔班克斯,与北京相差16小时。

美国的时区以及所属区域:

  • UTC+10 夏莫罗标准时区
  • UTC-11 美属萨摩亚标准时区
  • UTC-10 HST 夏威夷-阿留申标准时区
  • UTC-9 AKST 阿拉斯加标准时区
  • UTC-8 PST 太平洋标准时区
  • UTC-7 MST 山地标准时区
  • UTC-6 CST 中部标准时区
  • UTC-5 EST 东部标准时区
  • UTC-4 AST 大西洋标准时区

将MySQL Server时区设置为西八区,NOW()会自动计算夏令时吗?

@@session.time_zone。每个连接的客户端都有自己的会话时区设置,由@@session.time_zone变量指定。最初,session变量从global变量获取其值time_zone,但是客户端可以使用以下语句更改其自己的时区

将MySQL Server的变量@@session.time_zone设置为西八区,并查看NOW()

mysql> SET @@session.time_zone='-08:00';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@session.time_zone;
+---------------------+
| @@session.time_zone |
+---------------------+
| -08:00              |
+---------------------+
1 row in set (0.00 sec)

或者直接改MySQL的配置文件my.cnf,然后重启MySQL Server。

[mysqld]
default-time-zone='-08:00'

主机时间

# date +'%Y-%m-%d %H:%M:%S %Z %z'
2020-01-18 23:27:36 CST +0800

主机的时区设置
东八区上海 timedatectl set-timezone Asia/Shanghai
西六区芝加哥timedatectl set-timezone America/Chicago
西八区洛杉矶timedatectl set-timezone America/Los_Angeles
Linux系统时间同步(NTP)要关闭,为了我们人为设置Linux系统时间。

  • 关闭timedatectl set-ntp false
  • 开启timedatectl set-ntp true

主机时区设置为Asia/Shanghai

将主机的时区设置为Asia/Shanghai、时间设置为2020-04-01 00:00:00
MySQL的time_zone设置成时区偏移量’-08:00’,

查看SELECT NOW(), UNIX_TIMESTAMP()与北京时间相差几个小时?

# timedatectl set-timezone Asia/Shanghai
# date +'%Y-%m-%d %H:%M:%S %Z %z'
2020-04-01 00:05:29 CST +0800
mysql> SET @@session.time_zone='-08:00';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@session.time_zone, NOW(), UNIX_TIMESTAMP();
+---------------------+---------------------+------------------+
| @@session.time_zone | NOW()               | UNIX_TIMESTAMP() |
+---------------------+---------------------+------------------+
| -08:00              | 2020-03-31 08:05:29 |       1585670729 |
+---------------------+---------------------+------------------+
1 row in set (0.00 sec)

用PHP将时间戳 1585670729 转换格式,得到:

  • 2020-03-31 16:05:29 UTC +00:00 UTC
  • 2020-03-31 09:05:29 PST -08:00 America/Los_Angeles—— (PHP根据时区名称,自动包含夏令时,而MySQL根据时区偏移量’-08:00’只能转换成’08:05:29’,与PHP得到的结果相差一个小时)
  • 2020-04-01 00:05:29 CST +08:00 Asia/Shanghai——(上海洛杉矶相差15小时,即包含夏令时)

date_default_timezone_set('UTC');
$int = 1585670729;
format($int);

date_default_timezone_set('America/Los_Angeles');
format($int);

date_default_timezone_set('Asia/Shanghai');
format($int);

function format($timestamp)
{
    $format = 'Y-m-d H:i:s T P';
    echo date($format, $timestamp) . ' ' . date_default_timezone_get() . PHP_EOL;
}

结论

同样的时间戳1585670729,若MySQL的time_zone设置为时区偏移量’-08:00’,则NOW()不包含夏令时。

主机时区设置为America/Los_Angeles

将主机的时区设置为美国太平洋时区 PDT、时间设置为2020-04-01 00:00:00
MySQL的time_zone设置成时区偏移量’-08:00’,

# timedatectl set-timezone America/Los_Angeles
# date +'%Y-%m-%d %H:%M:%S %Z %z'
2020-04-01 00:07:10 PDT -0700
mysql> SET @@session.time_zone='-08:00';
mysql> SELECT @@session.time_zone, NOW(), UNIX_TIMESTAMP();
+---------------------+---------------------+------------------+
| @@session.time_zone | NOW()               | UNIX_TIMESTAMP() |
+---------------------+---------------------+------------------+
| -08:00              | 2020-03-31 23:07:10 |       1585724830 |
+---------------------+---------------------+------------------+
1 row in set (0.00 sec)

用PHP将时间戳 1585724830 转换格式,得到:
2020-04-01 07:07:10 UTC +00:00 UTC
2020-04-01 00:07:10 PDT -07:00 America/Los_Angeles
2020-04-01 15:07:10 CST +08:00 Asia/Shanghai——(上海洛杉矶相差15小时,即包含夏令时)

结论

若MySQL的time_zone设置为时区偏移量,如’-08:00’,则NOW()不包含夏令时。

time_zone设置为时区名称

设置time_zone为时区名称,如地理位置在西八区的美国洛杉矶。

mysql> SET @@session.time_zone='America/Los_Angeles';
ERROR 1298 (HY000): Unknown or incorrect time zone: 'America/Los_Angeles'

上方报错的原因是MySQL数据库中的时区信息默认为空。即mysql库下的时区表都是空表。

mysql> use mysql;
Database changed
mysql> SHOW TABLES LIKE "%time%";
+---------------------------+
| Tables_in_mysql (%time%)  |
+---------------------------+
| time_zone                 |
| time_zone_leap_second     |
| time_zone_name            |
| time_zone_transition      |
| time_zone_transition_type |
+---------------------------+
5 rows in set (0.00 sec)

填充时区表

Linux系统的时区文件导入MySQL Server的mysql库。

需要先进入MySQL安装目录的bin目录下,执行mysql_tzinfo_to_sql命令,Linux系统下的时区文件路径作为参数,2

[root@sy-pc bin]# cd /application/mysql/bin
[root@sy-pc bin]# mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql 
Enter password: 
Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.

如果您的系统没有zoneinfo数据库(例如Windows),则可以使用包含SQL语句的软件包,该软件包可从MySQL开发人员区域下载https://dev.mysql.com/downloads/timezones.html3,然后导入MySQL的mysql库中。

如果您的系统包含zoneinfo文件(例如Linux,FreeBSD,Sun Solaris),请不要使用此软件包。请mysql.time_zone*改用mysql_tzinfo_to_sql实用程序从这些文件中生成表!(否则,可能会导致MySQL与系统上其他应用程序之间在日期时间处理上有所不同。)

设置time_zone为洛杉矶

mysql> SET @@session.time_zone='America/Los_Angeles';
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%time_zone%';
+------------------+---------------------+
| Variable_name    | Value               |
+------------------+---------------------+
| system_time_zone | CST                 |
| time_zone        | America/Los_Angeles |
+------------------+---------------------+
2 rows in set (0.00 sec)

设置主机时区、时间

# timedatectl set-timezone America/Los_Angeles
# date +'%Y-%m-%d %H:%M:%S %Z %z'
2020-04-01 00:00:55 PDT -0700
mysql> SELECT @@session.time_zone, NOW(), UNIX_TIMESTAMP();
+---------------------+---------------------+------------------+
| @@session.time_zone | NOW()               | UNIX_TIMESTAMP() |
+---------------------+---------------------+------------------+
| America/Los_Angeles | 2020-04-01 00:00:55 |       1585724455 |
+---------------------+---------------------+------------------+
1 row in set (0.00 sec)

用PHP将时间戳 1585724455 转换格式,得到:
2020-04-01 07:00:55 UTC +00:00 UTC
2020-04-01 00:00:55 PDT -07:00 America/Los_Angeles
2020-04-01 15:00:55 CST +08:00 Asia/Shanghai

此时,我们将time_zone设置成时区偏移量,看看MySQL会不会自动计算夏时令。显然,time_zone为时区偏移量,MySQL不包含夏令时。

mysql> SET @@session.time_zone='-08:00';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@session.time_zone, NOW(), UNIX_TIMESTAMP();
+---------------------+---------------------+------------------+
| @@session.time_zone | NOW()               | UNIX_TIMESTAMP() |
+---------------------+---------------------+------------------+
| -08:00              | 2020-03-31 23:05:12 |       1585724712 |
+---------------------+---------------------+------------------+
1 row in set (0.00 sec)

结论

若MySQL的time_zone设置为时区名称,则NOW()包含夏令时。

mysql> 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;
+---------------------+---------------------+
| time1               | time2               |
+---------------------+---------------------+
| 2007-03-11 01:00:00 | 2007-03-11 01:00:00 |
+---------------------+---------------------+
1 row in set (0.01 sec)

mysql> SELECT 
    -> CONVERT_TZ('2020-04-01 00:00:00','Asia/Shanghai', 'America/Los_Angeles') AS 'time1',
    -> CONVERT_TZ('2020-01-01 00:00:00','Asia/Shanghai', 'America/Los_Angeles') AS 'time2';
+---------------------+---------------------+
| time1               | time2               |
+---------------------+---------------------+
| 2020-03-31 09:00:00 | 2019-12-31 08:00:00 |
+---------------------+---------------------+
1 row in set (0.00 sec)

遗留问题

上述操作中mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql -p爆出的Warning是啥意思?

[root@sy-pc bin]# cd /application/mysql/bin
[root@sy-pc bin]# mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql -p
Enter password: 
Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.

参考文献

mysql 时区 , 夏令时,冬令时[DB|OL]. https://www.cnblogs.com/zengkefu/p/5611392.html


  1. MySQL Server Time Zone Support[DB|OL]. https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html ↩︎

  2. mysql_tzinfo_to_sql — Load the Time Zone Tables[DB|OL]. https://dev.mysql.com/doc/refman/5.7/en/mysql-tzinfo-to-sql.html ↩︎

  3. MySQL::时区描述表[DB|OL]. https://dev.mysql.com/downloads/timezones.html ↩︎

你可能感兴趣的:(MySQL)