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

Erw*_*ter 7 postgresql timezone datetime

我只是花了一个小时绝望,这两个表达式的结果有差异:


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
Run Code Online (Sandbox Code Playgroud)

显然,第二个表达式根据DST规则扣除两个小时,其中第一个表达式仅使用标准偏移量.

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

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
Run Code Online (Sandbox Code Playgroud)

我查阅了关于时区PostgreSQL手册:

PostgreSQL允许您以三种不同的形式指定时区:

全时区名称,例如America/New_York.识别的时区名称列在pg_timezone_names视图中(参见第45.67节).PostgreSQL为此目的使用广泛使用的zoneinfo时区数据,因此许多其他软件也识别相同的名称.

时区缩写,例如PST.这样的规范仅定义了与UTC的特定偏移,而不是全时区名称,其也可以暗示一组夏令时转换日期规则.公认的缩写列在pg_timezone_abbrevs视图中(参见第45.66节).您不能将配置参数timezone或log_timezone设置为时区缩写,但可以在日期/时间输入值和AT TIME ZONE运算符中使用缩写.

大胆的重点我的.

为什么差异呢?

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

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
Run Code Online (Sandbox Code Playgroud)

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

我不知道时区名称的CET来源.但显然它在我的装置中存在.对sqlfiddle快速测试显示了相同的结果.

我在具有类似设置的两个不同服务器上测试 还有PostgreSQL 8.4.发现"CET"作为时区名称pg_timezone_names的所有的人.

Erw*_*ter 4

在我发布此内容后,我立即运行了另一个查询来检查可疑之处:

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
Run Code Online (Sandbox Code Playgroud)

事实证明,还有一个时区缩写名为CET(这是有道理的,“CET”是缩写)。PostgreSQL 似乎选择了缩写而不是全名。因此,即使我CET在时区名称中发现,表达式 '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
Run Code Online (Sandbox Code Playgroud)

我在时区名称中发现了 10 个时区缩写,但无法理解为什么会出现这些缩写。目的是什么?

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

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
Run Code Online (Sandbox Code Playgroud)

在这些情况下,人们可能会被愚弄(就像我一样),查找 tz名称并找到实际未应用的时间偏移。这是一个不幸的设计——即使不是一个错误,至少也是一个文档错误

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

附录 B.1。日期/时间输入解释提到了时区缩写的查找,但仍不清楚如何识别时区名称以及在令牌不明确的情况下哪个时区名称具有优先权。

如果标记是文本字符串,则与可能的字符串进行匹配:

对作为时区缩写的标记进行二分查找表查找。

嗯,这句话中有一点暗示,缩写是第一位的,但没有什么明确的。abbrev此外,两个表中都有一列,pg_timezone_names并且pg_timezone_abbrevs......