我们正在使用Postgresql 9.4,我注意到使用date_trunc时出现了一种奇怪的行为。结果中的时区移动了1小时:
select date_trunc('year','2016-08-05 04:01:58.372486-05'::timestamp with time zone);
date_trunc
------------------------
2016-01-01 00:00:00-06
截断到例如白天时没有这种行为:
select date_trunc('day','2016-08-05 04:01:58.372486-05'::timestamp with time zone);
date_trunc
------------------------
2016-08-05 00:00:00-05
这是预期的行为吗?如果是这样的话背后的逻辑是什么?
答案 0 :(得分:3)
date_trunc(text, timestamptz)
变体似乎有点记录不足,所以这是我的发现:
1)低于day
精度(第一个参数),结果的时区偏移始终与第二个参数的偏移量相同。
2)在day
精度或更高时,根据当前TimeZone
配置参数(can be set与{{} http://rextester.com/GBL17756重新计算时区偏移量 1}}或set time zone '...'
)。重新计算的偏移量始终与使用相同 set TimeZone to '...'
配置参数的确切时间瞬间相同。所以,f.ex。当TimeZone
参数包含DST 信息时,相应地对齐偏移量。但是,当实际的TimeZone
参数不包含DST信息(例如修正偏移)时,结果的时区偏移量不会受到影响。
总而言之,可以使用TimeZone
变体和date_trunc(text, timestamptz)
运算符 s 来模拟date_trunc(text, timestamp)
函数:
at time zone
应该相当于:
date_trunc('month', tstz)
至少,这就是我的想法。事实证明,有一些date_trunc('month', tstz at time zone current_setting('TimeZone')) at time zone current_setting('TimeZone'))
配置设置,这是有问题的。这是因为:
PostgreSQL允许您以三种不同的形式指定时区:
全时区名称,例如
TimeZone
。已识别的时区名称列在America/New_York
视图中(参见第50.80节)。 PostgreSQL为此目的使用广泛使用的IANA时区数据,因此相同的时区名称也被许多其他软件识别。时区缩写,例如
pg_timezone_names
。这样的规范仅定义了与PST
的特定偏移,而不是全时区名称,这也暗示了一组夏令时转换日期规则。公认的缩写列在UTC
视图中(参见第50.79节)。您不能将配置参数pg_timezone_abbrevs
或TimeZone
设置为时区缩写,但您可以在日期/时间输入值和AT TIME ZONE运算符中使用缩写。
(第三个是修正偏移,或者它的POSIX形式,但这里并不重要。)
如您所见,缩写无法设置为log_timezone
。但是有一些缩写,也被认为是全时区名称,f.ex。 TimeZone
。因此,CET
会成功,但实际上会在夏令时使用set time zone 'CET'
。但是CEST
将始终引用缩写,这是at time zone 'CET'
(而不是UTC
的固定偏移量,因为它可以使用CEST
;但at time zone 'CEST'
无效)。
以下是时区设置的完整列表,它们在set time zone 'CEST'
中使用时与在set time zone
中使用时(从9.6开始)时具有不兼容的含义:
at time zone
使用以下脚本,您可以检查您的版本:
CET
EET
MET
WET
{{3}}
答案 1 :(得分:1)
预计会有date_trunc
的两个变体:一个用于timestamp
,另一个用于timestamptz
,因为the doc说:
下面描述的所有功能和操作员需要时间或 时间戳输入实际上有两种变体:一种需要时间 带有时区的时区或时间戳,以及需要时间的时区 没有时区或没有时区的时间戳。为简洁起见,这些 变体不单独显示。
如果您想更好地了解时间戳和时间戳,请先阅读the great answer here。
然后关于date_trunc
。根据我对各种SO答案的实验和解释(如this one),一切都表现得好像,当收到时间戳时,date_trunc
首先将其转换为时间戳。此转换以本地时间返回时间戳。然后执行截断:仅保留日期并删除小时/分钟/秒。
为了避免这种转换(感谢pozs),请为date_trunc提供时间戳(不是timestamptz):
date_trunc('day', TIMESTAMPTZ '2001-07-16 23:38:40Z' at time zone 'UTC')
部分at time zone 'UTC'
表示"将此时间戳转换为UTC时间的时间戳" (小时不受此转换的影响)。然后date_trunc返回2001-07-16 00:00:00
。