如何使用时间戳正确处理夏令时

时间:2014-03-16 15:32:22

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

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

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

作为此列的数据类型,我选择带时区的时间戳,并希望以这种方式自动考虑夏令时。 (顺便说一句,我的默认时区是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'或相应值的东西)并且在10月的特定日期(但不是3月份),有两个3' oclock的条目,因此我将获得以下形式的列(其中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
  • ...

有人知道如何去做吗?我做的事基本上是错的吗?这只是格式问题吗?我必须写一个函数吗?任何帮助,将不胜感激。谢谢。

2 个答案:

答案 0 :(得分:0)

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;

答案 1 :(得分:0)

无论时区设置如何,时间戳始终存储为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