29

我在从Postgres中正确选择日期的问题 - 它们以UTC格式存储,但 不能正确使用Date()函数进行转换。PostgreSQL date()带时区

将时间戳转换为日期会给我错误的日期,如果它已经过了太平洋标准时间下午4点。

2012-06-21在这种情况下应该是2012-06-20

starts_at列数据类型为timestamp without time zone。这里是我的查询:

,无须转换为PST时区:

Select starts_at from schedules where id = 40; 

     starts_at  
--------------------- 
2012-06-21 01:00:00 

转换给出了这样的:

Select (starts_at at time zone 'pst') from schedules where id = 40; 
     timezone   
------------------------ 
2012-06-21 02:00:00-07 

但无论是转换成在时区的正确日期。

回答

22

我没有在您的问题中看到确切类型的starts_at。你真的应该包括这些信息,这是解决方案的关键。我必须猜测。

基本上,PostgreSQL 总是在内部存储类型为timestamp with time zone的UTC时间值。只有显示内容因当前的timezone设置而异。 AT TIME ZONE构造的效果也随底层数据类型而变化。更多详细信息在this related answer

如果从类型timestamp [without time zone]中提取date,则会得到当前时区的日期。输出中的日期将与timestamp值的显示相同。

如果从类型timestamp with time zone(简称timestamptz)中提取date,则首先应用时区偏移量。您仍会得到当前时区的日期,该日期与时间戳的显示一致。同样的时间点转化为第二天在欧洲部分地区,当时已经是下午4点了。以加利福尼亚州为例。要获取某个时区的日期,请首先应用AT TIME ZONE

因此,您在问题顶部所描述的内容与您的示例相矛盾。

鉴于starts_attimestamp [without time zone]和你的服务器上的时间设置为本地时间。测试:

SELECT now(); 

它显示与墙上的时钟相同的时间吗?如果是,则当前会话的timezone设置与您当地的时区一致。如果不是,您可能想要在postgresql.conf中访问timezone的设置。或者,也许你的客户做了一些设置? (可以设置每个会话。)Details in the manual.

starts_at获得当地的日期只是

SELECT starts_at::date 

等于是:

SELECT date(starts_at) 

顺便说一句,你的本地时间为UTC-7,现在,不是UTC-8,因为夏令时是有效的(不包括人类的更明亮的想法)。

太平洋标准时间(PST)通常比UTC(世界时区)早8小时,但在夏令时期间(如现在)则为7小时。这就是为什么timestamptz在您的示例中显示为2012-06-21 02:00:00-07。构造AT TIME ZONE 'PST'将夏令时考虑在内。这两个表达式产生不同的结果(一个在冬天,一个夏天),并可能导致不同的日期施放时:

SELECT '2012-06-21 01:00:00'::timestamp AT TIME ZONE 'PST' 
     ,'2012-12-21 01:00:00'::timestamp AT TIME ZONE 'PST' 
+0

这是正确的 - 'PST'不考虑夏时制,请参阅John Rennpferd关于使用“US/Pacific”与“PST”或“PDT”的答案 –

6

我知道这是一个古老的,但您可能要“考虑使用AT TIME ZONE US /太平洋“,以避免任何PST/PDT问题。所以

SELECT starts_at :: TIMESTAMPTZ AT TIME ZONE“US/Pacific”FROM schedule WHERE ID ='40';

+0

如果您需要历史日期,这是绝对正确的来表示他们发生的实际时间。 –

19

基本上你想要的是:

$ select starts_at AT TIME ZONE 'UTC' AT TIME ZONE 'US/Pacific' from schedules where id = 40 

我从这篇文章的解决方案是下面,这是直的黄金!它非常清楚地解释了这个不重要的问题,如果您希望更好地理解pstgrsql TZ管理,请给它一个解读。

Expressing PostgreSQL timestamps without zones in local time

这里是正在发生的事情。首先,您应该知道“PST时区比UTC时区晚8小时,因此例如2014年1月1日,太平洋标准时间下午4:30(周三,2014年1月1日16:00:30 -0800)相当于2014年1月2日00:30上午UTC(周四,2014年1月2日00:00:30 +0000)。任何时候在太平洋标准时间下午4点后到第二天,解释为UTC。

另外,正如Erwin Brandstetter上面提到的,postresql有两种类型的时间戳数据类型,一种带有时区,另一种没有时区。 如果时间戳包括时区,然后进行简单:

$ select starts_at AT TIME ZONE 'US/Pacific' from schedules where id = 40 

会工作。但是,如果您的时间戳是无时区的,执行上述命令将不起作用,您必须首先将您的无时区时间戳转换为带有时区(即UTC时区)的时间戳,然后将其转换为所需的'PST'或'US /太平洋'(这是相同的一些夏令时问题,我认为你应该罚款)。

让我演示一个例子,我创建一个无时区的时间戳。为方便起见,假设我们当地的时区确实是'PST'(如果不是这样,它会变得稍微复杂一些,这对于此解释来说是不必要的)。

说我有:

$ select timestamp '2014-01-2 00:30:00' AS a, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AS b, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AT TIME ZONE 'PST' AS c, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d 

这将产生:

"a"=>"2014-01-02 00:30:00" (This is the timezoneless timestamp) 
"b"=>"2014-01-02 00:30:00+00" (This is the UTC TZ timestamp, note that up to a timezone, it is equivalent to the timezoneless one) 
"c"=>"2014-01-01 16:30:00" (This is the correct 'PST' TZ conversion of the UTC timezone, if you read the documentation postgresql will not print the actual TZ for this conversion) 
"d"=>"2014-01-02 08:30:00+00" 

最后的时间戳是所有关于PostgreSQL的UTC的转换timezoneless时间戳 'PST' 混乱的原因。当我们写:

timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d 

我们正在采取timezoneless时间戳,并尝试将其转换为“PST TZ(我们间接地假设PostgreSQL将明白,我们希望它的时间戳从UTC TZ转换,但postresql有它自己的计划!)。在实践中,postgresql所做的是需要无时区的时间戳('2014-01-2 00:30:00),并将其视为它已经实现了'PST'TZ时间戳(即:2014-01-2 00:30:00 :00 -0800)并将其转换为UTC时区!所以它实际上推动了8小时而不是回来!因此我们得到(2014-01-02 08:30:00 + 00)。

无论如何,这最后(不直观)的行为是造成所有混淆的原因。如果你想要更全面的解释,请阅读文章,其实我得到的结果与最后一部分有所不同,但总的想法是一样的。

+0

感谢您的详细解释。在阅读本文之前,我完全失败了! –

相关问题