Joe*_*ner 6 mysql timestamp timezone
我已经调试了很多,最后设法在 mysql shell 中重现它。
我将时间戳存储在字段类型为 的 mysql 数据库中timestamp。我用来FROM_UNIXTIME()从我的脚本中更新它们,并UNIX_TIMESTAMP()在选择它们时使用。
当我没有在使用SET time_zone =它的连接中设置时区时,它工作正常。但是当我设置时区时,会发生以下情况:
UNIX_TIMESTAMP() 仍然给出正确的结果。UPDATE table SET field = FROM_UNIXTIME(..) 在数据库中设置错误的值。我知道是 FROM_UNIXTIME() 不起作用,因为当我打开另一个没有连接特定时区的连接时,我看到错误的值,直到我再次更新它。
它是 1 小时的差异这一事实让我认为这可能是夏令时问题。因为柏林有夏令时而曼谷没有(据我所知)。
这是 mysql shell 的未修改日志,我在其中重现了此行为。
服务器时区是亚洲/曼谷(CIT)
$ mysql -uroot -p timezonetest
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 530
Server version: 5.7.16-0ubuntu0.16.10.1 (Ubuntu)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SELECT @@system_time_zone;
+--------------------+
| @@system_time_zone |
+--------------------+
| ICT |
+--------------------+
1 row in set (0.00 sec)
mysql> describe test;
+------------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------+------+-----+-------------------+-----------------------------+
| payment_id | int(11) | NO | PRI | NULL | auto_increment |
| begins_at | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+-----------+------+-----+-------------------+-----------------------------+
2 rows in set (0.00 sec)
mysql> select * from test;
+------------+---------------------+
| payment_id | begins_at |
+------------+---------------------+
| 338840 | 2013-10-27 08:15:33 |
+------------+---------------------+
1 row in set (0.00 sec)
mysql> select unix_timestamp(begins_at) from test where payment_id = 338840;
+---------------------------+
| unix_timestamp(begins_at) |
+---------------------------+
| 1382836533 |
+---------------------------+
1 row in set (0.00 sec)
mysql> set time_zone = 'CET';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+------------+---------------------+
| payment_id | begins_at |
+------------+---------------------+
| 338840 | 2013-10-27 02:15:33 |
+------------+---------------------+
1 row in set (0.00 sec)
mysql> select unix_timestamp(begins_at) from test where payment_id = 338840;
+---------------------------+
| unix_timestamp(begins_at) |
+---------------------------+
| 1382836533 |
+---------------------------+
1 row in set (0.00 sec)
mysql> update test set begins_at = from_unixtime(1382836533) where payment_id = 338840;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select unix_timestamp(begins_at) from test where payment_id = 338840;
+---------------------------+
| unix_timestamp(begins_at) |
+---------------------------+
| 1382832933 |
+---------------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+------------+---------------------+
| payment_id | begins_at |
+------------+---------------------+
| 338840 | 2013-10-27 02:15:33 |
+------------+---------------------+
1 row in set (0.00 sec)
mysql>
Run Code Online (Sandbox Code Playgroud)
另一个日志:
mysql> describe test;
+------------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------+------+-----+-------------------+-----------------------------+
| payment_id | int(11) | NO | PRI | NULL | auto_increment |
| begins_at | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+-----------+------+-----+-------------------+-----------------------------+
2 rows in set (0.00 sec)
mysql> select * from test;
+------------+---------------------+
| payment_id | begins_at |
+------------+---------------------+
| 338840 | 2013-10-27 02:15:33 |
+------------+---------------------+
1 row in set (0.00 sec)
mysql> set time_zone = 'Europe/Berlin';
Query OK, 0 rows affected (0.00 sec)
mysql> select unix_timestamp(begins_at) from test;
+---------------------------+
| unix_timestamp(begins_at) |
+---------------------------+
| 1382832933 |
+---------------------------+
1 row in set (0.00 sec)
mysql> update test set begins_at = from_unixtime(1382836533);
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
mysql> select unix_timestamp(begins_at) from test;
+---------------------------+
| unix_timestamp(begins_at) |
+---------------------------+
| 1382832933 |
+---------------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+------------+---------------------+
| payment_id | begins_at |
+------------+---------------------+
| 338840 | 2013-10-27 02:15:33 |
+------------+---------------------+
1 row in set (0.00 sec)
mysql> set time_zone = 'Asia/Bangkok';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+------------+---------------------+
| payment_id | begins_at |
+------------+---------------------+
| 338840 | 2013-10-27 07:15:33 |
+------------+---------------------+
1 row in set (0.00 sec)
mysql> select unix_timestamp(begins_at) from test;
+---------------------------+
| unix_timestamp(begins_at) |
+---------------------------+
| 1382832933 |
+---------------------------+
1 row in set (0.00 sec)
mysql> update test set begins_at = from_unixtime(1382836533);
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select unix_timestamp(begins_at) from test;
+---------------------------+
| unix_timestamp(begins_at) |
+---------------------------+
| 1382836533 |
+---------------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+------------+---------------------+
| payment_id | begins_at |
+------------+---------------------+
| 338840 | 2013-10-27 08:15:33 |
+------------+---------------------+
1 row in set (0.00 sec)
mysql>
Run Code Online (Sandbox Code Playgroud)
MySQL 运行正常 – 您的测试无效。
如果您使用 DST 来回穿越时区,则在遇到转换时将不会进行无损转换。有问题的时间戳发生在“CET”和“Europe/Berlin”的 DST 转换期间。
亚洲/曼谷有两个挂钟时间对应于欧洲/柏林的一个挂钟时间。
mysql> SELECT CONVERT_TZ('2013-10-27 08:15:33','Asia/Bangkok','Europe/Berlin');
+------------------------------------------------------------------+
| CONVERT_TZ('2013-10-27 08:15:33','Asia/Bangkok','Europe/Berlin') |
+------------------------------------------------------------------+
| 2013-10-27 02:15:33 |
+------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT CONVERT_TZ('2013-10-27 07:15:33','Asia/Bangkok','Europe/Berlin');
+------------------------------------------------------------------+
| CONVERT_TZ('2013-10-27 07:15:33','Asia/Bangkok','Europe/Berlin') |
+------------------------------------------------------------------+
| 2013-10-27 02:15:33 |
+------------------------------------------------------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
将其转换为 UTC 进行检查...
mysql> select convert_tz('2013-10-27 02:59:59','Europe/Berlin','UTC');
+---------------------------------------------------------+
| convert_tz('2013-10-27 02:59:59','Europe/Berlin','UTC') |
+---------------------------------------------------------+
| 2013-10-27 00:59:59 |
+---------------------------------------------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
两秒后……
mysql> select convert_tz('2013-10-27 03:01:01','Europe/Berlin','UTC');
+---------------------------------------------------------+
| convert_tz('2013-10-27 03:01:01','Europe/Berlin','UTC') |
+---------------------------------------------------------+
| 2013-10-27 02:01:01 |
+---------------------------------------------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
……一小时两秒后。
或者,翻转它。
mysql> SET @@time_zone = 'CET';
mysql> SELECT FROM_UNIXTIME(1382825733) AS zero,
FROM_UNIXTIME(1382825733 + 3600) AS one,
FROM_UNIXTIME(1382825733 + 3600 + 3600) as two,
FROM_UNIXTIME(1382825733 + 3600 + 3600 + 3600) as three,
FROM_UNIXTIME(1382825733 + 3600 + 3600 + 3600 + 3600) as four;
+---------------------+---------------------+---------------------+---------------------+---------------------+
| zero | one | two | three | four |
+---------------------+---------------------+---------------------+---------------------+---------------------+
| 2013-10-27 00:15:33 | 2013-10-27 01:15:33 | 2013-10-27 02:15:33 | 2013-10-27 02:15:33 | 2013-10-27 03:15:33 |
+---------------------+---------------------+---------------------+---------------------+---------------------+
^^ ... wait, what? .. ^^
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
如果您在过渡时间内对不明确的值进行时区转换,则转换不是无损的。
时间戳的操作需要是 UTC 端到端的。在一侧或另一侧使用FROM_UNIXTIME()或UNIX_TIMESTAMP()使用本机 UTC 值,但该值仍会在另一侧转换为您的会话时区(或服务器时区,如果未设置会话时区) - 在成为或从TIMESTAMP列中的值的方式(实际上存储为 UTC,并转换为/从您的会话时区)。
这是您的服务器时钟应始终使用 UTC 的原因之一。
| 归档时间: |
|
| 查看次数: |
14708 次 |
| 最近记录: |