时区的PostgreSQL错误行为?

时间:2019-06-06 12:22:54

标签: postgresql timezone

我有一个简单的postgresql表,其中包含以下数据:

create table ti (
  l text,
  t timestamp without time zone
  );

insert into ti(l, t) values ('now', now());

我使用at time zone进行选择,我希望UTC + 5.30 hours

select now(), t, t at time zone 'Asia/Kolkata' from ti;

但这就是我得到的:

now|    t|  timezone
2019-06-06T12:11:42.576388Z|    2019-06-06T11:50:48.178689Z|    2019-06-06T06:20:48.178689Z

它减去了5.30小时而不是添加它们。

Sqlfiddle here

1 个答案:

答案 0 :(得分:2)

now()返回带有时区的时间戳。当将时区信息保存到表中时,该时区信息将被转换为没有时区的时间戳,但是其中保存的实际值将取决于会话时区。

您可以很容易地看到这种行为:

postgres=# begin;
BEGIN
postgres=# set time zone utc;
SET
postgres=# insert into test select now();
INSERT 0 1
postgres=# set time zone 'US/Eastern';
SET
postgres=# insert into test select now();
INSERT 0 1
postgres=# select * from test;
             a
----------------------------
 2019-06-06 12:46:10.475424
 2019-06-06 08:46:10.475424
(2 rows)

发表评论后再进行一些解释。我认为您遇到的问题是在时间戳和时间戳之间进行转换。如果仅坚持使用时间戳,那么时间戳就不会那么混乱了。让我们从讨论中删除now(),因为这会增加一层额外的复杂性,因为从now()的结果转换为无时区的时间戳取决于会话时区。

select '2019-06-06 12:00:00UTC'::timestamp with time zone, 
       ('2019-06-06 12:00:00UTC'::timestamp with time zone) at time zone 'Asia/Kolkata';
      timestamptz       |      timezone
------------------------+---------------------
 2019-06-06 12:00:00+00 | 2019-06-06 17:30:00

我相信这是您的期望?我们从带有时区的时间戳转换为在特定时区的没有时区的时间戳。

尽管如此,您正在做什么:

select '2019-06-06 12:00:00UTC'::timestamp with time zone, 
  (('2019-06-06 12:00:00UTC'::timestamp with time zone)::timestamp without time zone) at time zone 'Asia/Kolkata';
      timestamptz       |        timezone
------------------------+------------------------
 2019-06-06 12:00:00+00 | 2019-06-06 06:30:00+00
(1 row)

我想,如果您可以存储带时区的时间戳而不是无时区的时间戳,那么您会发现混乱程度降低了。