PostgreSQL 11 - 默认时间戳列

pet*_*rov 1 sql postgresql

我有一张这样的桌子。

create table public.test123
(id int not null primary key, dt timestamp null default clock_timestamp());
Run Code Online (Sandbox Code Playgroud)

然后,我从 2 个不同的客户端会话插入其中,第一个会话来自 PG Admin,第二个会话来自 DBeaver。

  1. 在我运行的第一个会话中,show timezone;它返回UTC.
    所以本次会话的时区是UTC。因此,从本次会议开始,我做了如下插入。

     insert into public.test123(id) values (1);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在第二次会议中,我有时区America/New_York,我就这样做了。

     insert into public.test123(id) values (2);
    
    Run Code Online (Sandbox Code Playgroud)

我运行这两个插入的时间间隔只有几秒钟。

但我在数据库中得到两个非常不同的值,例如

    id|dt                 |
    --|-------------------|
     1|2020-06-05 14:38:18|
     2|2020-06-05 10:38:26|
Run Code Online (Sandbox Code Playgroud)

我一直认为在这种情况下clock_timestamp()调用是在服务器上执行的,客户端会话的时区应该不重要。我期望得到两个相隔几秒的值,而不是相隔 4 小时的值。

我缺少什么?

另外,有没有办法获得独立于客户端会话时区的时间戳?

如何创建一个具有默认时间戳值的列,该值真正独立于客户端会话的时区?

Sch*_*ern 5

tl;dr:切换到时间戳。


timestamp我将通过它们的简称来引用这些类型: timestamp without time zoneistimestamptimestamp with time zoneis timestamptz

两者timestamptimestamptz存储绝对时间点。timestamp with time zone还记录输入的时区。

例如,2020-06-05 20:22:48Z2020-06-05 13:22:48-0700都代表同一时刻,但第一个采用通用协调时间,另一个采用 7 小时偏移(太平洋夏令时)。

timestamptz将存储2020-06-05 13:22:48-07002020-06-05 20:22:48,绝对时间点,但还要记住它是以 7 小时偏移量输入的。当您检索它时,您将得到2020-06-05 13:22:48-0700与 相同的时间2020-06-05 20:22:48Z

timestamp将完全忽略时区。它存储2020-06-05 13:22:48-07002020-06-05 13:22:48,没有时区,没有转换,仅此而已。


针对具体问题:dt timestamp null default clock_timestamp()

clock_timestamp()返回timestamptz,但您将其存储在timestamp. Postgres 只需删除时区即可将其中一个转换为另一个。它不会转换它。如果时区是 UTC 并且它2020-06-05 20:22:48Z会存储2020-06-05 20:22:48. 如果时区位于美国/纽约并且它将2020-06-05 16:22:48-0400存储2020-06-05 16:22:48.

还有...有没有办法获得独立于客户端会话时区的时间戳?

它们都独立于客户端的时区,这是类型转换的问题。切换到timestamptz


时区很复杂,但在将时间戳移入和移出数据库时,有两种处理时区的策略。

  1. 使用timestamp始终以 UTC 时间工作。
  2. 使用timestamptz始终包含时区。

前者比较简单,很多框架都采用这种方式。但我更喜欢后者,因为尽管它们很复杂且令人恼火,但人们喜欢与太阳隐约同步的时间。