当数据类型为WITH TIME ZONE
而不是WITHOUT TIME ZONE
时,PostgreSQL中的时间戳值是否存储不同?可以用简单的测试用例说明差异吗?
差异在the PostgreSQL documentation for date/time types涵盖。是的,TIME
或TIMESTAMP
的治疗方法在WITH TIME ZONE
或WITHOUT TIME ZONE
之间有所不同。它不会影响值的存储方式;它会影响它们的解释方式。
时区对这些数据类型的影响是文档中的covered specifically。不同之处在于系统可以合理地了解价值:
行为的不同取决于至少三个因素:
WITH TIME ZONE
或WITHOUT TIME ZONE
)。以下是涵盖这些因素组合的示例:
foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+09
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 06:00:00+09
(1 row)
foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+11
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 08:00:00+11
(1 row)
我试着解释它比引用的PostgreSQL文档更容易理解。
尽管名称暗示,TIMESTAMP
变体都没有存储时区(或偏移量)。不同之处在于对存储数据(以及预期应用程序)的解释,而不是存储格式本身:
TIMESTAMP WITHOUT TIME ZONE
存储当地日期时间(又名。挂历日期和挂钟时间)。就PostgreSQL而言,它的时区是未指定的(尽管你的应用程序可能知道它是什么)。因此,PostgreSQL在输入或输出上没有时区相关的转换。如果该值以'2011-07-01 06:30:30'
的形式输入到数据库中,那么在以后显示的时区中没有任何内容,它仍然会显示2011年,07月,第01天,06小时,30分钟和30秒(以某种格式) 。此外,PostgreSQL会忽略您在输入中指定的任何偏移或时区,因此'2011-07-01 06:30:30+00'
和'2011-07-01 06:30:30+05'
与'2011-07-01 06:30:30'
相同。对于Java开发人员:它类似于java.time.LocalDateTime
。TIMESTAMP WITH TIME ZONE
在UTC时间线上存储一个点。它的外观(多少小时,分钟等)取决于你的时区,但它总是指同一个“物理”瞬间(就像实际物理事件的那一刻)。输入内部转换为UTC,这就是它的存储方式。为此,必须知道输入的偏移量,因此当输入不包含显式偏移或时区(如'2011-07-01 06:30:30'
)时,假定它位于PostgreSQL会话的当前时区,否则显式指定的偏移或时区是使用(如在'2011-07-01 06:30:30+05'
)。输出显示转换为PostgreSQL会话的当前时区。对于Java开发人员:它类似于java.time.Instant
(尽管分辨率较低),但是使用JDBC和JPA 2.2,你应该将它映射到java.time.OffsetDateTime
(或者当然是java.util.Date
或java.sql.Timestamp
)。有人说,两个TIMESTAMP
变体存储UTC日期时间。有点,但在我看来这样说是令人困惑的。 TIMESTAMP WITHOUT TIME ZONE
存储为TIMESTAMP WITH TIME ZONE
,以UTC时区呈现,恰好在本地日期时间内提供相同的年,月,日,小时,分钟,秒和微秒。但这并不意味着代表UTC解释所说的时间线上的点,它只是本地日期时间字段编码的方式。 (它是时间线上的一些点簇,因为实时区域不是UTC;我们不知道它是什么。)
这是一个应该有用的例子。如果您有带时区的时间戳,则可以将该时间戳转换为任何其他时区。如果您没有基准时区,则无法正确转换。
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
输出:
-[ RECORD 1 ]---------------------------
now | 2018-09-15 17:01:36.399357+03
now | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03