我们正在讨论在postgres中存储时间戳的最佳方法。目前,所有时间戳都存储为+00,并且我们有与每个客户端关联的时区。我们查找时区并转换发生事件的时间,这会增加复杂性,因为我们需要进行更多连接和更复杂的查询。
另一种方法是连接到Postgres并设置连接的时区,它会一直更改为该时区。
我的问题是在ANZ有4-5个时区。当我们尝试开发票时,我们需要知道某些交易发生的那一天,并且在三个时区之间没有完美的解决方案。
我在考虑将时区包含在时间戳中以使其更容易 - TIMESTAMP'1999-01-15 8:00:00 -8:00'
我的印象是这是最佳做法,但有些人说这是个坏主意。我们将为ANZ的客户提供准确的发票,最佳解决方案和最优雅的解决方案?
干杯 斯科特
答案 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;