MySQL 的时区是否应该设置为 UTC?

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

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

优点和缺点是什么?

250249 次浏览

利弊几乎是一样的,这取决于你想不想要。

小心,如果 MySQL 时区不同于您的系统时间(例如 PHP) ,比较时间或打印到用户将涉及一些修补。

PHP 和 MySQL 有自己的默认时区配置。您应该同步您的数据库和 Web 应用程序之间的时间,否则您可能会运行一些问题。

阅读本教程: 如何同步您的 PHP 和 MySQL 时区

似乎服务器上的时区并不重要,只要你为当前时区设置了正确的时间,知道你存储的日期时间列的时区,并且意识到夏时制的问题。

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

以下是我收集的一些关于如何使用时区的笔记,作为我和其他人的备忘录,它们可能会影响用户为他/她的服务器选择什么时区,以及他/她如何存储日期和时间。

MySQL 时区备忘单

备注:

  1. 更改时区 < strong > 不会更改存储的日期时间或 时间戳 ,但是它将从 时间戳列

  2. 警告! UTC 有闰秒,这些看起来像“2012-06-3023:59:60”并且可以 随机增加,提前6个月通知 地球自转

  3. GMT 混淆了秒,这就是 UTC 发明的原因。

  4. 警告! 不同的区域时区可能产生相同的到期日期时间值 到夏令时

  5. 时间戳列只支持日期1970-01-0100:00:01 to 2038-01-1903:14:07 UTC,由于 一个限制的原因。

  6. 在内部,MySQL 时间戳列协调世界时的形式存储,但 当选择一个日期时,MySQL 会自动将其转换为 当前会话时区。

    当在时间戳中存储日期时,MySQL 将假定日期 在当前会话时区中,并将其转换为 UTC 储藏室。

  7. MySQL 可以在日期时间列中存储部分日期,如下所示 “2013-00-0004:00:00”

  8. 如果将 datetime 列设置为 除非您特别将列设置为允许 创造它。

  9. 看看这个

选择 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`

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

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个地方可以设置时区:

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

  1. 与 UTC 的偏移量:’+ 00:00’、’+ 10:00’或’-6:00’
  2. 作为一个命名的时区: “欧洲/赫尔辛基”,“美国/东部”,或“ 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”中设置的时区。

为了让时区名称正常工作(即使是默认的时区) ,你必须设置你的时区信息表需要填充: < a href = “ http://dev.mysql.com/doc/refman/5.1/en/time-zone-Support.html”rel = “ norefrer”> 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

如果这个命令给出错误“ 第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 更改基于上述表中的规则和您使用的日期。

注:
根据 医生,你为 time _ zone 设置的值不会改变,例如,如果你将它设置为“ + 01:00”,那么 time _ zone 将被设置为与 UTC 的偏移量,这与 DST 不同,因此它将全年保持不变。

只有命名的 时区会在夏令时改变时间。

CET这样的缩写将永远是冬季时间,而 CEST将永远是夏季时间,而 + 01:00将永远是 UTC时间 + 1小时,两者都不会随着 DST 而改变。

system时区将是安装 mysql 的主机的时区(除非 mysql 无法确定它)

你可以阅读更多关于使用 DST 给你的信息

传奇人物乔恩 · 斯基特(Jon Skeet)的《何时不使用 UTC 》一 https://codeblog.jonskeet.uk/2019/03/27/storing-UTC-is-not-a-silver-bullet/ (例如,未来的预定事件表示一个时间,而不是时间上的一个瞬间)

相关问题:

资料来源:

这是一个很好的例子:

jdbc:mysql://localhost:3306/database?useUnicode=yes&characterEncoding=UTF-8&serverTimezone=Europe/Moscow

如何让您的应用程序不知道服务器的时区?

由于以下任何一种可能的情况:

  • 您可能无法控制 Web/数据库服务器的时区设置
  • 您可能会搞砸并且设置不正确
  • 在其他的答案中有如此多的设置,如此多的事情需要跟踪,以至于你可能会错过一些东西
  • 服务器上的更新、软件重置或其他管理员可能在不知情的情况下将服务器的时区重置为默认值——从而破坏您的应用程序

上述所有场景都会导致应用程序的时间计算中断。因此,似乎更好的方法是让应用程序独立于服务器的时区工作。

其思想很简单,就是在将日期存储到数据库之前,始终以 UTC 格式创建日期,并且始终以 UTC 格式从存储的值重新创建日期。这样,时间计算就永远不会出错,因为它们总是以 UTC 为单位。这可以通过在创建 PHP DateTime对象时显式声明 DateTimeZone参数来实现。

另一方面,可以将客户端功能配置为将从服务器接收的所有日期/时间转换为客户端的时区。像 Moment.js 这样的图书馆使得这项工作非常容易。

例如,在数据库中存储日期时,不要使用 MySQL 的 NOW()函数,而是以 UTC 格式创建时间戳字符串,如下所示:

// Storing dates
$date = new DateTime('now', new DateTimeZone('UTC'));
$sql = 'insert into table_name (date_column) values ("' . $date . '")';


// Retreiving dates
$sql = 'select date_column from table_name where condition';
$dateInUTC = new DateTime($date_string_from_db, new DateTimeZone('UTC'));

您可以在 PHP 中为所有创建的日期设置默认时区,这样就不需要每次创建日期时都初始化 DateTimeZone 类。

检查时区

SELECT@@ System _ time _ zone 作为 System _ tz,@@ global.time _ zone 作为 MYSQL _ timeZone,NOW ()作为 timenow _ bymysql;

SET GLOBAL 时区 =’+ 05:30’;

或者

更改系统时区,然后重新启动 MariaDB/MYSQL

再查一下时区