我如何确保围绕 PostgreSQL 的整个开发环境不会与本地时区发生冲突。为简单起见,我需要 100% 确定每个时间(标记)值都是 UTC。当我使用
timestamp without time zone
函数插入带有 CURRENT_TIMESTAMP
(!) 的行时,我不得不意识到情况并非如此,即使我从未指定过任何时区信息。
有没有任何分步手册可以帮助我摆脱时区?
首先了解 Postgres 如何处理时间戳和时间:
timestamp [without time zone]
进行操作,但您的客户端中仍然有一个时区。
您的陈述:
当我使用
函数插入带有timestamp without time zone
(!) 的行时...CURRENT_TIMESTAMP
是形容词中的矛盾。
CURRENT_TIMESTAMP
返回 timestamp with time zone
(!)。如果您只是将其强制转换(或自动强制)到 timestamp [without time zone]
,则时区偏移量将被截断而不是应用。您将获得当前会话的 timezone
设置的当地时间,而不是 UTC。考虑:
SELECT CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
, CURRENT_TIMESTAMP::timestamp
除非您的本地时区设置是“UTC”或“伦敦”之类的内容,否则这两个表达式将返回不同的值。
如果您想保存在您的时区中看到的文字值,请使用以下之一:
SELECT CURRENT_TIMESTAMP::timestamp
, now()::timestamp
, LOCALTIMESTAMP;
如果您想保存以 UTC 表示的时间点,请使用以下选项之一:
SELECT CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
, now() AT TIME ZONE 'UTC;
您陷入了一个重大误解:时间戳不包含任何时区信息!有关详细信息,请参阅我的其他答案此处。换句话说,您的整个开发环境已经不使用时区。您需要确保的唯一一件事是,当时间的文本表示形式转换为时间戳(反之亦然)时,进行转换的人知道文本表示形式表示的时区。对于其他所有内容,时区是无关紧要的。
这都怪Sun!他们认为,如果包含在时间戳对象本身内部将时间戳与文本相互转换的方法(首先使用
Date
,然后使用 Calendar
),对开发人员来说会很方便。由于此转换需要时区,因此他们认为如果同一个类存储时区会更加方便,这样您就不必每次进行转换时都传递它。这助长了 Java 有史以来最普遍(最具破坏性)的误解之一。我不知道用其他语言开发的人有什么借口。也许他们只是愚蠢。
声明日期列“timestamptz”或“带时区的时间戳”。
您是否还询问转换未存储时间戳的现有数据?
TIMEZONE WITH TIME ZONE
无时区的时间戳
Postgres 和 SQL 标准中的
TIMESTAMP WITHOUT TIME ZONE
数据类型表示日期和时间,但没有任何时区或相对 UTC 偏移量的概念。所以这个类型不能代表一个时刻,不是时间轴上的一个点。
您向此类列提交的任何时区或偏移信息都将被忽略。
跟踪特定时刻时,请使用另一种类型,
TIMESTAMP WITH TIME ZONE
。在 Postgres 中,您向此类列提交的任何时区或偏移信息都将用于调整为 UTC(然后被丢弃)。
为了简单起见,我需要 100% 确定每个时间(标记)值都是 UTC。
然后使用类型为
TIMESTAMP WITH TIME ZONE
的列。
有任何分步手册可以帮助我摆脱时区吗?
您确实不想想要摆脱时区(和偏移量),因为这意味着您将留下不明确的日期和时间。例如,今年 1 月 23 日的中午无法告诉我们您指的是日本东京的中午、法国图卢兹的中午还是美国俄亥俄州托莱多的中午。这些都是不同的时刻,相隔几个小时。
使用 JDBC 4.2,我们可以交换 java.time 对象,而不是可怕的遗留日期时间类。
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
检索。
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
这些值均采用 UTC 格式,偏移量为零时-分-秒。
请注意,许多工具和中间件(例如 PgAdmin)会欺骗您。在一个善意的反功能中,他们对从数据库中提取的数据应用了默认时区。
TIMESTAMP WITH TIME ZONE
类型的值在 Postgres 中始终存储在 UTC 中。但您的工具可能会像在 America/Montreal
、或 Pacific/Auckland
或任何其他默认时区一样报告该值。
我建议始终将此类工具中的默认时区设置为 UTC。