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

时间:2011-05-03 21:56:21

标签: postgresql types timezone

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

6 个答案:

答案 0 :(得分:123)

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)

答案 1 :(得分:17)

我试着比引用的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;我们不知道它是什么。)

答案 2 :(得分: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

答案 3 :(得分:1)

时间戳vs时间戳

Postgres中的timestamptz字段基本上只是Postgres实际上只是存储“规范化” UTC时间的timestamp字段,即使输入字符串中给出的时间戳具有时区也是如此。

如果您输入的字符串是:2018-08-28T12:30:00 + 05:30,则当此时间戳存储在数据库中时,它将存储为2018-08-28T07:00:00。

与简单时间戳字段相比,此方法的优势在于您对数据库的输入将不受时区限制,并且当来自不同时区的应用程序插入时间戳或将数据库服务器位置移至其他时区时,输入的内容不会不准确。

要引用文档,请执行以下操作:

对于带时区的时间戳,内部存储的值始终位于 UTC(世界标准时间,通常称为格林威治标准时间) 时间(格林尼治标准时间)。指定了明确时区的输入值是 使用该时区的适当偏移量转换为UTC。如果 在输入字符串中未指定时区,则假定为 在系统的TimeZone参数指示的时区中,并且为 使用时区的偏移量转换为UTC。给一个 简单类推,timestamptz值表示时间的瞬间, 观看任何人的瞬间。但是时间戳值只是 代表时钟的特定方向,它将代表 根据您所在时区的不同时间实例。

在几乎所有用例中,timestamptz几乎总是一个更好的选择。由于timestamptz和timestamp占用相同的8个字节的数据,因此使选择变得更容易。

来源: https://hasura.io/blog/postgres-date-time-data-types-on-graphql-fd926e86ee87/

答案 4 :(得分:0)

运行以下命令以查看 pgAdmin 中的差异:

create table public.testts (tz timestamp with time zone, tnz timestamp without time zone);
insert into public.testts values(now(), now());
select * from public.testts;

如果您在 Angular/Typescript/Node API/PostgreSql 环境中遇到类似的时间戳精度问题,希望我的 complete answer and solution 能帮到您。

答案 5 :(得分:0)

差异显示在 PostgreSQL official docs 中。请参考文档进行深入挖掘。

简而言之,如果您提供带有时区信息的日期时间,TIMESTAMP WITHOUT TIME ZONE 不会保存任何与时区相关的信息,它只需要日期和时间而忽略时区

例如

当我将此 12:13, 11 June 2021 IST 保存到 PostgreSQL 时,TIMESTAMP WITHOUT TIME ZONE 将拒绝时区信息并保存日期时间 12:13,11 June 2021

但在 TIMESTAMP WITH TIME ZONE 的情况下,它以 UTC 格式保存时区信息。

例如

当我将此 12:13, 11 June 2021 IST 保存到 PostgreSQL TIMESTAMP WITH TIME ZONE 类型变量时,它会将这次解释为 UTC 值和 存储如下6:43,11 June 2021 UTC

注意:UTC + 5.30 是 IST

TIMESTAMP WITH TIME ZONE 返回的时间转换时间将以UTC格式存储,我们可以将其转换为所需的时区,如IST或PST等。

所以PostgreSQL中推荐的时间戳类型是TIMESTAMP WITH TIME ZONETIMESTAMPZ