我在从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
但是都没有转换为时区中的正确日期。
我没有在你的问题中看到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
是timestamp [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'
基本上你想要的是:
$ 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)。
无论如何,这最后(不直观)的行为是导致所有混乱的原因。阅读文章如果你想要更全面的解释,我实际上得到的结果与他们在最后一部分有点不同,但总体思路是一样的。
我知道这是一个旧版本,但您可能需要考虑使用AT TIME ZONE“US / Pacific”进行投射以避免任何PST / PDT问题。所以
SELECT starts_at :: TIMESTAMPTZ AT TIME ZONE“US / Pacific”FROM schedule WHERE ID ='40';
cast(master.Stamp5DateTime as date) >= '05-05-2019' AND
cast(master.Stamp5DateTime as date) <= '05-05-2019'