使用 node-postgres 在 utc 中获取 Postgres“没有时区的时间戳”

Posted

技术标签:

【中文标题】使用 node-postgres 在 utc 中获取 Postgres“没有时区的时间戳”【英文标题】:Use node-postgres to get Postgres "timestamp without timezone" in utc 【发布时间】:2014-01-09 19:51:32 【问题描述】:

我有一些时间戳存储为 Postgres 类型 timestamp without time zone

我将使用时间戳2013-12-20 20:45:27 作为示例。我打算这代表一个 UTC 时间戳。

在 psql 中,如果我运行查询 SELECT start_time FROM table_name WHERE id = 1,我会按预期返回那个时间戳字符串:2013-12-20 20:45:27

但是,如果在我的 Node 应用程序中,我使用 node-postgres 库来运行相同的查询,我会返回本地时区的时间戳:Fri Dec 20 2013 20:45:27 GMT-0600 (CST)。这是一个 javascript 日期对象,但它已经存储为该时区。我真正想要的是一个代表2013-12-20 20:45:27 GMT+0000 的日期对象(甚至只是一个字符串)。我已经知道这次是 UTC。

我尝试将我的 postgresql.conf 文件中的时区参数设置为:timezone = 'UTC',结果没有差异。

我做错了什么?

编辑

问题似乎出在这个文件中:https://github.com/brianc/node-postgres/blob/master/lib/types/textParsers.js

如果从 Postgres 返回的日期字符串没有指定时区(即Z+06:30,那么它只会构造一个 JavaScript 日期对象,我相信它只会包含本地时区。我要么需要更改我的应用以在数据库中存储时区,要么覆盖此转换器。

【问题讨论】:

【参考方案1】:

这不是最好的解决方案,但我只是切换到使用 Postgres 类型 timestamp with time zone 并确保我保存到数据库的所有日期都是 UTC。

【讨论】:

就我而言,由于夏令时问题,这个解决方案无论如何都更安全。【参考方案2】:

不是要恢复一个老问题,而是看到我刚刚遇到完全相同的问题here,有一个替代解决方案可以通过覆盖用于timestamp without time zone 的类型解析器来工作:

var pg = require('pg');
var types = pg.types;
types.setTypeParser(1114, function(stringValue) 
return stringValue;
);

这将使 node-pg 无法将值解析为 Date 对象,而是为您提供原始时间戳字符串。

来源:从node-postgres issues得到它

【讨论】:

我不知道这是否仍然被认为是有效的,但它改变了解析器的行为以返回一个字符串而不是一个日期对象(至少在 pg-promise 的当前迭代中,它在幕后使用 node-postgres)。这破坏了我所有希望返回日期的代码,所以这个解决方案对我没有帮助。唯一对我有用的是在将 stringValue 传递给日期构造函数之前对其进行修改:return new Date(stringValue + '+0000')。希望看到一种更好的方法来从未指定时区的 ISO 8601 字符串创建 UTC 日期对象。 是的,但是 OP 说一个字符串就足够了。无论哪种方式,通过覆盖解析器,您可以轻松返回几乎任何您需要的东西,包括一个新的日期对象。 也适用于我,因为我在获取后仍然使用其他时间库,如“moment”或“luxon”。当他们将所有内容都转换为当地时间时,并不是那些 Date() 的粉丝...... 此处列出了所有解决方法60devs.com/…。我最终使用了 moment() 解决方案。【参考方案3】:

您可以按照@BadIdeaException 的建议修改解析器。以下是有关它为何无法按预期工作的更多详细信息,以及两种可能的解决方案。

对于timestamp without time zone 类型的列,解析器接收一个ISO 8601 格式的字符串,没有指定时区:2016-07-12 22:47:34

在 Javascript 中创建 Date 对象时,如果未指定时区,则假定日期在当前时区。对于定义为 GMT 时区的 UTC 日期,这将为您提供一个具有不正确的绝对值 (date.value) 的日期,除非您的 Javascript 恰好在 GMT 时区运行.

因此,无法通过 Date 构造函数将 ISO 8601 字符串直接转换为 UTC 日期。您的选择是:修改字符串,使其被解释为 UTC:

var pg = require('pg');
var types = pg.types;
types.setTypeParser(1114, function(stringValue) 
    return new Date(stringValue + "+0000");
);

或让您的日期使用“错误”(当前)时区创建,然后提取它的值(仍在您当前的时区),然后使用这些值在 UTC 时区生成日期.请注意,Date.UTC() 返回日期 而不是对象,然后可以将其传递给 Date 构造函数。

types.setTypeParser(1114, function(stringValue) 

    var temp = new Date(stringValue);
    return new Date(Date.UTC(
        temp.getFullYear(), temp.getMonth(), temp.getDate(), temp.getHours(), temp.getMinutes(), temp.getSeconds(), temp.getMilliseconds())
    );

【讨论】:

你节省了我的时间,伙计。谢谢 就我而言,我所做的是完全避免使用 Date 直到我完全知道时区,而是使用具有此架构的简单对象:year,month,day,hour,minutes,seconds ,然后仅在我知道要构建它的时区后才从中构建 ISO 字符串。【参考方案4】:

我想知道@BadIdeaException 从哪里得到数字 1114。 在打字稿中,可以看到界面中的值。

import  types  from 'pg';
types.setTypeParser(types.TypeId.TIME, (timeStr) => timeStr);
types.setTypeParser(types.TypeId.TIMESTAMP, (timeStr) => timeStr);
types.setTypeParser(types.TypeId.TIMESTAMPTZ, (timeStr) => timeStr);

这将覆盖所有时间戳字段解析器,并防止字段被错误地解析为 Date 对象。

【讨论】:

以上是关于使用 node-postgres 在 utc 中获取 Postgres“没有时区的时间戳”的主要内容,如果未能解决你的问题,请参考以下文章

使用 node-postgres 监听查询超时?

如何在服务器中使用 node-postgres?

如何在服务器中使用 node-postgres?

使用 node-postgres 查询的 Promise 总是返回 undefined

具有大量查询的 node-postgres

使用 node-postgres 更新数据的更简单方法?