带时区的Postgresql date_trunc将区域移动1小时

时间:2016-08-05 09:30:55

标签: postgresql timezone postgresql-9.4

我们正在使用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

这是预期的行为吗?如果是这样的话背后的逻辑是什么?

2 个答案:

答案 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_abbrevsTimeZone设置为时区缩写,但您可以在日期/时间输入值和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

相关问题