4

2012 年 11 月 4 日,美国的冬季时钟从凌晨 2 点变为凌晨 1 点。例如,凌晨 2 点 CDT 变成了凌晨 1 点 CST。

这意味着在凌晨 1:32 “发生了两次”:CDT 1:32(纪元 1352010776642)和一小时后 CST 1:32(纪元 1352014376642)。

是否有可能以某种方式timestamp在 PostgreSQL 中以普通类型区分两者?我们观察到,在 CDT 的 1:32,我们的应用程序将日期存储为 1352014376642(“第二次出现”)。

4

1 回答 1

2

据我所知,没有。

TIMESTAMP WITHOUT TIME ZONE(“plain” timestamp)就像您使用的那样直接存储本地时间,同样不存储关联的 UTC 偏移量或时区。它是本地时间,因此除非您存储与该本地时间关联的时区,否则它可能是许多不同的时间之一瞬间。

没有办法区分'2012-01-01 11:00 +0800'一次'2012-01-01 11:00 +0700'转换为timestamptz和存储。因此,如果您有一个 DST 班次导致一个小时在不同的时区重播,您将无法重建该信息。见证:

regress=> select extract(epoch from '2012-01-01 11:00 +0800'::timestamp),
                 extract(epoch from '2012-01-01 11:00 +700'::timestamp);
 date_part  | date_part  
------------+------------
 1325415600 | 1325415600
(1 row)

如您所见,时区被忽略;它被剥离并丢弃。timestamp字段不是用于识别离散时间点的正确类型,因此您是 SOL。


顺便说一句,TIMESTAMP WITH TIME ZONE使用设置将时间戳转换为 UTC 进行存储并返回本地时区进行检索timezone。它描述了一个瞬间(大致,见最后的链接)。这意味着在timestamptztimestamp 原来的时区一样丢失了。这令人困惑,并且似乎与数据类型的名称相矛盾。显然,标准就是这样,所以无论它是否愚蠢,我们都坚持使用它。为了区分时间戳,您还需要存储相关的 UTC 偏移量。最好命名为TIMESTAMP WITH TIME ZONE CONVERSION.

timestamptz对于存储离散的时间点很有好处,但对于存储在现实世界当地时间何时发生的事件并没有多大好处。同时存储 UTC 偏移量和/或 tzname。

看:

regress=> select extract(epoch from '2012-01-01 01:00 CST'::timestamptz),
                 extract(epoch from '2012-01-01 02:00 CDT'::timestamptz);
 date_part  | date_part  
------------+------------
 1325401200 | 1325401200

遗憾的是,没有数据类型可以TIMESTAMP WITH TIME ZONE与内部 UTC 偏移量结合,记录转换前的 TZ 偏移量。

也就是说,你不能依靠时钟来不加倍时间戳或以其他方式变得奇怪,因此有必要拥有对时间非常健壮并且不相信它有多大意义的代码。

于 2012-11-05T12:15:12.540 回答