PostgreSQL date()带有时区

问题描述 投票:41回答:3

我在从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

转换给出了这个:

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

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

postgresql datetime timezone postgresql-9.1 rails-postgresql
3个回答
30
投票

我没有在你的问题中看到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_attimestamp [without time zone],服务器上的时间设置为当地时间。测试:

SELECT now();

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

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

要从starts_at获取当地日期

SELECT starts_at::date

无异于:

SELECT date(starts_at)

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

太平洋标准时间(PST)通常比UTC(通用时区)早8小时(更大的timestamp值),但在夏令时期间(如现在)可以是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'

31
投票

基本上你想要的是:

$ 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,postgresql有两种类型的时间戳数据类型,一种带有时区,一种带有时区。如果您的时间戳包含时区,那么简单:

$ 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转换为'PST'的所有混淆的原因。当我们写:

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

我们正在采用无时区的时间戳并尝试将其转换为'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)。

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


7
投票

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

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


0
投票
cast(master.Stamp5DateTime as date) >= '05-05-2019' AND 

cast(master.Stamp5DateTime as date) <= '05-05-2019' 
© www.soinside.com 2019 - 2024. All rights reserved.