10

We are having a debate about the best way to store a timestamp in postgres. Currently all time stamps are stored as +00 and we have a timezone associated with each client. We look up the timezone and convert the time that something happened which increases complexity as we need to do more joins and a more complex query.

Another method is connecting to Postgres and setting the timezone of the connection and it changes all the times to be that timezone.

My problem with this is that in ANZ there are 4-5 timezones. When we try and do our invoices we need to know what day certain transactions happened, and across three timezones there is no perfect solution.

I was thinking of including the timezone in the timestamp to make it easier - TIMESTAMP '1999-01-15 8:00:00 -8:00'

I was under the impression that this was best practice, but some people say this is a bad idea. We will have customers across ANZ that we need to do accurate invoices for, what is the best solution and most elegant?

Cheers Scott

4

2 回答 2

16

这里没有防弹解决方案。

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

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

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

  • 用于TIMESTAMP WITH TIMEZONE (timestamptz)存储主要与“物理”时间相关的事件,您主要感兴趣的是查询是否event 1之前event 2(不考虑时区),或计算时间间隔(以“物理单位”为单位,例如秒;不在“民用”单位,如日-月等)。典型的例子是记录创建/修改时间——“时间戳”这个词通常意味着什么。

  • 用于TIMESTAMP WITHOUT TIMEZONE (timestamp)存储相关信息为“公民时间” (即{year-month-day hour-min-sec}整个字段)且查询涉及日历计算的事件。在这种情况下,您将仅在此处存储“本地时间”,即相对于某些未指定(不相关、隐含或存储在其他地方)时区的日期时间。

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

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

另请参阅此答案

于 2013-01-31T00:46:31.780 回答
12

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

例子……</p>

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; 
于 2013-01-30T23:12:29.427 回答