CURRENT_TIME在MySQL中插入行时时间戳不正确,我正在使用AWS RDS

时间:2018-12-26 21:46:32

标签: mysql time amazon-rds node-mysql2

编辑:这是我正在使用node-mysql2的mysql库的问题

我正在使用AWS RDS托管MySQL数据库。

我的数据库中有一列具有以下定义

process.env.PUBLIC_URL

当我在美国东部标准时间下午4:43插入一行时,该列的值如下所示:

createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP

当我尝试将此值转换为EST时

2018-12-27T02:43:32.000Z

这是不正确的。

我做错了什么,还是需要配置?

3 个答案:

答案 0 :(得分:0)

这不是AWS或MySQL的问题

我弄清楚了问题所在,node-mysql2决定转换时间戳。我不知道它在做什么转换或原因。

我用来修复的设置是

let options = {
    ...,
    timezone: 'UTC', // Interpret all received timestamps as UTC. Otherwise local timezone is assumed.
    dateStrings: [
        'DATE', // DATE's are returned as strings (otherwise they would be interpreted as YYYY-MM-DD 00:00:00+00:00)
        'DATETIME' // DATETIME's return as strings (otherwise they would interpreted as YYYY-MM-DD HH:mm:ss+00:00)
    ]
}

答案 1 :(得分:0)

我认为该测试很有趣,尽管它不能回答您的问题,但确实使节点库脱离了方程式,我认为这很有趣。

CREATE DATABASE IF NOT EXISTS test;
USE test;

CREATE TEMPORARY TABLE t (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  created DATETIME DEFAULT CURRENT_TIMESTAMP,
  tz varchar(32)
);

select "datetime";

SET time_zone = "CST6CDT";
insert into t (tz) values (@@SESSION.time_zone);

SET time_zone = "EST5EDT";
insert into t (tz) values (@@SESSION.time_zone);

SET time_zone = "UTC";
insert into t (tz) values (@@SESSION.time_zone);

SET time_zone = "CST6CDT";
select *, @@SESSION.time_zone FROM t;

SET time_zone = "EST5EDT";
select *, @@SESSION.time_zone FROM t;

SET time_zone = "UTC";
select *, @@SESSION.time_zone FROM t;

DROP TEMPORARY TABLE t;

select "timestamp";

CREATE TEMPORARY TABLE t (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  tz varchar(32)
);

SET time_zone = "CST6CDT";
insert into t (tz) values (@@SESSION.time_zone);

SET time_zone = "EST5EDT";
insert into t (tz) values (@@SESSION.time_zone);

SET time_zone = "UTC";
insert into t (tz) values (@@SESSION.time_zone);

SET time_zone = "CST6CDT";
select *, @@SESSION.time_zone FROM t;

SET time_zone = "EST5EDT";
select *, @@SESSION.time_zone FROM t;

SET time_zone = "UTC";
select *, @@SESSION.time_zone FROM t;

在这里,我创建一个带有DEFAULT CURRENT_TIMESTAMP列的临时表。我将会话变量设置为CST,EST和UTC的每一个,以测试一些不同的本地时区,然后插入会话时区以跟踪哪个时区创建了哪个值。然后,我将每个时区设置为我的会话时区,再次选择它们,以显示插入会话的时区和选择会话的时区如何相互作用。

然后,我完全像以前一样重复整个操作,但是第二次使用TIMESTAMP字段创建了列,而不是DATETIME。

我在docker容器中运行它只是为了进行测试,我以此开始:

docker run --rm -d -e MYSQL_ALLOW_EMPTY_PASSWORD=true --name mysql mysql

但是从任何可以访问服务器的mysql客户端运行应该很容易;这是我的docker exec调用:

