是的,PostgreSQL 在内部将所有时间戳存储为 UTC。对于timestamp with time zone,时区偏移仅用于将时间调整为 UTC,但不会显式存储。
我不会存储 时区字符串,甚至不会存储时区缩写(这些都不精确)。这在以后可能需要昂贵的计算,因为您必须考虑夏令时和国际时间制度的其他奇怪情况。
您可以将时区偏移存储为间隔(需要 12 字节)或数字量的秒数(需要 4 字节 作为整数),就像我在这个related answer.
或者,就像您已经建议的那样:除了 UTC 时间戳(占用 8 个字节)之外,还存储本地时间戳。这将使您的任务变得容易。考虑以下演示::
-- DROP TABLE tbl;
CREATE TEMP TABLE tbl (id int, ts_tz timestamp with time zone, ts timestamp);
INSERT INTO tbl VALUES
(1,'2012-1-1 00:00+01','2012-1-1 00:00+01')
,(2,'2012-1-1 00:00+02','2012-1-1 00:00+02')
,(3,'2012-1-1 00:01+03','2012-1-1 00:01+03')
,(4,'2012-1-1 00:02+04','2012-1-1 00:02+04');
查询问题 1:
SELECT *
FROM tbl
WHERE ts = '2012-1-1 00:00'::timestamp;
id | ts_tz | ts
----+------------------------+---------------------
1 | 2012-01-01 00:00:00+01 | 2012-01-01 00:00:00
2 | 2011-12-31 23:00:00+01 | 2012-01-01 00:00:00
查询问题 2:
SELECT *
FROM tbl
ORDER BY ts_tz;
id | ts_tz | ts
----+------------------------+---------------------
4 | 2011-12-31 21:02:00+01 | 2012-01-01 00:02:00
3 | 2011-12-31 22:01:00+01 | 2012-01-01 00:01:00
2 | 2011-12-31 23:00:00+01 | 2012-01-01 00:00:00
1 | 2012-01-01 00:00:00+01 | 2012-01-01 00:00:00
此解决方案的棘手部分可能是输入本地时间戳。只要所有数据都在本地输入,这很容易。但是,如果您输入数据,例如洛杉矶的纽约,则需要考虑。为此使用AT TIME ZONE construct:
SELECT ('2012-1-1 00:00+00' AT TIME ZONE 'America/New_York')::timestamp
, ('2012-1-1 00:00+00' AT TIME ZONE 'America/Los_Angeles')::timestamp
timezone | timezone
---------------------+---------------------
2011-12-31 19:00:00 | 2011-12-31 16:00:00
注意我如何使用时间戳带时区作为输入。 AT TIME ZONE 为带或不带时区的时间戳提供不同的结果。