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 ZONE
将timestamp
重新解释为在该时区中,以将其转换为UTC 。
timestamp with time zone AT TIME ZONE
的timestamptz
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';