PostgreSQL中有/无时区的时间戳之间的差异

问题描述 投票:146回答:3

当数据类型为WITH TIME ZONE而不是WITHOUT TIME ZONE时,PostgreSQL中的时间戳值是否存储不同?可以用简单的测试用例说明差异吗?

postgresql types timezone
3个回答
131
投票

差异在the PostgreSQL documentation for date/time types涵盖。是的,TIMETIMESTAMP的治疗方法在WITH TIME ZONEWITHOUT TIME ZONE之间有所不同。它不会影响值的存储方式;它会影响它们的解释方式。

时区对这些数据类型的影响是文档中的covered specifically。不同之处在于系统可以合理地了解价值:

  • 使用时区作为值的一部分,可以将值呈现为客户端中的本地时间。
  • 如果没有时区作为值的一部分,则明显的默认时区是UTC,因此将为该时区进行渲染。

行为的不同取决于至少三个因素:

  • 客户端中的时区设置。
  • 值的数据类型(即WITH TIME ZONEWITHOUT 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)

18
投票

我试着解释它比引用的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.Datejava.sql.Timestamp)。

有人说,两个TIMESTAMP变体存储UTC日期时间。有点,但在我看来这样说是令人困惑的。 TIMESTAMP WITHOUT TIME ZONE存储为TIMESTAMP WITH TIME ZONE,以UTC时区呈现,恰好在本地日期时间内提供相同的年,月,日,小时,分钟,秒和微秒。但这并不意味着代表UTC解释所说的时间线上的点,它只是本地日期时间字段编码的方式。 (它是时间线上的一些点簇,因为实时区域不是UTC;我们不知道它是什么。)


9
投票

这是一个应该有用的例子。如果您有带时区的时间戳,则可以将该时间戳转换为任何其他时区。如果您没有基准时区,则无法正确转换。

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
© www.soinside.com 2019 - 2024. All rights reserved.