Knex默默地将时区转换为Postgres时间戳,并返回错误的时间

时间:2018-06-28 12:20:22

标签: javascript postgresql knex.js bookshelf.js

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

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

从psql控制台运行时

SELECT trigger_time AT TIME ZONE 'UTC' 
FROM tasks 
WHERE task_id = 1;

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

类似地,当我跑步

SELECT trigger_time AT TIME ZONE 'America/Glace_Bay' 
FROM tasks 
WHERE task_id = 1;

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

使用knex.raw("SELECT trigger_time AT TIME ZONE 'America/Glace_Bay' FROM tasks WHERE task_id = 1")得到2018-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停止静默更改数据?

1 个答案:

答案 0 :(得分:1)

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

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

您可以像获取UTC一样获取时间,并在带有时间矩或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);

例如,可以使所有时间戳作为字符串返回的代码可以添加到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';

哪个将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;

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