Postgres时间戳

时间:2013-01-30 22:40:22

标签: database postgresql timezone timestamp

我们正在讨论在postgres中存储时间戳的最佳方法。目前,所有时间戳都存储为+00,并且我们有与每个客户端关联的时区。我们查找时区并转换发生事件的时间,这会增加复杂性,因为我们需要进行更多连接和更复杂的查询。

另一种方法是连接到Postgres并设置连接的时区,它会一直更改为该时区。

我的问题是在ANZ有4-5个时区。当我们尝试开发票时,我们需要知道某些交易发生的那一天,并且在三个时区之间没有完美的解决方案。

我在考虑将时区包含在时间戳中以使其更容易 - TIMESTAMP'1999-01-15 8:00:00 -8:00'

我的印象是这是最佳做法,但有些人说这是个坏主意。我们将为ANZ的客户提供准确的发票,最佳解决方案和最优雅的解决方案?

干杯 斯科特

2 个答案:

答案 0 :(得分:15)

这里没有防弹解决方案。

我的第一个建议:永远不要依赖服务器的默认时区。

我的第二个建议:根据数据的(主要)语义在timestamp - timestamptz之间进行选择。

更详细: PostgresSQL有两个时间戳变体,混淆地命名为 TIMESTAMP WITHOUT TIMEZONE (timestamp) TIMESTAMP WITH TIMEZONE (timestamptz) 。实际上,既不存储时区,也不存储偏移量。两种数据类型占用相同的宽度(4个字节),它们的区别很微妙 - 更糟糕的是,如果你不完全理解它们并且你的服务器改变了时区,它会咬你。我的理智规则是:

  • 使用 TIMESTAMP WITH TIMEZONE (timestamptz) 存储主要与“物理”时间相关的事件,您主要对查询是否感兴趣{ {1}}在event 1之前(无论时区如何),或计算时间间隔(以“物理单位”为单位,例如秒;不以“民用”单位表示为天 - 月等)。典型的例子是记录创建/修改时间 - 通常用“ Timestamp ”这个词来表示。

  • 使用 event 2 存储相关信息为“民事时间”的事件(即字段{{1}作为一个整体),查询涉及日历计算。在这种情况下,您只会在此处存储“本地时间”,即相对于某些未指定(不相关或隐含或存储在其他地方)时区的日期时间。

第二个选项让您更容易查询,例如“在2013-01-20'日发生的所有事件”(在每个相应的区域/国家/时区中) - 但却更难以查询“在参考事件之前(物理上)发生的所有事件“(除非我们知道它们在同一时区)。你选择。

如果您需要完整的东西,这两者都不够,您需要将时区或偏移量存储在附加字段中。另一个选项,浪费几个字节,但对查询更有效,是存储两个字段。

另见this answer

答案 1 :(得分:10)

对输入字段使用timestamptz(或timestamp with time zone作为标准SQL语法),然后可以使用时区或时间偏移为每个插入设置自定义时间偏移,以适合您的偏好。

示例...

CREATE TABLE "timetest"
(
"timestamp" timestamptz
);

INSERT INTO "timetest" ("timestamp") VALUES ('2013-01-01 08:45:00 PST');
INSERT INTO "timetest" ("timestamp") VALUES ('2013-01-01 08:45:00 Europe/Madrid');
INSERT INTO "timetest" ("timestamp") VALUES ('2013-01-01 08:45:00 Europe/Athens');
INSERT INTO "timetest" ("timestamp") VALUES ('2013-01-01 08:45:00 GMT+11');
INSERT INTO "timetest" ("timestamp") VALUES ('2013-01-01 08:45:00 GMT-11');
INSERT INTO "timetest" ("timestamp") VALUES ('2013-01-01 08:45:00 UTC');

......您的时间将相应调整

SELECT * FROM "timetest"; -- note this may default to your timezone
------------------------
[timestamp]
------------------------
2013-01-01 16:45:00+00
2013-01-01 07:45:00+00
2013-01-01 06:45:00+00
2012-12-31 21:45:00+00
2013-01-01 19:45:00+00
2013-01-01 08:45:00+00
2013-01-01 08:45:00+00

或者更好的是,尝试以下内容......

SELECT 
"timestamp" AT TIME ZONE 'Australia/Sydney' AS "Sydney", 
"timestamp" AT TIME ZONE 'Australia/Perth' AS "Perth" 
FROM "timetest";
--------------------------------------------
[Sydney]..............[Perth]
--------------------------------------------
2013-01-02 03:45:00 - 2013-01-02 00:45:00
2013-01-01 18:45:00 - 2013-01-01 15:45:00
2013-01-01 17:45:00 - 2013-01-01 14:45:00
2013-01-01 08:45:00 - 2013-01-01 05:45:00
2013-01-02 06:45:00 - 2013-01-02 03:45:00
2013-01-01 19:45:00 - 2013-01-01 16:45:00

最后,要了解可用于数据库的时区列表,请尝试:

SELECT * FROM pg_timezone_names ORDER BY utc_offset DESC;