关于sql:如何使用AT TIME ZONE选择带偏移量的timestamptz

How to select timestamptz with offset with AT TIME ZONE

我已经在PostgreSQL中添加时间戳,阅读有关此行为的有趣博客,但无法解决以下问题:

如何从另一列中选择带有AT TIME ZONE的"带时区的时间戳"列,包括一步到达UTC的偏移量?

我在PostgreSQL 10中有一个示例表:

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE public.test_tz
(
    id serial,
    in_zone_timestamp TIMESTAMP WITHOUT TIME zone,
    in_zone CHARACTER VARYING COLLATE pg_catalog."default",
    CONSTRAINT test_tz_pkey PRIMARY KEY (id)
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

PostgreSQL的设置是

timezone=localtime

服务器运行在'Europe / Berlin'

$ date --> Fri Mar 9 12:15:23 CET 2018

timestamp列应该存储这种凌乱的语法:

1
2
INSERT INTO public.test_tz ( in_zone_timestamp, in_zone) VALUES
('2017-02-01 00:00:00' AT TIME ZONE current_setting('TIMEZONE') AT TIME ZONE 'Asia/Singapore', 'Asia/Singapore' );

如果有人知道更好的方法,请不要退缩!另一种解决方案是使用'2017-02-01 00:00:00-08'作为值,但我不知道偏移值。

我想存储本地时间戳,例如:'用户在新加坡的2017-02-01 00:00:00点击一个键'。

如果我问数据库:在欧洲的哪个时间,当新加坡的用户正在按键时,我得到了:

1
2
SELECT in_zone_timestamp FROM public.test_tz;
--> '2017-01-31 17:00:00+01'

这似乎没问题,因为新加坡的偏差为+8小时。

如果我想知道,在新加坡哪个时候,我使用:

1
2
SELECT in_zone_timestamp AT TIME ZONE in_zone FROM public.test_tz;
--> '2017-02-01 00:00:00'

这也没关系,但是,它不会将偏移返回到'UTC',所以我看不到这个时间戳不在我当地的时间!

我尝试AT TIME ZONE的某些组合或转换为timestamptz,但结果不是我想要的。我期待一个结果,如:

1
--> '2017-02-01 00:00:00+08'

在这里,我只看到一个解决方案,手动连接/转换/操作结果并手动添加偏移量,但这是唯一的方法吗?

对不起,如果我解释这个问题有点过于复杂,希望有人能听从我的想法。

提前致谢


感谢澄清,但在我看来,PostgreSQL在这个主题上有一些缺点。

对于这个问题,"不太完美"的解决方案实际上是手动连接所需的部件。

1
2
SELECT (test_tz.in_zone_timestamp AT TIME ZONE in_zone) || (SELECT abbrev FROM pg_timezone_names WHERE name = in_zone)  FROM public.test_tz;
--> 2017-02-01 00:00:00+08

这提供了可接受的解决方案,具有小的缺陷。

在某些情况下,缩写列中有时区短文本,如"CET"或"PDT",而不是+/-数值。这会产生结果(例如'欧洲/柏林'),如:

1
--> 2017-02-01 02:00:00CET

更好的值给出了pg-timezone-names的列'utc_offset',但这需要更复杂的文本操作,我现在不想要。

第二种解决方案可以是通过应用程序操作输出并将文本格式化为您需要的任何内容。

希望帮助其他人解决此类问题,而无需在互联网上搜索数据库中没有的可用支持。

再见


使PostgreSQL自动将时间戳转换为2017-02-01 00:00:00+08之类的字符串的唯一方法是更改??会话时区:

1
SET timezone = 'Asia/Singapore';

您可以使用SET LOCAL仅在事务持续时间内更改设置。

如果您不想这样,您可以使用如下查询获取偏移:

1
2
3
4
5
6
7
SELECT TIMESTAMP '2000-01-01 00:00:00' AT TIME ZONE 'UTC'
     - TIMESTAMP '2000-01-01 00:00:00' AT TIME ZONE 'Asia/Singapore';

 ?COLUMN?
----------
 08:00:00
(1 ROW)