当数据类型是带时区和没有时区时,时间戳值在PostgreSQL中存储不同吗?这些差异可以用简单的测试用例来说明吗?


当前回答

我试图比参考的PostgreSQL文档更容易理解地解释它。

TIMESTAMP变量都不存储时区(或偏移量),尽管它们的名称暗示了这一点。区别在于对存储数据的解释(以及预期的应用程序),而不是存储格式本身:

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 knows 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). The input 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 to java.time.OffsetDateTime (or to java.util.Date or java.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 a TIMESTAMP 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.)

其他回答

执行以下命令,查看pgAdmin中的diff:

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环境中有类似的时间戳精度问题,希望我完整的回答和解决方案能帮助你。

这些区别在PostgreSQL官方文档中有显示。请参考文档进行深入挖掘。

简而言之,TIMESTAMP WITHOUT TIME ZONE不会保存任何与时区相关的信息,如果你给date TIME timezone信息,它只接受date & TIME,忽略timezone

例如

当我保存这个12:13,2021年6月11日IST到PostgreSQL的时间戳没有时区将拒绝时区信息,并保存日期时间12:13,2021年6月11日

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

例如

当我保存这个12:13,2021年6月11日IST到PostgreSQL的时间戳带时区类型变量时,它将把这个时间解释为UTC值和 存储如下所示,UTC 2021年6月11日6:43

注意:UTC + 5.30是IST

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

所以PostgreSQL中推荐的时间戳类型是timestamp WITH TIME ZONE或TIMESTAMPZ

时间戳vs时间戳

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

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

与简单的时间戳字段相比,这个字段的优点是你对数据库的输入是独立于时区的,当来自不同时区的应用程序插入时间戳时,或者当你将数据库服务器位置移动到不同的时区时,它不会不准确。

引用文件中的内容:

For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system’s TimeZone parameter, and is converted to UTC using the offset for the timezone zone. To give a simple analogy, a timestamptz value represents an instant in time, the same instant for anyone viewing it. But a timestamp value just represents a particular orientation of a clock, which will represent different instances of time based on your timezone.

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

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

我试图比参考的PostgreSQL文档更容易理解地解释它。

TIMESTAMP变量都不存储时区(或偏移量),尽管它们的名称暗示了这一点。区别在于对存储数据的解释(以及预期的应用程序),而不是存储格式本身:

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 knows 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). The input 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 to java.time.OffsetDateTime (or to java.util.Date or java.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 a TIMESTAMP 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.)

下面是一个应该会有帮助的例子。如果您有一个带有时区的时间戳,您可以将该时间戳转换为任何其他时区。如果你没有一个基本时区,它将不会被正确转换。

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