似乎服务器上的时区并不重要,只要您为当前时区设置了正确的时间,知道您存储的日期时间列的时区,并了解夏令时的问题时间。
另一方面,如果您可以控制所使用服务器的时区,那么您可以在内部将所有内容设置为 UTC,而不必担心时区和 DST,至少在存储内部时间方面是这样。
以下是我收集的一些关于如何将时区作为我自己和其他人的备忘单形式的笔记,这些笔记可能会影响该人将为他/她的服务器选择哪个时区以及他/她将如何存储日期和时间。
MySQL 时区备忘单
注意事项:
-
更改时区不会更改存储的日期时间或
时间戳,但它会选择不同的日期时间
时间戳列
-
警告! UTC 有闰秒,这些看起来像 '2012-06-30 23:59:60' 并且可以
随机添加,提前6个月通知,由于放缓
地球自转
-
GMT 会混淆秒数,这就是发明 UTC 的原因。
-
警告!不同的区域时区可能会产生相同的日期时间值,因为
到夏令时
-
由于a limitation,时间戳列仅支持 1970-01-01 00:00:01 到 2038-01-19 03:14:07 UTC 日期。
-
在内部 MySQL timestamp column 存储为 UTC 但
选择日期时,MySQL 会自动将其转换为
当前会话时区。
当在时间戳中存储日期时,MySQL 将假定日期
位于当前会话时区并将其转换为 UTC
存储。
-
MySQL 可以在 datetime 列中存储部分日期,这些看起来像
"2013-00-00 04:00:00"
-
如果您将日期时间列设置为,MySQL 将存储“0000-00-00 00:00:00”
NULL,除非您在指定时将列设置为允许为空
创建它。
-
Read this
选择 UTC 格式的时间戳列
无论当前 MySQL 会话在哪个时区:
SELECT
CONVERT_TZ(`timestamp_field`, @@session.time_zone, '+00:00') AS `utc_datetime`
FROM `table_name`
您还可以将服务器或全局或当前会话时区设置为 UTC,然后像这样选择时间戳:
SELECT `timestamp_field` FROM `table_name`
以 UTC 选择当前日期时间:
SELECT UTC_TIMESTAMP();
SELECT UTC_TIMESTAMP;
SELECT CONVERT_TZ(NOW(), @@session.time_zone, '+00:00');
示例结果:2015-03-24 17:02:41
在会话时区中选择当前日期时间
SELECT NOW();
SELECT CURRENT_TIMESTAMP;
SELECT CURRENT_TIMESTAMP();
选择服务器启动时设置的时区
SELECT @@system_time_zone;
例如,返回莫斯科时间的“MSK”或“+04:00”,存在(或曾经)一个 MySQL 错误,如果设置为数字偏移量,它将不会调整夏令时
获取当前时区
SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);
如果您的时区是 +2:00,它将返回 02:00:00。
获取当前 UNIX 时间戳(以秒为单位):
SELECT UNIX_TIMESTAMP(NOW());
SELECT UNIX_TIMESTAMP();
获取时间戳列作为 UNIX 时间戳
SELECT UNIX_TIMESTAMP(`timestamp`) FROM `table_name`
获取 UTC 日期时间列作为 UNIX 时间戳
SELECT UNIX_TIMESTAMP(CONVERT_TZ(`utc_datetime`, '+00:00', @@session.time_zone)) FROM `table_name`
从一个正的 UNIX 时间戳整数获取当前时区日期时间
SELECT FROM_UNIXTIME(`unix_timestamp_int`) FROM `table_name`
从 UNIX 时间戳获取 UTC 日期时间
SELECT CONVERT_TZ(FROM_UNIXTIME(`unix_timestamp_int`), @@session.time_zone, '+00:00')
FROM `table_name`
从一个负的 UNIX 时间戳整数中获取当前时区日期时间
SELECT DATE_ADD('1970-01-01 00:00:00',INTERVAL -957632400 SECOND)
在 MySQL 中有 3 个地方可以设置时区:
注意:时区可以设置为 2 种格式:
- 与 UTC 的偏移量:“+00:00”、“+10:00”或“-6:00”
- 作为命名时区:“Europe/Helsinki”、“US/Eastern”或“MET”
命名时区只能在时区信息表中使用
在 mysql 数据库中已创建并填充。
在“my.cnf”文件中
default_time_zone='+00:00'
或
timezone='UTC'
@@global.time_zone 变量
查看它们设置为什么值
SELECT @@global.time_zone;
要为其设置一个值,请使用其中一个:
SET GLOBAL time_zone = '+8:00';
SET GLOBAL time_zone = 'Europe/Helsinki';
SET @@global.time_zone='+00:00';
@@session.time_zone 变量
SELECT @@session.time_zone;
要设置它,请使用其中一个:
SET time_zone = 'Europe/Helsinki';
SET time_zone = "+00:00";
SET @@session.time_zone = "+00:00";
“@@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`
设置 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` ;
如果它们是空的,则通过运行此命令来填充它们
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
如果此命令给出错误“data too long for column 'abbreviation' at row 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
(确保您的服务器 dst 规则是最新的 zdump -v Europe/Moscow | grep 2011 https://chrisjean.com/updating-daylight-saving-time-on-linux/)
查看每个时区的完整 DST(夏令时)转换历史
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
CONVERT_TZ 还会根据上表中的规则和您使用的日期应用任何必要的 DST 更改。
注意:
根据docs,您为time_zone设置的值不会改变,例如如果您将其设置为“+01:00”,那么time_zone将设置为UTC的偏移量,它不遵循DST,所以它将全年保持不变。
只有命名的timezones 会在夏令时更改时间。
CET 等缩写将始终是冬季时间,CEST 将是夏季时间,而 +01:00 将始终是 UTC 时间 + 1 小时,两者都不会随 DST 改变。
system 时区将是安装 mysql 的主机的时区(除非 mysql 无法确定)
您可以阅读有关使用 DST 的更多信息here
传奇人物 Jon Skeet 何时不使用 UTC:https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/
(例如未来的预定事件,它代表一个时间,而不是一个瞬间)
相关问题:
来源: