应用于时间戳时,具有相同属性的时区名称会产生不同的结果

我只是在绝望中度过了一个小时,这两个expression式的结果不一致:

db=# SELECT '2012-01-18 1:0 CET '::timestamptz AT TIME ZONE 'UTC' ,'2012-01-18 1:0 Europe/Vienna '::timestamptz AT TIME ZONE 'UTC'; timezone | timezone ---------------------+--------------------- 2012-08- 18 00:00:00 | 2012-08- 17 23:00:00 

显然,第二个expression式按照DST规则扣两小时,第一个expression式只使用标准偏移量。

我检查了这两个时区名称的目录。 他们都在那里,看起来也一样:

 db=# SELECT * FROM pg_timezone_names WHERE name IN ('CET', 'Europe/Vienna'); name | abbrev | utc_offset | is_dst ---------------+--------+------------+-------- Europe/Vienna | CEST | 02:00:00 | t CET | CEST | 02:00:00 | t 

我咨询了关于时区的PostgreSQL手册 :

PostgreSQL允许你以三种不同的forms指定时区:

全时区名称,例如America / New_York。 识别的时区名称在pg_timezone_names视图中列出(请参阅第45.67节)。 PostgreSQL为此使用了广泛使用的zoneinfo时区数据,所以同样的名字也被许多其他软件所识别。

时区缩写,例如PST。 这样的规范仅仅定义了UTC的一个特定的偏移量,而全时区名称可能意味着一套夏令时过渡date规则。 公认的缩写列在pg_timezone_abbrevs视图中(请参阅第45.66节)。 您不能将configuration参数timezone或log_timezone设置为时区缩写,但是您可以在date/时间input值中使用缩写和AT TIME ZONE运算符。

大胆强调我的。

那么为什么差异呢?

我的设置(更多细节添加)

  • Debian Squeeze上的PostgreSQL 9.1.4(来自http://backports.debian.org/debian-backports的标准挤压backports)

  • 本地timezone设置默认为系统区域设置de_AT.UTF-8 ,但应该与示例无关。

 SELECT version(); version ------------------------------------------------------------------------------------------------------- PostgreSQL 9.1.4 on x86_64-unknown-linux-gnu, compiled by gcc-4.4.real (Debian 4.4.5-8) 4.4.5, 64-bit SHOW timezone_abbreviations; timezone_abbreviations ------------------------ Default 

..哪(我假设)从这个文件加载缩写:/usr/share/postgresql/9.1/timezonesets/Default

我不知道时区名CET从哪里来。 但显然这是在我的设施。 对sqlfiddle快速testing显示相同的结果。

我使用类似的设置在两台不同的服务器上testing 也用PostgreSQL 8.4。 在所有的pg_timezone_names中find'CET'作为时区名称

我发布这个后,我跑了另一个查询来检查怀疑:

 SELECT * FROM pg_timezone_abbrevs WHERE abbrev IN ('CEST', 'CET'); abbrev | utc_offset | is_dst --------+------------+-------- CEST | 02:00:00 | t CET | 01:00:00 | f 

事实certificate,还有一个名为CET的时区缩写 (有意义,“CET”是缩写)。 而且PostgreSQL似乎在全名中select了缩写。 因此,尽pipe我在时区名称中find了CET ,但expression式“2012-01-18 1:0 CET”:: timestamptz是根据时区缩写的细微差别规则来解释的。

 SELECT '2012-01-18 1:0 CEST'::timestamptz(0) ,'2012-01-18 1:0 CET'::timestamptz(0) ,'2012-01-18 1:0 Europe/Vienna'::timestamptz(0); timestamptz | timestamptz | timestamptz ------------------------+------------------------+------------------------ 2012-01-18 00:00:00+01 | 2012-01-18 01:00:00+01 | 2012-01-18 01:00:00+01 SELECT '2012-08-18 1:0 CEST'::timestamptz(0) ,'2012-08-18 1:0 CET'::timestamptz(0) ,'2012-08-18 1:0 Europe/Vienna'::timestamptz(0); timestamptz | timestamptz | timestamptz ------------------------+------------------------+------------------------ 2012-08-18 01:00:00+02 | 2012-08-18 02:00:00+02 | 2012-08-18 01:00:00+02 

我在时区名称中find了10个时区缩写的例子,但却不明白为什么那些在那里。 目的是什么?

其中,由于DST设置,时间偏移( utc_offset )在四种情况下不一致:

 SELECT n.*, a.* FROM pg_timezone_names n JOIN pg_timezone_abbrevs a ON a.abbrev = n.name WHERE n.utc_offset <> a.utc_offset; name | abbrev | utc_offset | is_dst | abbrev | utc_offset | is_dst ------+--------+------------+--------+--------+------------+-------- CET | CEST | 02:00:00 | t | CET | 01:00:00 | f EET | EEST | 03:00:00 | t | EET | 02:00:00 | f MET | MEST | 02:00:00 | t | MET | 01:00:00 | f WET | WEST | 01:00:00 | t | WET | 00:00:00 | f 

在这些情况下,人们可能会被愚弄(像我一样),查找tz的名字,并find一个实际上没有应用的时间偏移量。 这是一个不幸的devise – 如果不是一个错误,至less是一个文档错误

我在手册中找不到关于时区名称缩写之间的歧义如何解决的任何内容。 显然缩写优先。

附录B.1。 date/时间input解释提到了时区缩写的查找,但仍不清楚时区名称是如何识别的,以及哪些在具有模糊标记的情况下具有优先权。

如果令牌是文本string,则匹配可能的string:

对令牌执行二进制search表查找作为时区缩写。

那么在这句话中有一个小小的提示:缩写是第一位的,但没有任何确切的意思。 此外,这两个表中都有一个列abbrevpg_timezone_namespg_timezone_abbrevs

时区缩写不包括夏令时(DST)转换规则的原因是它们往往意味着一个状态。 在美国的中西部地区,我们在冬季的几个月里,在中央标准时间(CST),以及在今年余下的时间,在中央夏令时间(CDT)上。 有exception的地方不使用DST,因此变得复杂。

PostgreSQL不保留自己的时区数据,尽pipe它在每个发行版上打包了最新的Olson时区数据,但是没有提供这些数据。 一般来说,PostgreSQL会使用来自操作系统的时区信息,所以如果你有问题,确保你有最新的版本。

作为参考,在我今天的系统中,我得到了这些结果:

 test =#SELECT'2012-01-18 1:0 CET':: timestamptz AT TIME ZONE'UTC'
 test-#,'2012-01-18 1:0欧洲/维也纳':: timestamptz AT TIME ZONE'UTC';
      时区| 时区       
 --------------------- + ---------------------
  2012-01-18 00:00:00 |  2012-01-18 00:00:00
 (1排)

 test =#SELECT * FROM pg_timezone_names WHERE name IN('CET','Europe / Vienna');
     名字|  abbrev |  utc_offset |  is_dst 
 --------------- + -------- + ------------ + --------
  CET |  CEST |  02:00:00 |  Ť
 欧洲/维也纳|  CEST |  02:00:00 |  Ť
 (2行)
 test =#SELECT * FROM pg_timezone_abbrevs
 test-#WHERE缩写IN('CEST','CET');
  abbrev |  utc_offset |  is_dst 
 -------- + ------------ + --------
  CEST |  02:00:00 |  Ť
  CET |  01:00:00 |  F
 (2行)

 test =#SELECT'2012-01-18 1:0 CEST':: timestamptz(0)
 test-#,'2012-01-18 1:0 CET':: timestamptz(0)
 test-#,'2012-01-18 1:0 Europe / Vienna':: timestamptz(0);
       timestamptz |  timestamptz |  timestamptz       
 ------------------------ + ------------------------ + ------------------------
  2012-01-17 17:00:00-06 |  2012-01-17 18:00:00-06 |  2012-01-17 18:00:00-06
 (1排)

 test =#SELECT'2012-08-18 1:0 CEST':: timestamptz(0)
 test-#,'2012-08-18 1:0 CET':: timestamptz(0)
 test-#,'2012-08-18 1:0欧洲/维也纳':: timestamptz(0);
       timestamptz |  timestamptz |  timestamptz       
 ------------------------ + ------------------------ + ------------------------
  2012-08-17 18:00:00-05 |  2012-08-17 19:00:00-05 |  2012-08-17 18:00:00-05
 (1排)