PostgreSQL错误的时间戳没有时区转换时间戳与时区

今天上午我面临以下问题:

select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT'; 

回复我2011-12-30 05:30:00+00女巫是错的。

但接下来的查询如下:

 select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'UTC-5'; select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT'; 

我看到正确的date2011-12-29 19:30:00

防止你对我的本地时区的问题:

 SELECT current_setting('TIMEZONE'); current_setting ----------------- UTC (1 row) 

timestamp without time zone人有答案为什么postgresql转换timestamp without time zone一些怪异的方式,而是带走5小时,而不是增加呢?

关键的事情要了解

timestamp without time zone AT TIME ZONEtimestamp 重新解释为在该时区中,以将其转换为UTC

timestamp with time zone AT TIME ZONEtimestamptz timestamp with time zone AT TIME ZONE timestamptz转换为指定时区的timestamp

PostgreSQL使用ISO-8601时区,它指定格林威治以东是正的…除非你使用POSIX时区说明符,在这种情况下它遵循POSIX。 疯狂随之而来。

为什么第一个产生了意想不到的结果

SQL中的时间戳和时区非常可怕。 这个:

 select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT'; 

将未知types的文字'2011-12-30 00:30:00'timestamp without time zone ,除非另有说明,否则Pg认为该timestamp without time zone在本地TimeZone中。 当你使用AT TIME ZONE ,它(根据规格)被重新解释为时区EST5EDT中的时区的timestamp with time zone然后以UTC的绝对时间存储 – 所以它 EST5EDT转换 UTC,即时区偏移量减去x - (-5)x + 5

此时间戳调整为UTC存储,然后针对您的服务器TimeZone设置进行调整以显示,以便在当地时间显示。

如果你希望说“我有UTC时间的这个时间戳,并且希望看到EST5EDT中的等效本地时间是什么”,如果你想独立于服务器的TimeZone设置,你需要写下如下内容:

 select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST5EDT'; 

这说“给定的时间戳2011-12-30 00:30:00,把它当UTC时间戳转换为timestamptz,然后将该时间戳转换为本地时间在EST5EDT”。

太可怕了,不是吗? 我想要坚定地与任何决定AT TIME ZONE的疯狂语义的人交谈 – 它应该是timestamp CONVERT FROM TIME ZONE '-5'timestamptz CONVERT TO TIME ZONE '+5' 。 此外, timestamp with time zone实际上应该带有其时区,而不是以UTC存储并自动转换为本地时间。

为什么第二个工作(只要TimeZone = UTC)

你原来的“作品”版本:

 select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT'; 

只有当TimeZone被设置为UTC时才是正确的,因为text-to-timestamptz强制在没有指定的时候会假设TimeZone。

为什么第三个工作

两个问题相互抵消了。

似乎工作的另一个版本独立于TimeZone,但它只能工作,因为两个问题自行消失。 首先,如上所述, timestamp without time zone AT TIME ZONE的时间戳将时间戳重新解释为在该时区中以转换为UTC时间戳; 这有效地减去了时区偏移量。

然而,由于我超出我的理由,PostgreSQL使用反向符号的时间戳我以前看到的大部分地方。 请参阅文档 :

另一个要记住的问题是,在POSIX时区名称中,格林威治以西的位置使用正偏移量。 在其他地方,PostgreSQL遵循ISO-8601惯例,正面时区偏移量在格林威治东部。

这意味着EST5EDT+5相同,而不是-5 。 这就是为什么它的工作原理:因为你正在减去tz偏移量而不是增加它,但是你正在减去一个否定偏移量!

你需要正确的做法是:

 select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC' AT TIME ZONE '+5';