我们正在使用 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 set 与set 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 thepg_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 fromUTC
, 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 thepg_timezone_abbrevs
view (see Section 50.79). You cannot set the configuration parametersTimeZone
orlog_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$;
关于带时区的 Postgresql date_trunc 将时区移动 1 小时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38785924/