CURRENT_TIME 在 MySQL 中插入行时时间戳不正确,我使用的是 AWS RDS
CURRENT_TIME Timestamp is incorrect when inserting row in MySQL, I'm using AWS RDS
编辑:这是我使用 node-mysql2
的 mysql 库的问题
我正在使用 AWS RDS 托管 MySQL 数据库。
我的数据库中有一列定义如下
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
当我在美国东部时间 4:43 下午插入一行时,我得到该列的以下值
2018-12-27T02:43:32.000Z
当我尝试将此值转换为 EST 时,我得到
12/26/2018, 9:43:32 PM
这是不正确的。
我是不是做错了什么或者我需要配置什么?
这不是 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)
]
}
我认为这个测试很有趣,虽然它没有回答你的问题,但它确实将节点库排除在外。
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;
这里我创建了一个临时的 table 和一个 DEFAULT CURRENT_TIMESTAMP
列。我将会话变量分别设置为 CST、EST 和 UTC 以测试一些不同的本地时区,然后插入会话时区以跟踪哪个时区创建了哪个值。然后,我再次 select 将每个时区设置为我的会话时区,以显示插入会话的时区和 select 会话的时区如何相互作用。
然后我再次执行整个操作,与之前完全一样,但我第二次创建了包含 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 的时区进行更正。
另一方面,时间戳的行为不同。在这些情况下,时间戳在 selected 时被正确转换为 SELECT 的会话时间。我认为这通常是数据库客户端所期望的行为(我设置了一个会话时区,所以显示该会话时区中的日期)。
简而言之,如果要存储本地时间,请使用datetime。如果要存储“绝对时间”,请使用时间戳。不确定这是否是一个好规则,但根据我的测试,这似乎是一个好方法。
当然,正如您所指出的,客户端确实仍然需要设置他们希望使用的时区。将所有内容设置为使用相同的时区可以避免整个问题:)
切勿使用任何数据库日期、时间或时间戳格式。 Oracle、MySQL 和 MS SQL 服务器是否如本问题所述“偏爱”。当我们从变速箱工厂运来的变速箱在发货前到达装配厂时,我以非常艰难的方式了解到这一点。在一家大型汽车公司中,物流是巨大的——每天有超过 100 万英里的 18 轮车辆交通。安排好时间是一大笔钱,所以我们被告知要立即修复它!
原来操作系统认为是一个时区,数据库认为是另一个时区,当我们登录时,每个用户ID都设置了不同的时区。
唯一的解决办法是将所有时间记录为 GMT 毫秒。这样,您就可以检索某个时间间隔内的事件、比较时间、计算时间间隔以及合并来自多个来源的数据。您必须担心以对每个用户都有意义的方式显示时间,但至少您不必担心基础计算中的愚蠢。
永远、永远不要使用数据库时间戳 - 它们会以有趣的方式让您感到困惑。
编辑:这是我使用 node-mysql2
的 mysql 库的问题我正在使用 AWS RDS 托管 MySQL 数据库。
我的数据库中有一列定义如下
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
当我在美国东部时间 4:43 下午插入一行时,我得到该列的以下值
2018-12-27T02:43:32.000Z
当我尝试将此值转换为 EST 时,我得到
12/26/2018, 9:43:32 PM
这是不正确的。
我是不是做错了什么或者我需要配置什么?
这不是 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)
]
}
我认为这个测试很有趣,虽然它没有回答你的问题,但它确实将节点库排除在外。
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;
这里我创建了一个临时的 table 和一个 DEFAULT CURRENT_TIMESTAMP
列。我将会话变量分别设置为 CST、EST 和 UTC 以测试一些不同的本地时区,然后插入会话时区以跟踪哪个时区创建了哪个值。然后,我再次 select 将每个时区设置为我的会话时区,以显示插入会话的时区和 select 会话的时区如何相互作用。
然后我再次执行整个操作,与之前完全一样,但我第二次创建了包含 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 的时区进行更正。
另一方面,时间戳的行为不同。在这些情况下,时间戳在 selected 时被正确转换为 SELECT 的会话时间。我认为这通常是数据库客户端所期望的行为(我设置了一个会话时区,所以显示该会话时区中的日期)。
简而言之,如果要存储本地时间,请使用datetime。如果要存储“绝对时间”,请使用时间戳。不确定这是否是一个好规则,但根据我的测试,这似乎是一个好方法。
当然,正如您所指出的,客户端确实仍然需要设置他们希望使用的时区。将所有内容设置为使用相同的时区可以避免整个问题:)
切勿使用任何数据库日期、时间或时间戳格式。 Oracle、MySQL 和 MS SQL 服务器是否如本问题所述“偏爱”。当我们从变速箱工厂运来的变速箱在发货前到达装配厂时,我以非常艰难的方式了解到这一点。在一家大型汽车公司中,物流是巨大的——每天有超过 100 万英里的 18 轮车辆交通。安排好时间是一大笔钱,所以我们被告知要立即修复它!
原来操作系统认为是一个时区,数据库认为是另一个时区,当我们登录时,每个用户ID都设置了不同的时区。
唯一的解决办法是将所有时间记录为 GMT 毫秒。这样,您就可以检索某个时间间隔内的事件、比较时间、计算时间间隔以及合并来自多个来源的数据。您必须担心以对每个用户都有意义的方式显示时间,但至少您不必担心基础计算中的愚蠢。
永远、永远不要使用数据库时间戳 - 它们会以有趣的方式让您感到困惑。