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

Lar*_*nal 165 postgresql timezone types

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

big*_*ose 141

PostgreSQL文档中介绍了日期/时间类型的差异.是的,一个或多个的处理TIMETIMESTAMP不同.它不会影响值的存储方式; 它会影响它们的解释方式.WITH TIME ZONEWITHOUT TIME ZONE

这些数据类型的时区的影响被明确包括在文档.不同之处在于系统可以合理地了解价值:

  • 使用时区作为值的一部分,可以将值呈现为客户端中的本地时间.

  • 如果没有时区作为值的一部分,则明显的默认时区是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)
Run Code Online (Sandbox Code Playgroud)

  • 仅在引用插入/检索值的过程时才更正.但是读者应该理解Postgres中的数据类型,"带时区的时间戳"和"没有时区的时间戳",并不*实际存储时区信息.您可以通过浏览数据类型文档页面来确认这一点:两种类型都占用相同数量的八位字节并具有值的保存范围,因此无法存储时区信息.该页面的文字证实了这一点.一些用词不当:"没有tz"意味着"在插入数据时忽略偏移量"和"用tz"意味着"使用偏移量来调整为UTC". (80认同)
  • 数据类型在第二种方式是用词不当:他们说"时区"但实际上我们谈论的是与UTC/GMT的偏差.时区实际上是关于夏令时(DST)和其他异常的偏移*加*规则/历史. (35认同)
  • 我宁愿说偏移是时区加上夏令时的规则.您无法发现给定偏移的时区,但您可以根据时区和DST规则发现偏移. (4认同)
  • 引用[官方文档](https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-TIMEZONES):_所有时区感知日期和时间都以UTC格式存储在内部.在显示给客户端之前,它们将在TimeZone配置参数指定的区域中转换为本地时间._ (3认同)
  • @BasilBourque 他们使用时区,而不仅仅是偏移量。正如您所说,它们不存储任何偏移量或区域标识符(因此偏移量与区域并不重要)。但是,当解析或格式化“带时区的时间戳”时,DST 会被计算在内,因此它是时区,而不仅仅是偏移量。 (2认同)
  • @ igorsantos07时区*是关于DST更改和其他更改的规则/历史记录集.你的措辞似乎是多余的.你的声明"偏移是一个时区加上夏令时的规则"是完全错误的:一个偏移只是一个小时,分钟和秒的数字 - 仅此而已. (2认同)

dde*_*any 22

我试着解释它比引用的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存储方式类似于a TIMESTAMP WITH TIME ZONE,使用UTC时区进行渲染时,会产生与本地日期时间相同的年,月,日,小时,分钟,秒和微秒.但这并不意味着代表UTC解释所说的时间线上的点,它只是本地日期时间字段编码的方式.(它是时间线上的一些点簇,因为实时区域不是UTC;我们不知道它是什么.)

  • 我认为这解释得更好、更准确。我发现最受接受的答案令人困惑和误导。谢谢。 (3认同)

小智 12

PostgreSQL 官方文档中显示了差异。请参阅文档进行深入挖掘。

简而言之,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到 PostgreSQLTIMESTAMP 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 ZONE或者 TIMESTAMPZ

  • 那么您的答案中有哪些新内容未被其他/接受的答案涵盖 (4认同)

ser*_*rby 10

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

SELECT now(),
   now()::timestamp,
   now() AT TIME ZONE 'CST',
   now()::timestamp AT TIME ZONE 'CST'
Run Code Online (Sandbox Code Playgroud)

输出:

-[ 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
Run Code Online (Sandbox Code Playgroud)

  • 声明*"将无法正确转换"*根本不是真的.你必须要了解`timestamp`和`timestamptz`是什么意思.`timestamptz`表示绝对时间点(UTC),而`timestamp`表示时钟在某个时区显示的内容.因此,当将'timestamptz`转换为时区时,你会问*在这个绝对时间点,纽约的时钟显示是什么?*而当"转换"一个"时间戳"时,你会问*什么是绝对的纽约时钟显示x?*的时间点 (5认同)
  • 即使您已经了解“WITH”与“WITHOUT TIME ZONE”类型,“AT TIME ZONE”构造本身就是一个脑筋急转弯。所以解释它们是一个奇怪的选择。(:(`AT TIME ZONE` 将 `WITH TIME ZONE` 时间戳转换为 `WITHOUT TIME ZONE` 时间戳,反之亦然......并不完全明显。) (3认同)

Max*_*ges 5

时间戳与时间戳

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

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

与简单的时间戳字段相比,这样做的优势在于您对数据库的输入将与时区无关,并且当来自不同时区的应用程序插入时间戳时,或者当您将数据库服务器位置移动到不同的时区时,不会不准确。

引用文档:

对于带时区的时间戳,内部存储的值始终采用 UTC(世界协调时间,传统上称为格林威治标准时间,GMT)。使用该时区的适当偏移量将具有指定显式时区的输入值转换为 UTC。如果输入字符串中未指定时区,则假定它位于系统的 TimeZone 参数指示的时区中,并使用时区区域的偏移量转换为 UTC。做一个简单的类比,timestamptz 值代表一个时刻,对于任何查看它的人来说都是同一时刻。但是时间戳值仅代表时钟的特定方向,它将代表基于您的时区的不同时间实例。

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

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