Postgresql:如何从时间戳,时区字段正确创建带时区的时间戳

时间:2013-09-28 00:51:32

标签: postgresql timezone timestamp

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

和一个字段“timezone”,对于Pacific来说是“P”,对于Mountain来说是“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

3 个答案:

答案 0 :(得分:1)

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

也就是说,可以使用应用于timestamp without time zone的{​​{3}}构造进行转换。

假设我们从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);

结果是:

        timezone        
------------------------
 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时区中首先发生的小时跨度,然后是DST切换后-08时区中的第二次。对postgres的解释是在-08时区看到它。如果我们考虑前一分钟,它会落入-07时区。

答案 1 :(得分:1)

如果您考虑他们的名字,TIMESTAMP WITHOUT TIME ZONETIMESTAMP WITH TIME ZONETIMESTAMPTZ)之间的区别可能非常棘手。 (事实上​​,规范似乎足够混乱,因此各种RDBMS以不同的方式实现它。)

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

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

据推测,您使用“{时区”列(PM)和TIMESTAMP WITHOUT TIME ZONE来补偿输入值中的歧义。< / p>

原则上,如果您与存储时间戳的时区位于相同的时区,则应该返回相同的值,因此如果您将客户端设置为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",您已经丢失了您赢得的信息'能够恢复。

如果您只是希望它在'-08'和'-07'之间切换,具体取决于该时刻的DST设置,这将自动为您完成,但您应该使用{{1}首先,确切地说,你代表的是哪个时刻。

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

TIMESTAMPTZ

结果:

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);

不幸的是,只有两个字段无法处理DST更改。

当然值得阅读Date/Time types section of the PostgreSQL manual,并注意AT TIME ZONE documentation中表格的“返回类型”列,以便更好地理解这些问题。

答案 2 :(得分: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