使用 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“没有时区的时间戳”的主要内容,如果未能解决你的问题,请参考以下文章