【问题标题】:Postgresql: how to correctly create timestamp with timezone from timestamp, timezone fieldsPostgresql:如何从时间戳,时区字段正确创建带有时区的时间戳
【发布时间】:2013-10-04 10:04:47
【问题描述】:

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

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

我需要创建一个类型为“timestamp with time zone”的字段

鉴于我拥有的两个字段,有没有一种方法可以正确考虑夏令时?

具体来说: 时间戳: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

【问题讨论】:

  • 您的问题在编辑后更有意义,但基本问题仍然存在。 “2013-11-03 01:00:00”仍然不明确:它可以是“2013-11-03 00:59:00-07”之后的一分钟或“2013-11-03 01:59”之后的一分钟: 00-07"。

标签: postgresql timezone timestamp


【解决方案1】:

首先,当说结果会变成例如2013-11-03 01:00:00-07时,应该补充一点,这实际上取决于SQL客户端的时区设置。例如,欧洲时间的会话永远不会将2013-11-03 01:00:00-07 读取为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 时区首先发生的小时跨度,然后在 DST 切换后的 -08 时区中第二次发生。 postgres的解释是在-08时区看到。如果我们考虑前一分钟,它属于-07 时区。

【讨论】:

    【解决方案2】:

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

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

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

    您大概使用了“时区”列(PM)和 TIMESTAMP 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 in首先,准确地说是您所代表的时间点。

    这是一个保留初始时区的示例,因此您可以看到“-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 更改。

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

    【讨论】:

      【解决方案3】:

      检查这对你是否有意义

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-27
        • 2014-01-24
        • 2017-08-16
        • 1970-01-01
        • 2014-02-12
        • 2017-05-28
        相关资源
        最近更新 更多