PostgreSQL date()带有时区

Mat*_*ner 43 postgresql timezone datetime rails-postgresql postgresql-9.1

我在从Postgres正确选择日期时遇到问题 - 它们以UTC格式存储,但未正确转换为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
Run Code Online (Sandbox Code Playgroud)

转换给出了这个:

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

但是都没有转换为时区中的正确日期.

Erw*_*ter 34

我没有看到你问题的确切类型starts_at.你真的应该包含这些信息,这是解决方案的关键.我不得不猜.

基本上,PostgreSQL 总是timestamp with time zone内部存储该类型的UTC时间值.只有显示屏因当前timezone设置而异.AT TIME ZONE构造的效果也随基础数据类型而变化.更多细节:

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

如果date从类型中提取timestamp with time zone(timestamptz简称),则首先"应用"时区偏移.您仍然可以获得当前时区的日期,这与时间戳的显示一致.同一时间点转换为欧洲部分地区的第二天,例如在加利福尼亚州的下午4点过去.要获取特定时区的日期,AT TIME ZONE请先申请.

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

鉴于这starts_at是a timestamp [without time zone]并且服务器上的时间设置为本地时间.测试:

SELECT now();
Run Code Online (Sandbox Code Playgroud)

它与墙上的时钟显示的时间相同吗?如果是(并且数据库服务器以正确的时间运行),则timezone当前会话的设置与您的本地时区一致.如果不是,您可能需要访问timezonepostgresql.conf或您的客户的会话设置.手册中的详细信息.

请注意,timezone偏移量使用时间戳文字中显示的相反符号.看到:

要获得当地的日期starts_at只是

SELECT starts_at::date
Run Code Online (Sandbox Code Playgroud)

无异于:

SELECT date(starts_at)
Run Code Online (Sandbox Code Playgroud)

顺便说一下,你现在的时间是UTC-7,而不是UTC-8,因为夏令时有效(不是人类更明智的想法).

太平洋标准时间(PST)通常timestamp比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'
Run Code Online (Sandbox Code Playgroud)


Ami*_*itF 33

基本上你想要的是:

$ select starts_at AT TIME ZONE 'UTC' AT TIME ZONE 'US/Pacific' from schedules where id = 40
Run Code Online (Sandbox Code Playgroud)

我从这篇文章得到的解决方案如下,这是直的黄金!它非常清楚地解释了这个非平凡的问题,如果你想更好地理解pstgrsql TZ管理,请给它一个读.

在本地时间表示没有区域的PostgreSQL时间戳

这是正在发生的事情.首先你应该知道'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
Run Code Online (Sandbox Code Playgroud)

将工作.但是,如果您的时间戳是无时区的,执行上述命令将不起作用,您必须首先将您的无时区时间戳转换为带有时区的时间戳,即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
Run Code Online (Sandbox Code Playgroud)

这将产生:

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

最后一个时间戳是在postgresql中将无时区时间戳从UTC转换为'PST'的所有混淆的原因.当我们写:

timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d
Run Code Online (Sandbox Code Playgroud)

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

无论如何,这最后(不直观)的行为是导致所有混乱的原因.阅读文章如果你想要更全面的解释,我实际上得到的结果与他们在最后一部分有点不同,但总体思路是一样的.

  • 感谢您的详细解释。在读到这篇文章之前我完全不知所措! (2认同)
  • 我会说这是一个更好的被接受的答案,因为关于“首先转换为 UTC”的额外信息,因为这是我面临的问题(没有时区怪癖的时间戳)。 (2认同)

小智 8

我知道这是一个旧版本,但您可能需要考虑使用AT TIME ZONE"US/Pacific"进行投射以避免任何PST/PDT问题.所以

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