Answers:
PostgreSQL文档中涵盖了日期/时间类型的差异。是的,治疗TIME
或TIMESTAMP
一个之间的不同WITH TIME ZONE
或WITHOUT TIME ZONE
。它不会影响值的存储方式。它影响它们的解释方式。
在文档中专门介绍了时区对这些数据类型的影响。不同之处在于系统可以合理地了解该值:
将时区作为值的一部分,可以将值呈现为客户端中的本地时间。
如果没有时区作为值的一部分,则明显的默认时区为UTC,因此将针对该时区进行呈现。
行为取决于至少三个因素而不同:
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)
timestamp with time zone
和timestamp without time zone
都没有实际存储时区信息。您可以在数据类型doc页面上一目了然地确认这一点:两种类型占用相同数量的八位位组,并且具有值的保存范围,因此没有空间存储时区信息。页面文本确认了这一点。误称:“无tz”表示“在插入数据时忽略偏移量”,“有tz”表示“使用偏移量调整到UTC”。
与参考的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'
它们与just相同'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;我们不知道它是什么。)
TIMESTAMP WITH TIME ZONE
将a 检索为a 没有错Instant
。两者都代表UTC时间轴上的一个点。Instant
在我看来,OffsetDateTime
它更可取,因为它更易于自我记录:A TIMESTAMP WITH TIME ZONE
始终以UTC的形式从数据库中检索,而a Instant
始终以UTC形式存在,因此是自然匹配,而an OffsetDateTime
可以携带其他偏移量。
OffsetDateTime
了映射的Java类型。我不确定Instance
某个地方是否仍非官方支持。
'2011-07-01 06:30:30+00'
和)'2011-07-01 06:30:30+05'
都被忽略了,但我能够这样做insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);
,它将正确地将其转换为utc。日期是没有时区的时间戳。我试图了解带有时区的时间戳的主要价值是什么,并且遇到了麻烦。
::timestamptz
。这样,您就可以将字符串转换为TIMESTAMP WITH TIME ZONE
,并在将其进一步转换WITHOUT TIME ZONE
为时,该字符串将存储从会话时区(可能是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
timestamp
和timestamptz
意味着什么。timestamptz
表示绝对时间点(UTC),而timestamp
表示在特定时区显示的时钟。因此,当转换timestamptz
为时区时,您要问的是在这个绝对时间点纽约的时钟显示了什么?而当“转换” a时timestamp
,您要问的是纽约的时钟显示x的绝对时间点
AT TIME ZONE
即使您已经了解了WITH
vs. WITHOUT TIME ZONE
类型,该构造也是它自己的脑筋急转弯。因此,对它们进行解释是一个很好的选择。(:(AT TIME ZONE
将WITH TIME ZONE
时间戳转换为WITHOUT TIME ZONE
时间戳,反之亦然...不太明显。)
now()::timestamp AT TIME ZONE 'CST'
毫无意义,除非您将“ CST”区域的时钟在何时显示本地时钟的当前时间