3

我有一个没有时区的带有时间戳的表。YYYY-MM-DD HH:MM:SS

和一个字段“时区”,“P”代表太平洋或“M”代表山区。

我需要创建一个“带有时区的时间戳”类型的字段

鉴于我拥有的两个字段,有没有办法正确考虑夏令时?

具体来说:时间戳:2013-11-03 01:00:00 时区:“P”将变为:2013-11-03 01:00:00-07

和时间戳:2013-11-03 03:00:00 时区:“P”将变为:2013-11-03 03:00:00-08

4

3 回答 3

1

首先,当说结果会变成 example2013-11-03 01:00:00-07时,应该补充一点,这实际上取决于 SQL 客户端的时区设置。例如,欧洲时间的会话永远不会读取2013-11-03 01:00:00-07为 a 的值,timestamp with time zone因为没有欧洲国家在GMT-07

也就是说,可以通过将AT TIME ZONE构造应用于timestamp without time zone.

假设我们从US/Pacific时区运行它:

SET time zone 'US/Pacific';

SELECT t AT TIME ZONE 
     case z when 'P' then 'US/Pacific' when 'M' then 'US/Mountain' end  
  from (values
    ('2013-11-03 01:00:00'::timestamp, 'P'),
    ('2013-11-03 03:00:00'::timestamp, 'P')
  ) as v(t,z);

结果是:

        时区        
----------------------
 2013-11-03 01:00:00-08
 2013-11-03 03:00:00-08

2013-11-03 01:00:00 AT time zone 'US/Pacific'具有歧义,因为它属于在-07时区中首先发生的小时跨度,然后-08在 DST 切换后的时区中第二次发生。postgres的解释是看-08时区。如果我们考虑前一分钟,它属于-07时区。

于 2013-09-28T11:24:42.793 回答
1

TIMESTAMP WITHOUT TIME ZONE如果您考虑它们的名称,则和TIMESTAMP WITH TIME ZONE( )之间的区别TIMESTAMPTZ可能会非常难以理解。(事实上​​,规范似乎很混乱,以至于各种 RDBMS 以不同的方式实现它。)

在 PostgreSQL 中,两种类型都不会存储值存储时的时区,而是TIMESTAMPTZ将值存储为基于 UTC 引用的精确时刻,而TIMESTAMP WITHOUT TIME ZONE始终是相对的。

  • 查询时,TIMESTAMPTZ将调整 a 以表示与最初存储的时间相同的时刻(在世界的任何地方),就像它在客户端配置的当前时区中的时刻一样。
  • 相对于客户端配置的时区, ATIMESTAMP WITHOUT TIME ZONE始终是相同的值,即使您查询它的时区不同:由 表示的时刻2013-11-03 03:00:00将是不明确的,并且取决于客户端设置。

据推测,您使用“时区”列(PMTIMESTAMP WITHOUT TIME ZONE来补偿输入值的歧义。

原则上,如果您与存储时间戳的相对时区处于相同的相对时区,则应该返回相同的值,因此,如果您已将客户端设置在US/Pacific时区并且您已存储2013-11-03 03:00:00在您的P时间区,你应该2013-11-03 03:00:00回来。但是,这仅在相对值没有歧义时才有效。

您的第一个示例中的问题是已经存在一些歧义:

时间戳:2013-11-03 01:00:00 时区:“P”将变为:2013-11-03 01:00:00-07

2013-11-03 01:00:00可以表示时区中两个不同的US/Pacific时间点,因此仅使用2013-11-03 01:00:00"P",您已经丢失了无法恢复的信息。

如果您只是希望它根据当时的 DST 设置在“-08”和“-07”之间切换,这将自动为您完成,但您应该TIMESTAMPTZ首先使用 a精确到您所代表的时间点。

这是保留初始时区的示例,因此您可以看到“-08”和“-07”之间的变化:

SET time zone 'US/Pacific';

SELECT t AS "Date/Time for US/Pacific",
       t AT time zone 'UTC' "Date/Time in UTC"
FROM (VALUES
    ('2013-11-03 00:00:00-07'::timestamptz),
    ('2013-11-03 01:00:00-07'::timestamptz),
    ('2013-11-03 02:00:00-07'::timestamptz),
    ('2013-11-03 03:00:00-07'::timestamptz)) AS v(t);

结果:

| DATE/TIME FOR US/PACIFIC | DATE/TIME IN UTC    |
|--------------------------|---------------------|
| 2013-11-03 00:00:00-07   | 2013-11-03 07:00:00 |
| 2013-11-03 01:00:00-07   | 2013-11-03 08:00:00 |
| 2013-11-03 01:00:00-08   | 2013-11-03 09:00:00 |
| 2013-11-03 02:00:00-08   | 2013-11-03 10:00:00 |

不幸的是,没有办法仅使用您的两个字段来处理 DST 更改。

当然值得阅读PostgreSQL 手册的日期/时间类型部分,并注意AT TIME ZONE文档中表格的“返回类型”列,以便更好地理解这些问题。

于 2013-09-28T14:31:09.060 回答
0

检查这对您是否有意义

set timezone to 'PST8PDT';

select now();
              now              
-------------------------------
 2013-09-28 03:24:20.169189-07

select ts,
    ts at time zone 'PST' as "PST",
    ts at time zone 'PDT' as "PDT"
from (values
    ('2013-11-03 01:00:00'::timestamp),
    ('2013-11-03 02:00:00'),
    ('2013-11-03 03:00:00')
) s (ts)
;
         ts          |          PST           |          PDT           
---------------------+------------------------+------------------------
 2013-11-03 01:00:00 | 2013-11-03 01:00:00-08 | 2013-11-03 01:00:00-07
 2013-11-03 02:00:00 | 2013-11-03 02:00:00-08 | 2013-11-03 01:00:00-08
 2013-11-03 03:00:00 | 2013-11-03 03:00:00-08 | 2013-11-03 02:00:00-08
于 2013-09-28T11:11:46.973 回答