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

标签 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

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

最佳答案

date_trunc(text, timestamptz) 变体的文档似乎有点不足,所以这是我的发现:

1) 在day 精度(第一个参数)之下,结果的时区offset 始终与第二个参数的偏移量相同。

2) 在 day 精度或更高精度时,根据当前 TimeZone 配置参数(其中 can be setset time zone '...'set TimeZone to '...')。重新计算的偏移量始终与在 same TimeZone 配置参数生效的那个确切时间点上的偏移量相同。所以,f.ex。当 TimeZone 参数包含 DST 信息时,则相应地对齐偏移量。但是,当实际的 TimeZone 参数不包含 DST 信息(例如固定偏移量)时,结果的时区偏移量将保持不变。

总而言之,date_trunc(text, timestamptz) 函数可以用 date_trunc(text, timestamp) 变体和 at time 来模拟区域运算符s:

date_trunc('month', tstz)

应该等同于:

date_trunc('month', tstz at time zone current_setting('TimeZone')) at time zone current_setting('TimeZone'))

至少,我是这么想的。事实证明,有一些 TimeZone 配置设置存在问题。因为:

PostgreSQL allows you to specify time zones in three different forms:

  • A full time zone name, for example America/New_York. The recognized time zone names are listed in the pg_timezone_names view (see Section 50.80). PostgreSQL uses the widely-used IANA time zone data for this purpose, so the same time zone names are also recognized by much other software.

  • A time zone abbreviation, for example PST. Such a specification merely defines a particular offset from UTC, in contrast to full time zone names which can imply a set of daylight savings transition-date rules as well. The recognized abbreviations are listed in the pg_timezone_abbrevs view (see Section 50.79). You cannot set the configuration parameters TimeZone or log_timezone to a time zone abbreviation, but you can use abbreviations in date/time input values and with the AT TIME ZONE operator.

(第三个是固定偏移量,或者它的 POSIX 形式,但这在这里并不重要)。

如您所见,abbreviations 不能设置为 TimeZone。但是有一些缩写,也被认为是完整时区名称,f.ex。 英语考试。因此,set time zone 'CET' 会成功,但实际上会在夏令时使用 CEST。但是 at time zone 'CET' 将始终引用 abbreviation,它是 UTC 的固定偏移量(永远不会 CEST,因为可以使用 at time zone 'CEST';但 set time zone 'CEST' 无效。

这是时区设置的完整列表,当它们用于 set time zone 与用于 at time zone 时具有不兼容的含义(从 9.6 开始):

CET
EET
MET
WET

使用以下脚本,您可以检查您的版本:

create or replace function incompatible_tz_settings()
  returns setof text
  language plpgsql
as $func$
declare
  cur cursor for select name from pg_timezone_names;
begin
  for rec IN cur loop
    declare
      r pg_timezone_names;
    begin
      r := rec;
      execute format('set time zone %L', (r).name);
      if exists(select 1
                from   generate_series(current_timestamp - interval '12 months', current_timestamp + interval '12 months', interval '1 month') tstz
                where  date_trunc('month', tstz) <> date_trunc('month', tstz at time zone (r).name) at time zone (r).name) then
        return next (r).name;
      end if;
    end;
  end loop;
end
$func$;

http://rextester.com/GBL17756

关于带时区的 Postgresql date_trunc 将时区移动 1 小时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38785924/

相关文章:

arrays - JSONB 会使 PostgreSQL 数组变得无用吗?

postgresql - 在go中保留来自postgresql时间戳的时区信息

mysql - 如何在脚本中处理MySQL时区

安卓时区困惑

sql - 几乎相同的 PostgreSQL 查询有 1 分钟的差异?

postgresql - 索引对使用 && 的相交查询没有影响

postgresql - 使用 Rails、Postgres 和 Sidekiq 导入 CSV

php - 外部服务器上的关系表上的 Laravel where() 和 orderBy()

postgresql - pg_restore 使用不正确的数据库名称

SQL Server 作业/时间表 - 美国与英国夏令时调整