67

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

但两者都不会转换为时区中的正确日期。

4

4 回答 4

65

基本上你想要的是:

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

我从这篇文章中得到的解决方案如下,这是纯金!!!它非常清楚地解释了这个重要的问题,如果您想更好地了解 pstgrsql TZ 管理,请阅读它。

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

这是正在发生的事情。首先,您应该知道'PST 时区比 UTC 时区晚 8 小时,因此例如 2014 年 1 月 1 日下午 4:30 PST(2014 年 1 月 1 日星期三 16:00:30 -0800)相当于 2014 年 1 月 2 日 00:30上午 UTC(2014 年 1 月 2 日星期四 00:00:30 +0000)。PST 下午 4:00 之后的任何时间都会滑到第二天,解释为 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 转换为“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)。

无论如何,最后一个(不直观的)行为是所有混乱的原因。如果您想要更彻底的解释,请阅读文章,实际上我得到的结果与最后一部分的结果有些不同,但总体思路是相同的。

于 2014-11-18T00:21:01.390 回答
50

我在您的问题中看不到确切的类型。starts_at您确实应该包含此信息,这是解决方案的关键。我将不得不猜测。

PostgreSQL总是在内部存储该类型的 UTC 时间timestamp with time zone。输入和输出(显示)调整为当前timezone设置或给定时区。的效果AT TIME ZONE也会随着基础数据类型的变化而变化。看:

如果您date从 type中提取一个timestamp [without time zone],您将获得当前时区的日期。输出中的日期将与timestamp值显示中的相同。

如果您date从类型中提取一个timestamp with time zonetimestamptz简称),则首先“应用”时区偏移量。您仍然可以获得当前时区的日期,与时间戳的显示一致。同一时间点在欧洲部分地区转化为第二天,例如在加利福尼亚时间已超过下午 4 点。要获取某个时区的日期,AT TIME ZONE请先申请。

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

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

SELECT now();

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

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

获取您的本地starts_at日期

SELECT starts_at::date

等于:

SELECT date(starts_at)

顺便说一句,您的当地时间现在是 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'
于 2012-06-22T00:02:39.163 回答
11

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

SELECT starts_at::TIMESTAMPTZ AT TIME ZONE "US/Pacific" 
  FROM schedules 
 WHERE ID = '40';
于 2014-08-04T16:43:33.067 回答
-1
cast(master.Stamp5DateTime as date) >= '05-05-2019' AND 

cast(master.Stamp5DateTime as date) <= '05-05-2019' 
于 2019-06-07T09:35:51.817 回答