【问题标题】:How to correctly handle daylight saving times with timestamps如何正确处理带有时间戳的夏令时
【发布时间】:2014-04-21 17:50:04
【问题描述】:

我正在尝试在 Postgres 中创建一个表,用于存储在接下来的几年中每天每整小时发生一次的事件。所以我使用以下表达式填充了一个列:

INSERT INTO tablename(time) 
SELECT CAST('2013-01-01' AS DATE) + (n || ' hour')::INTERVAL 
 FROM generate_series(0, 100000) n;

作为本专栏的数据类型,我选择了 timestamp with time zone,并希望通过这种方式自动考虑夏令时。 (顺便说一句,我的默认时区是 CET,所以适用 DST 时是 UTC+1 或 UTC+2)。作为上述查询的结果,我得到了这个:

  • 2013-03-31 00:00:00 +01
  • 2013-03-31 01:00:00 +01
  • 2013-03-31 03:00:00 +02
  • 2013-03-31 03:00:00 +02
  • 2013-03-31 04:00:00 +02
  • ...
  • 2013-10-27 00:00:00 +02
  • 2013-10-27 01:00:00 +02
  • 2013-10-27 02:00:00 +01
  • 2013-10-27 03:00:00 +01
  • 2013-10-27 04:00:00 +01
  • ...

与 UTC 的偏移量发生了变化,我预计 3 月 31 日的 02:00 会被忽略,因为这一天只有 23 小时,但我不知道为什么 03:00 有两次,而 10 月 27 日是 02:00因为这一天有 25 小时,所以只有一次而不是两次。我想要实现的是,在 3 月 2 点的特定日期的所有年份都不会被跳过(我宁愿输入 'na' 或相应的值),并且 3'oclock 有两个条目在 10 月的特定日期(但不是 3 月),这样我将获得以下形式的列(其中 1 代表 00:00-1:00 的小时,2 代表 1:00-2:00等):

  • 2013-03-31 1 +01
  • 2013-03-31 2 +01
  • 2013-03-31 3 +02
  • 2013-03-31 4 +02
  • 2013-03-31 5 +02
  • ...
  • 2013-10-27 1 +02
  • 2013-10-27 2 +02
  • 2013-10-27 3A +02
  • 2013-10-27 3B +01
  • 2013-10-27 4 +01
  • 2013-10-27 5 +01
  • ...

有人知道怎么做吗?我做错了什么吗?只是格式的问题吗?我必须写一个函数吗?任何帮助,将不胜感激。谢谢。

【问题讨论】:

    标签: sql postgresql timezone dst timestamp-with-timezone


    【解决方案1】:

    Postgres 中的日期和时间以 UTC 存储,并根据时区配置指定的时区转换为本地时间。

    这意味着您只需要解决表示问题。尝试使用 AT TIME ZONE 'UTC+2' 将 UTC 时间转换为您的时区并查看结果。这是查询:

    SELECT (CAST('2013-03-30' AS DATE) + (n || ' hour')::INTERVAL) AT TIME ZONE 'UTC+2' 
        FROM generate_series(0, 1000) n;
    

    【讨论】:

      【解决方案2】:

      无论时区设置如何,时间戳始终存储为 UTC。 From the manual

      对于带时区的时间戳,内部存储的值始终采用 UTC(通用协调时间,传统上称为格林威治标准时间,GMT)。具有指定明确时区的输入值将使用该时区的适当偏移量转换为 UTC。如果输入字符串中没有说明时区,则假定它在系统的 TimeZone 参数指示的时区中,并使用时区的偏移量转换为 UTC。

      set time zone 'CET';
      
      drop table if exists events;
      create table events (
          tstz timestamp with time zone
      );
      insert into events (tstz)
      select generate_series('2013-01-01', '2013-10-28', interval '1 hour') s(tstz)
      ;
      

      注意generate_series 函数的使用。

      select
          tstz at time zone 'UTC' as "UTC",
          tstz at time zone 'CET' as "CET",
          tstz at time zone 'CEST' as "CEST",
          tstz as "LOCAL"
      from events
      where date_trunc('day', tstz) in ('2013-03-31', '2013-10-27')
      order by tstz
      ;
               UTC         |         CET         |        CEST         |         LOCAL          
      ---------------------+---------------------+---------------------+------------------------
       2013-03-30 23:00:00 | 2013-03-31 00:00:00 | 2013-03-31 01:00:00 | 2013-03-31 00:00:00+01
       2013-03-31 00:00:00 | 2013-03-31 01:00:00 | 2013-03-31 02:00:00 | 2013-03-31 01:00:00+01
       2013-03-31 01:00:00 | 2013-03-31 02:00:00 | 2013-03-31 03:00:00 | 2013-03-31 03:00:00+02
       2013-03-31 02:00:00 | 2013-03-31 03:00:00 | 2013-03-31 04:00:00 | 2013-03-31 04:00:00+02
       2013-03-31 03:00:00 | 2013-03-31 04:00:00 | 2013-03-31 05:00:00 | 2013-03-31 05:00:00+02
      ...
       2013-10-26 22:00:00 | 2013-10-26 23:00:00 | 2013-10-27 00:00:00 | 2013-10-27 00:00:00+02
       2013-10-26 23:00:00 | 2013-10-27 00:00:00 | 2013-10-27 01:00:00 | 2013-10-27 01:00:00+02
       2013-10-27 00:00:00 | 2013-10-27 01:00:00 | 2013-10-27 02:00:00 | 2013-10-27 02:00:00+02
       2013-10-27 01:00:00 | 2013-10-27 02:00:00 | 2013-10-27 03:00:00 | 2013-10-27 02:00:00+01
       2013-10-27 02:00:00 | 2013-10-27 03:00:00 | 2013-10-27 04:00:00 | 2013-10-27 03:00:00+01
       2013-10-27 03:00:00 | 2013-10-27 04:00:00 | 2013-10-27 05:00:00 | 2013-10-27 04:00:00+01
       2013-10-27 04:00:00 | 2013-10-27 05:00:00 | 2013-10-27 06:00:00 | 2013-10-27 05:00:00+01
      

      如果 timestamp with timestamp 列是 selectec 而不像上面的 LOCAL 列中那样使用 at time zone,它将在服务器时区在那个时间戳输出。这就是为什么会出现缺失和重复的小时数。

      我认为你想要的输出是错误的。但是通过一些 query fu

      是可以实现的

      我无法重现您的实际输出。服务器时区是什么?

      show time zone;
       TimeZone 
      ----------
       CET
      

      【讨论】:

        猜你喜欢
        • 2017-09-25
        • 2020-07-14
        • 2011-10-22
        • 1970-01-01
        • 2013-01-31
        • 1970-01-01
        • 1970-01-01
        • 2021-01-20
        • 1970-01-01
        相关资源
        最近更新 更多