PostgreSQL 中带/不带时区的时间戳之间的差异
当数据类型为 WITH TIME ZONE
与 WITHOUT TIME ZONE
时,时间戳值在 PostgreSQL 中的存储方式是否不同?可以用简单的测试用例来说明差异吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
当数据类型为 WITH TIME ZONE
与 WITHOUT TIME ZONE
时,时间戳值在 PostgreSQL 中的存储方式是否不同?可以用简单的测试用例来说明差异吗?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(7)
Timestamptz 与 Timestamp
Postgres 中的 timestamptz 字段基本上只是时间戳字段,Postgres 实际上只是存储“标准化”UTC 时间,即使输入字符串中给出的时间戳具有时区。
如果您的输入字符串是: 2018-08-28T12:30:00+05:30 ,当这个时间戳存储在数据库中时,它将存储为 2018-08-28T07:00:00 。
与简单时间戳字段相比,它的优点是您对数据库的输入将与时区无关,并且当来自不同时区的应用程序插入时间戳时,或者当您将数据库服务器位置移动到不同时区时,不会不准确。
引用文档:
对于几乎任何用例,timestamptz 几乎总是更好的选择。由于 timestamptz 和 timestamp 都占用相同的 8 字节数据,这一选择变得更加容易。
来源:
https://hasura.io/blog/postgres-日期时间数据类型-graphql-fd926e86ee87/
Timestamptz vs Timestamp
The timestamptz field in Postgres is basically just the timestamp field where Postgres actually just stores the “normalised” UTC time, even if the timestamp given in the input string has a timezone.
If your input string is: 2018-08-28T12:30:00+05:30 , when this timestamp is stored in the database, it will be stored as 2018-08-28T07:00:00.
The advantage of this over the simple timestamp field is that your input to the database will be timezone independent, and will not be inaccurate when apps from different timezones insert timestamps, or when you move your database server location to a different timezone.
To quote from the docs:
For pretty much any use case, timestamptz is almost always a better choice. This choice is made easier with the fact that both timestamptz and timestamp take up the same 8 bytes of data.
source:
https://hasura.io/blog/postgres-date-time-data-types-on-graphql-fd926e86ee87/
差异显示在 PostgreSQL 官方文档中。请参阅文档进行深入挖掘。
简而言之,如果您提供带有时区信息的日期时间,则
TIMESTAMP WITHOUT TIME ZONE
不会保存任何时区相关信息,它需要日期和时间。仅时间并忽略时区例如
当我将此
12:13, 11 June 2021 IST
保存到PostgreSQL时TIMESTAMP NOT 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
NB : UTC + 5.30 is IST
在 < 返回的时间转换时间期间code>TIMESTAMP WITH TIME ZONE 将以 UTC 格式存储,我们可以将其转换为所需的时区,如 IST 或 PST 等。
因此 PostgreSQL 中推荐的时间戳类型是 TIMESTAMP WITH时区或
TIMESTAMPZ
The diffrences are shown in PostgreSQL official docs. Please refer the docs for deep digging.
In a nutshell
TIMESTAMP WITHOUT TIME ZONE
doesn't save any timezone related informations if you give date time with timezone info,it takes date & time only and ignores timezoneFor example
When I save this
12:13, 11 June 2021 IST
to PostgreSQLTIMESTAMP WITHOUT TIME ZONE
will reject the timezone information and saves the date time12:13,11 June 2021
But the the case of
TIMESTAMP WITH TIME ZONE
it saves the timezone info inUTC
format.For example
When I save this
12:13, 11 June 2021 IST
to PostgreSQLTIMESTAMP WITH TIME ZONE
type variable it will interpret this time toUTC
value andstored as shown in below
6:43,11 June 2021 UTC
NB : UTC + 5.30 is IST
During the time conversion time returned by
TIMESTAMP WITH TIME ZONE
will be stored in UTC format and we can convert it to the required timezone like IST or PST etc.So the recommented timestamp type in PostgreSQL is
TIMESTAMP WITH TIME ZONE
orTIMESTAMPZ
运行以下命令来查看 pgAdmin 中的差异:
如果您在 Angular / Typescript / Node API / PostgreSql 环境中遇到与我类似的时间戳精度问题,希望我的 完整的答案和解决方案将为您提供帮助。
Run the following to see diff in pgAdmin:
If you have similar issues I had of timestamp precision in Angular / Typescript / Node API / PostgreSql environment, hope my complete answer and solution will help you out.
我喜欢将其视为没有时区的时间戳就像存储特定的时钟指针设置并在检索时将它们复制粘贴到其他时钟上。
假设没有时区的时间戳,您拍摄某个时钟的快照并存储它。此设置完全不知道它所在的时区,它只是一个没有时区上下文的值。然而,如果你得到它的纪元值,它将被解释为这个精确值在你的本地时区,这真的很令人困惑。例如,如果 3 个单独的数据库客户端将请求来自三个不同时区位置的时间戳 01-01-2025 00:00:UTC-8、UTC 和 UTC+8,则它们都将获得相同的日期他们的当地时间:
但它代表 UTC 中的三个不同时间戳:
时间戳与时区的工作方式完全不同:它将在存储时间时对齐其偏移量以匹配 UTC。之后,获取此时间戳的客户端将根据其时区调整日期。当存储 01-01-2025 00:00 +3 时,它将存储为 31-12-2024 21:00 UTC,现在,请求此时间戳的相同客户端能够以 UTC 模式检索此时间戳,然后所有三个都将获得相同的标准化日期:
或者,当尊重客户端会话时区时:
这是没有时区的时间戳的图形解释
I like to think about it as the timestamp without time zone is like storing a particular clock hand setup and copy-pasting them on other clocks upon retrieval.
timestamp WITHOUT time zone assumes, you take a snapshot of some clock and store it. This setup has absolutely no idea, what time zone it resides, it's just a value with no timezone context. HOWEVER if you will get it's epoch value, it will be interpreted as this exact value IN YOUR LOCAL TIMEZONE, what's really confusing. For example, if 3 separate DB clients will request timestamp 01-01-2025 00:00 from three distinct time zone locations: UTC-8, UTC and UTC+8, they will all get same date for their local time:
But it represents three different timestamps in UTC:
timestamp WITH time zone works entirely different: It will at store time align its offset to match UTC. Afterward, clients obtaining this timestamp will align date according to their time zones. When storing 01-01-2025 00:00 +3, it will be stored as 31-12-2024 21:00 UTC and now, same clients asking for this timestamp are able to retrieve this timestamp in UTC mode, all three then will get same, normalized date:
Or, when respecting client session time zone:
Here is the graphical interpretation of timestamp without time zone
日期/时间类型的 PostgreSQL 文档中介绍了这些差异。是的,
WITH TIME ZONE
和WITHOUT TIME ZONE
之间对TIME
或TIMESTAMP
的处理有所不同。它不会影响值的存储方式;它会影响它们的解释方式。时区对这些数据类型的影响专门介绍了< /a> 在文档中。差异在于系统可以合理地了解该值的内容:
将时区作为该值的一部分,该值可以在客户端中呈现为本地时间。
如果没有时区作为值的一部分,明显的默认时区是 UTC,因此它是针对该时区呈现的。
该行为的不同取决于至少三个因素:
WITH TIME ZONE
或WITHOUT TIME ZONE
)。以下是涵盖这些因素组合的示例:
The differences are covered at the PostgreSQL documentation for date/time types. Yes, the treatment of
TIME
orTIMESTAMP
differs between oneWITH TIME ZONE
orWITHOUT TIME ZONE
. It doesn't affect how the values are stored; it affects how they are interpreted.The effects of time zones on these data types is covered specifically in the docs. The difference arises from what the system can reasonably know about the value:
With a time zone as part of the value, the value can be rendered as a local time in the client.
Without a time zone as part of the value, the obvious default time zone is UTC, so it is rendered for that time zone.
The behaviour differs depending on at least three factors:
WITH TIME ZONE
orWITHOUT TIME ZONE
) of the value.Here are examples covering the combinations of those factors:
我尝试比 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;我们不知道它是什么。)I try to explain it more understandably than the PostgreSQL documentation.
Neither
TIMESTAMP
variants store a time zone (or an offset), despite what the names suggest. The difference is in the interpretation of the stored data (and in the intended application), not in the storage format itself:TIMESTAMP WITHOUT TIME ZONE
stores local date-time (aka. wall calendar date and wall clock time). Its time zone is unspecified as far as PostgreSQL can tell (though your application may know what it is). Hence, PostgreSQL does no time zone related conversion on input or output. If the value was entered into the database as'2011-07-01 06:30:30'
, then no mater in what time zone you display it later, it will still say year 2011, month 07, day 01, 06 hours, 30 minutes, and 30 seconds (in some format). Also, any offset or time zone you specify in the input is ignored by PostgreSQL, so'2011-07-01 06:30:30+00'
and'2011-07-01 06:30:30+05'
are the same as just'2011-07-01 06:30:30'
.For Java developers: it's analogous to
java.time.LocalDateTime
.TIMESTAMP WITH TIME ZONE
stores a point on the UTC time line. How it looks (how many hours, minutes, etc.) depends on your time zone, but it always refers to the same "physical" instant (like the moment of an actual physical event). Theinput is internally converted to UTC, and that's how it's stored. For that, the offset of the input must be known, so when the input contains no explicit offset or time zone (like
'2011-07-01 06:30:30'
) it's assumed to be in the current time zone of the PostgreSQL session, otherwise the explicitly specified offset or time zone is used (as in'2011-07-01 06:30:30+05'
). The output is displayed converted to the current time zone of the PostgreSQL session.For Java developers: It's analogous to
java.time.Instant
(with lower resolution though), but with JDBC and JPA 2.2 you are supposed to map it tojava.time.OffsetDateTime
(or tojava.util.Date
orjava.sql.Timestamp
of course).Some say that both
TIMESTAMP
variations store UTC date-time. Kind of, but it's confusing to put it that way in my opinion.TIMESTAMP WITHOUT TIME ZONE
is stored like aTIMESTAMP WITH TIME ZONE
, which rendered with UTC time zone happens to give the same year, month, day, hours, minutes, seconds, and microseconds as they are in the local date-time. But it's not meant to represent the point on the time line that the UTC interpretation says, it's just the way the local date-time fields are encoded. (It's some cluster of dots on the time line, as the real time zone is not UTC; we don't know what it is.)这是一个应该有所帮助的示例。如果您有带有时区的时间戳,则可以将该时间戳转换为任何其他时区。如果您没有基准时区,则无法正确转换。
输出:
Here is an example that should help. If you have a timestamp with a timezone, you can convert that timestamp into any other timezone. If you haven't got a base timezone it won't be converted correctly.
Output: