Postgres时间戳

Sco*_*007 9 database postgresql timezone timestamp

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

另一种方法是连接到Postgres并设置连接的时区,它将所有时间都更改为该时区.

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

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

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

干杯斯科特

leo*_*loy 16

这里没有防弹解决方案.

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

我的第二个建议是:选择timestamp- timestamptz根据数据的(主要)语义.

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

  • 使用TIMESTAMP WITH TIMEZONE (timestamptz)用于存储主要是相关的事件"物理"的时间,为你的主要兴趣在询问是否event 1是之前event 2(不管时区),或计算的时间间隔(以"物理单位",例如,秒;不是"民间"单位为天 - 月等).典型的例子是记录创建/修改时间 - 通常用" Timestamp " 这个词来表示.

  • 使用TIMESTAMP WITHOUT TIMEZONE (timestamp)用于存储该相关信息是事件的"民间时间" (即字段{year-month-day hour-min-sec}作为一个整体),和查询涉及日历计算.在这种情况下,您只会在此处存储"本地时间",即相对于某些未指定(不相关或隐含或存储在其他地方)时区的日期时间.

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

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

另见这个答案.

  • 为了澄清差异:“TIMESTAMP WITH TIME ZONE”使用任何传递的 UTC 偏移量或时区信息来*调整*到 UTC。相反,“没有时区的时间戳”**忽略任何偏移量或时区**,无论是包含还是省略。仅当您想要日期时间的总体概念(例如“圣诞节于 2015 年 12 月 25 日午夜开始”)时才使用“WITHOUT”,当然,这意味着奥克兰的时刻与悉尼、珀斯、巴黎或西雅图的时刻不同。请参阅这位专家的帖子[始终使用带有时区的时间戳](http://justatheory.com/computers/databases/postgresql/use-timestamptz.html)。 (2认同)

Luc*_*cas 11

对输入字段使用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');
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM pg_timezone_names ORDER BY utc_offset DESC; 
Run Code Online (Sandbox Code Playgroud)