CREATE TABLE my_tbl (
my_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
CHECK(EXTRACT(TIMEZONE FROM my_timestamp) = '0')
);
test=> SET timezone = 'America/Los_Angeles';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
ERROR: new row for relation "my_tbl" violates check constraint "my_tbl_my_timestamp_check"
test=> SET timezone = 'UTC';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
INSERT 0 1
-- Make sure we're all working off of the same local time zone
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT NOW();
now
-------------------------------
2011-05-27 15:47:58.138995-07
(1 row)
test=> SELECT NOW() AT TIME ZONE 'UTC';
timezone
----------------------------
2011-05-27 22:48:02.235541
(1 row)
请注意,AT TIME ZONE 'UTC'剥离时区信息,并创建一个相对的 TIMESTAMP WITHOUT TIME ZONE使用您的目标的参考框架(UTC)。
当从不完整的 TIMESTAMP WITHOUT TIME ZONE转换为 TIMESTAMP WITH TIME ZONE时,缺少的时区是从您的连接继承的:
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
date_part
-----------
-7
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
date_part
-----------
-7
(1 row)
-- Now change to UTC
test=> SET timezone = 'UTC';
SET
-- Create an absolute time with timezone offset:
test=> SELECT NOW();
now
-------------------------------
2011-05-27 22:48:40.540119+00
(1 row)
-- Creates a relative time in a given frame of reference (i.e. no offset)
test=> SELECT NOW() AT TIME ZONE 'UTC';
timezone
----------------------------
2011-05-27 22:48:49.444446
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
date_part
-----------
0
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
date_part
-----------
0
(1 row)
底线是:
将用户的时区存储为命名标签(例如 America/Los_Angeles) ,而不是从 UTC (例如 -0700)的偏移量
绝对时间用于记录事件。例如: “用户123登录”或“毕业典礼开始于2011-05-28下午2时太平洋标准时间。”不管你的本地时区,如果你可以传送到事件发生的地方,你可以目睹事件的发生。数据库中的大多数时间数据都是绝对的(因此应该是 TIMESTAMP WITH TIME ZONE,最理想的是带有 + 0偏移量和表示特定时区规则的文本标签,而不是偏移量)。
假设现在是2004年,您需要安排在2008年10月31日下午1点在美国西海岸交货(即 America/Los_Angeles/PST8PDT)。如果您使用 ’2008-10-31 21:00:00.000000+00’::TIMESTAMP WITH TIME ZONE存储使用绝对时间,交付将显示在下午2时,因为美国政府通过了 2005年能源政策法,改变了规则的夏时制时间。在2004年交付时,10-31-2008将是太平洋标准时间(+8000) ,但从2005年开始时区数据库认识到,10-31-2008将是太平洋夏令时(+0700)。存储一个带有时区的相对时间戳可以产生一个正确的交付时间表,因为相对时间戳不会受到国会不知情的篡改。在使用相对时间和绝对时间来安排事情之间的分界线是一条模糊的线,但是我的经验法则是,未来任何超过3-6mo 的安排都应该使用相对时间戳(计划 = 绝对 vs 计划 = 相对? ? ?).
另一种/最后一种相对时间类型是 INTERVAL。示例: “会话将在用户登录后超时20分钟”。INTERVAL可以正确使用绝对时间戳(TIMESTAMP WITH TIME ZONE)或相对时间戳(TIMESTAMP WITHOUT TIME ZONE)。同样正确的说法是,“一个用户会话在成功登录后20分钟到期(login _ utc + session _ )”或者“我们的早餐会议只能持续60分钟(循环 _ start _ time + meet _ length)”。
最后一点混淆: DATE、 TIME、 TIME WITHOUT TIME ZONE和 TIME WITH TIME ZONE都是相对的数据类型。例如: '2011-05-28'::DATE表示一个相对日期,因为你没有时区信息可以用来识别午夜。类似地,'23:23:59'::TIME是相对的,因为您既不知道时区,也不知道由时间表示的 DATE。即使使用 '23:59:59-07'::TIME WITH TIME ZONE,你也不知道 DATE会是什么。最后,带时区的 DATE实际上不是 DATE,而是 TIME1:
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
timezone
---------------------
2011-05-11 07:00:00
(1 row)
test=> SET timezone = 'UTC';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
timezone
---------------------
2011-05-11 00:00:00
(1 row)
事实上,“ WITHTIME ZONE”和“ WITHOUTTIME ZONE”都将值存储为类 Unix 的绝对 UTC 时间戳。区别在于时间戳的显示方式。当“ WITHtime zone”时,显示的值是转换为用户区域的 UTC 存储值。当“没有时区”的 UTC 存储值扭曲,以显示相同的时钟面,无论用户设置的区域。
“ WITHOUT 时区”唯一可用的情况是,无论实际时区如何,时钟面值都适用。例如,当时间戳显示投票站何时可能关闭(即。他们在20:00关门,不管一个人的时区)。