$ docker exec -i mysql mysql -t  < t.sql
+----------+
| datetime |
+----------+
| datetime |
+----------+
+----+---------------------+---------+---------------------+
| id | created             | tz      | @@SESSION.time_zone |
+----+---------------------+---------+---------------------+
|  1 | 2018-12-27 11:07:05 | CST6CDT | CST6CDT             |
|  2 | 2018-12-27 12:07:05 | EST5EDT | CST6CDT             |
|  3 | 2018-12-27 17:07:05 | UTC     | CST6CDT             |
+----+---------------------+---------+---------------------+
+----+---------------------+---------+---------------------+
| id | created             | tz      | @@SESSION.time_zone |
+----+---------------------+---------+---------------------+
|  1 | 2018-12-27 11:07:05 | CST6CDT | EST5EDT             |
|  2 | 2018-12-27 12:07:05 | EST5EDT | EST5EDT             |
|  3 | 2018-12-27 17:07:05 | UTC     | EST5EDT             |
+----+---------------------+---------+---------------------+
+----+---------------------+---------+---------------------+
| id | created             | tz      | @@SESSION.time_zone |
+----+---------------------+---------+---------------------+
|  1 | 2018-12-27 11:07:05 | CST6CDT | UTC                 |
|  2 | 2018-12-27 12:07:05 | EST5EDT | UTC                 |
|  3 | 2018-12-27 17:07:05 | UTC     | UTC                 |
+----+---------------------+---------+---------------------+
+-----------+
| timestamp |
+-----------+
| timestamp |
+-----------+
+----+---------------------+---------+---------------------+
| id | created             | tz      | @@SESSION.time_zone |
+----+---------------------+---------+---------------------+
|  1 | 2018-12-27 11:07:05 | CST6CDT | CST6CDT             |
|  2 | 2018-12-27 11:07:05 | EST5EDT | CST6CDT             |
|  3 | 2018-12-27 11:07:05 | UTC     | CST6CDT             |
+----+---------------------+---------+---------------------+
+----+---------------------+---------+---------------------+
| id | created             | tz      | @@SESSION.time_zone |
+----+---------------------+---------+---------------------+
|  1 | 2018-12-27 12:07:05 | CST6CDT | EST5EDT             |
|  2 | 2018-12-27 12:07:05 | EST5EDT | EST5EDT             |
|  3 | 2018-12-27 12:07:05 | UTC     | EST5EDT             |
+----+---------------------+---------+---------------------+
+----+---------------------+---------+---------------------+
| id | created             | tz      | @@SESSION.time_zone |
+----+---------------------+---------+---------------------+
|  1 | 2018-12-27 17:07:05 | CST6CDT | UTC                 |
|  2 | 2018-12-27 17:07:05 | EST5EDT | UTC                 |
|  3 | 2018-12-27 17:07:05 | UTC     | UTC                 |
+----+---------------------+---------+---------------------+

如您所见,DATETIME始终存储在会话的本地时间。对于我来说,如何知道是否将其转换为其他时区并不是很明显,因为该时区显然是在内部存储为UTC,但显然并未针对SELECT sesson的时区进行校正。

另一方面,时间戳的行为有所不同。在这些情况下,时间戳在被选择时会正确转换为SELECT的会话时间。我认为这通常是数据库客户端期望的行为(我设置了会话时区,因此请向我显示该会话时区中的日期)。

简而言之,如果要存储本地时间,请使用datetime。如果要存储“绝对时间”,请使用时间戳。不知道这是否是一个好规则,但是根据我的测试,这似乎是一个好方法。

当然,正如您所指出的,客户端确实仍然必须设置他们希望使用的时区。将所有内容都设置为使用相同的时区可以避免整个问题:)

答案 2 :(得分:0)

绝对不要使用任何数据库日期,时间或时间戳格式。 Oracle,MySQL和MS SQL Server对此问题有帮助。当我们从变速箱工厂装运的变速箱在装运之前到达组装工厂时,我就很难理解这一点。大型汽车公司的物流非常庞大-每天超过一百万英里的18轮客流。安排的很好,就是大钱,所以我们被告知立即修复!

事实证明,操作系统认为它在一个时区中,数据库确信它在另一个时区中,当我们登录时,每个用户ID都被设置为另一个时区。

唯一的解决方案是将所有时间记录为GMT毫秒。这样,您可以按时间间隔检索事件,比较时间,计算时间间隔并合并来自许多来源的数据。您不必担心以对每个用户有意义的方式显示时间,但是至少您不必担心基础计算中的愚蠢行为。

从不,从不使用数据库时间戳-它们以有趣的方式将您弄乱。