MySQL的时区应该设置为UTC吗?

Tim*_*nen 135 mysql time utc

跟进https://serverfault.com/questions/191331/should-servers-have-their-timezone-set-to-gmt-utc的问题

MySQL时区应该设置为UTC还是应该设置为与服务器或PHP设置相同的时区?(如果不是UTC)

优缺点都有什么?

Tim*_*nen 497

只要您为当前时区设置了正确的时间,知道您存储的日期时间列的时区,并了解夏令时的问题,服务器上的时区似乎无关紧要.

另一方面,如果您可以控制所使用的服务器的时区,那么您可以在内部将所有内容设置为UTC,而不必担心时区和DST.

以下是我收集的关于如何使用时区作为自己和他人的备忘单形式的一些注释,这些注释可能影响该人为他/她的服务器选择的时区以及他/她将如何存储日期和时间.

MySQL Timezone Cheatsheet

笔记:

  1. 更改时区不会更改存储的日期时间或时间戳,但会从时间戳列中选择不同的日期时间
  2. UTC不使用夏令时,GMT(区域),GMT(时区)没有(GMT也混淆了秒的定义,这就是UTC发明的原因).
  3. 警告!UTC有闰秒,看起来像'2012-06-30 23:59:60'并且可以随意添加,提前6个月通知,因为地球自转速度减慢
  4. 警告!由于夏令时,不同的区域时区可能会产生相同的日期时间值
  5. 由于限制,时间戳列仅支持日期1970-01-01 00:00:01到2038-01-19 03:14:07 UTC .
  6. 在内部,MySQL时间戳列存储为UTC,但在选择日期时,MySQL会自动将其转换为当前会话时区.

    在时间戳中存储日期时,MySQL将假定日期在当前会话时区中并将其转换为UTC以进行存储.

  7. MySQL可以在datetime列中存储部分日期,这些日期看起来像"2013-00-00 04:00:00"
  8. 如果将datetime列设置为NULL,MySQL将存储"0000-00-00 00:00:00",除非您在创建时专门将列设置为允许为null.
  9. 读这个

以UTC格式选择时间戳列

无论当前MySQL会话处于什么时区:

SELECT 
CONVERT_TZ(`timestamp_field`, @@session.time_zone, '+00:00') AS `utc_datetime` 
FROM `table_name`
Run Code Online (Sandbox Code Playgroud)

您还可以将服务器或全局或当前会话时区设置为UTC,然后选择时间戳,如下所示:

SELECT `timestamp_field` FROM `table_name`
Run Code Online (Sandbox Code Playgroud)

要以UTC格式选择当前日期时间:

SELECT UTC_TIMESTAMP();
SELECT UTC_TIMESTAMP;
SELECT CONVERT_TZ(NOW(), @@session.time_zone, '+00:00');
Run Code Online (Sandbox Code Playgroud)

示例结果: 2015-03-24 17:02:41

在会话时区中选择当前日期时间

SELECT NOW();
SELECT CURRENT_TIMESTAMP;
SELECT CURRENT_TIMESTAMP();
Run Code Online (Sandbox Code Playgroud)

选择服务器启动时设置的时区

SELECT @@system_time_zone;
Run Code Online (Sandbox Code Playgroud)

莫斯科时间返回"MSK"或"+04:00",例如,有一个MySQL错误,如果设置为数字偏移,则不会调整夏令时

获取当前时区

SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);
Run Code Online (Sandbox Code Playgroud)

如果你的时区是+2:00,它将返回02:00:00.

要获取当前的UNIX时间戳(以秒为单位):

SELECT UNIX_TIMESTAMP(NOW());
SELECT UNIX_TIMESTAMP();
Run Code Online (Sandbox Code Playgroud)

将timestamp列作为UNIX时间戳获取

SELECT UNIX_TIMESTAMP(`timestamp`) FROM `table_name`
Run Code Online (Sandbox Code Playgroud)

将UTC日期时间列作为UNIX时间戳获取

SELECT UNIX_TIMESTAMP(CONVERT_TZ(`utc_datetime`, '+00:00', @@session.time_zone)) FROM `table_name`
Run Code Online (Sandbox Code Playgroud)

从正UNIX时间戳整数获取当前时区日期时间

SELECT FROM_UNIXTIME(`unix_timestamp_int`) FROM `table_name`
Run Code Online (Sandbox Code Playgroud)

从UNIX时间戳获取UTC日期时间

SELECT CONVERT_TZ(FROM_UNIXTIME(`unix_timestamp_int`), @@session.time_zone, '+00:00') 
FROM `table_name`
Run Code Online (Sandbox Code Playgroud)

从负UNIX时间戳整数获取当前时区日期时间

SELECT DATE_ADD('1970-01-01 00:00:00',INTERVAL -957632400 SECOND) 
Run Code Online (Sandbox Code Playgroud)

有三个地方可以在MySQL中设置时区:

注意:时区可以设置为2种格式:

  1. UTC的偏移量:'+ 00:00','+ 10:00'或'-6:00'
  2. 作为指定时区:'Europe/Helsinki','US/Eastern'或'MET'

