Knex 默默地将 Postgres 时间戳转换为时区并返回不正确的时间

Spa*_*ear 8 javascript postgresql bookshelf.js knex.js

我的 psql 数据库中有一个表,其中有一个类型为“TIMESTAMP WITH TIME ZONE DEFAULT now()”的“trigger_time”列

我行中的数据是这样的2018-06-27 15:45:00-03

从 psql 控制台运行时

SELECT trigger_time AT TIME ZONE 'UTC' 
FROM tasks 
WHERE task_id = 1;
Run Code Online (Sandbox Code Playgroud)

此查询返回“2018-06-27 18:45:00”。

同样,当我跑步时

SELECT trigger_time AT TIME ZONE 'America/Glace_Bay' 
FROM tasks 
WHERE task_id = 1;
Run Code Online (Sandbox Code Playgroud)

我得到 2018-06-27 15:45:00

使用knex.raw("SELECT trigger_time AT TIME ZONE 'America/Glace_Bay' FROM tasks WHERE task_id = 1")I get2018-06-27T18:45:00.000Z和运行时knex.raw("SELECT trigger_time AT TIME ZONE 'UTC' FROM tasks WHERE task_id = 1")我得到2018-06-27T21:45:00.000Z

knex 的这两个结果都不正确,我如何让 knex 停止默默地更改我的数据?

Mik*_*stö 8

事情可能会失败,因为当您在特定时区从数据库查询日期时间并有效地将时间戳类型转换为没有时区的时间戳时。在这种情况下,数据库不会向 knex 发送有关返回时间所在时区的信息。

因此 knex (或者更确切地说 knex 使用的 pg 驱动程序)将您的时间戳解释为本地时间,这取决于运行 knex 的应用程序服务器的时区设置。

您可以像 UTC 一样获取时间,并使用 moment 或 luxon 库在 JavaScript 端进行时区转换(IMO 后者更适合时区处理)。

其他解决方案是告诉 pg 驱动程序时间戳和带有时区类型的时间戳不应转换为JavaScript Date对象。

可以这样完成(https://github.com/brianc/node-pg-types):

const types = require('pg').types;
const TIMESTAMPTZ_OID = 1184;
const TIMESTAMP_OID = 1114;
types.setTypeParser(TIMESTAMPTZ_OID, val => val);
types.setTypeParser(TIMESTAMP_OID, val => val);
Run Code Online (Sandbox Code Playgroud)

此代码使所有时间戳作为字符串返回,可以添加到例如 start of 中knexfile.js。这些返回的字符串将与数据库服务器本身返回的格式完全相同。

编辑:

在原始帖子的代码中,当时间戳转换为时区时,UTC数据库服务器将timestamp with time zone类型转换为正常timestamp without time zone,因此返回的值没有时区信息。要添加时区信息,您可以将 +02 添加到返回时间戳的末尾,如下所示:

select ('2010-01-01T00:00:00.000Z'::timestamptz AT TIME ZONE 'UTC')::text || '+00';
Run Code Online (Sandbox Code Playgroud)

它返回2010-01-01 00:00:00+00到驱动程序,也可以被 pg 驱动程序正确读取。

SET TIME ZONE 'UTC';这将有效地执行与创建连接时在数据库服务器中设置并直接返回 timestamptz 列相同的操作:

SET TIME ZONE 'UTC';
select '2010-01-01T00:00:00.000+02:00'::timestamptz;
Run Code Online (Sandbox Code Playgroud)

哪个会返回2009-12-31 22:00:00+00