仅当已创建并填充mysql数据库中的时区信息表时,才能使用命名时区.

在"my.cnf"文件中

default_time_zone='+00:00'
Run Code Online (Sandbox Code Playgroud)

要么

timezone='UTC'
Run Code Online (Sandbox Code Playgroud)

@@ global.time_zone变量

要查看它们设置的值

SELECT @@global.time_zone;
Run Code Online (Sandbox Code Playgroud)

要为其设置值,请使用以下任一项:

SET GLOBAL time_zone = '+8:00';
SET GLOBAL time_zone = 'Europe/Helsinki';
SET @@global.time_zone='+00:00';
Run Code Online (Sandbox Code Playgroud)

@@ session.time_zone变量

SELECT @@session.time_zone;
Run Code Online (Sandbox Code Playgroud)

设置它使用任何一个:

SET time_zone = 'Europe/Helsinki';
SET time_zone = "+00:00";
SET @@session.time_zone = "+00:00";
Run Code Online (Sandbox Code Playgroud)

"@@ global.time_zone变量"和"@@ session.time_zone变量"都可能返回"SYSTEM",这意味着它们使用"my.cnf"中设置的时区.

要使时区名称起作用(即使是默认时区),您必须设置需要填充的时区信息表: http ://dev.mysql.com/doc/refman/5.1/en/time-zone-support. HTML

注意:你不能这样做,因为它会返回NULL:

SELECT 
CONVERT_TZ(`timestamp_field`, TIMEDIFF(NOW(), UTC_TIMESTAMP), '+00:00') AS `utc_datetime` 
FROM `table_name`
Run Code Online (Sandbox Code Playgroud)

设置mysql时区表

对于CONVERT_TZ工作,你需要填充时区表

SELECT * FROM mysql.`time_zone` ;
SELECT * FROM mysql.`time_zone_leap_second` ;
SELECT * FROM mysql.`time_zone_name` ;
SELECT * FROM mysql.`time_zone_transition` ;
SELECT * FROM mysql.`time_zone_transition_type` ;
Run Code Online (Sandbox Code Playgroud)

如果它们为空,则通过运行此命令填充它们

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
Run Code Online (Sandbox Code Playgroud)

如果此命令为您提供错误" 第1行的列'缩写'的数据太长 ",则可能是由于在时区缩写的末尾附加了NULL字符引起的

修复是要运行它

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
(if the above gives error "data too long for column 'abbreviation' at row 1")
mysql_tzinfo_to_sql /usr/share/zoneinfo > /tmp/zut.sql

echo "SET SESSION SQL_MODE = '';" > /tmp/mysql_tzinfo_to.sql
cat /tmp/zut.sql >> /tmp/mysql_tzinfo_to.sql

mysql --defaults-file=/etc/mysql/my.cnf --user=verifiedscratch -p mysql < /tmp/mysql_tzinfo_to.sql
Run Code Online (Sandbox Code Playgroud)

(确保您的服务器dst规则是最新的zdump -v Europe/Moscow | grep 2011 https://chrisjean.com/updating-daylight-saving-time-on-linux/)

查看每个时区的完整夏令时(夏令时)转换历史记录

SELECT 
tzn.Name AS tz_name,
tztt.Abbreviation AS tz_abbr,
tztt.Is_DST AS is_dst,
tztt.`Offset` AS `offset`,
DATE_ADD('1970-01-01 00:00:00',INTERVAL tzt.Transition_time SECOND)  AS transition_date
FROM mysql.`time_zone_transition` tzt
INNER JOIN mysql.`time_zone_transition_type` tztt USING(Time_zone_id, Transition_type_id)
INNER JOIN mysql.`time_zone_name` tzn USING(Time_zone_id)
-- WHERE tzn.Name LIKE 'Europe/Moscow' -- Moscow has weird DST changes
ORDER BY tzt.Transition_time ASC
Run Code Online (Sandbox Code Playgroud)

CONVERT_TZ 还会根据上表中的规则和您使用的日期应用任何必要的DST更改.

注意:
根据文档,您为time_zone设置的值不会更改,例如,如果将其设置为"+01:00",则time_zone将设置为UTC的偏移量,这不会跟随DST,因此它将全年保持不变.

只有指定的时区才会在夏令时期间改变时间.

类似的缩写CET将永远是冬季时间,CEST将是夏季时间,而+01:00将始终是UTC时间+ 1小时,并且两者都不会随着夏令时而改变.

system时区将是在安装MySQL的主机的时区(除非MySQL的失败来确定它)

您可以在此处阅读有关使用DST的更多信息

相关问题:

资料来源:

  • 多年来使用SO时遇到的最好,最丰富的答案之一.谢谢. (5认同)
  • @bumperbox mysql 始终假定您提供时间戳列的日期与 mysql 服务器在同一时区,因此您需要将日期从 +12:00 时区转换为 mysql 服务器时区以进行更新。这就是为什么我在 mysql 服务器上使用 UTC 并在存储之前将任何日期转换为 UTC。 (4认同)