jOOQ 时间戳与本地时区偏移一起存储

jOOQ Timestamp being stored with local Timezone offset

PostgreSQL 9.3 / postgresql-9.3-1100-jdbc41.jar

我有一个 table,其中有一列类型为 timestamp without time zone,这会生成我的对象,其中包含适用的 java.util.Timestamp 属性.

我在插入期间看到的是 jOOQ 的绑定过程,将 java.util.Timestamp 转换为具有本地时区偏移的日期。

例如,对于 unix 时间戳 1421109419(2015 年 1 月 13 日 00:36:59 GMT),属性 设置为 new Timestamp(1421109419 * 1000)

从我看到的 jOOQ 记录器:

2015-01-13 14:14:31,482 DEBUG [http-bio-8002-exec-4] org.jooq.tools.LoggerListener#debug:255 - -> with bind values      : insert into "foo"."bar" ("start_date") values (timestamp '2015-01-13 13:36:59.0') returning "foo"."bar"."id"

2015-01-13 14:14:31,483 TRACE [http-bio-8002-exec-4] org.jooq.impl.DefaultBinding#trace:179 - Binding variable 3       : 2015-01-13 13:36:59.0 (class java.sql.Timestamp)

果然记录中的值是“2015-01-13 13:36:59”。

该软件在 NZDT 的机器上 运行,它解释了 +13 偏移量。

鉴于时间是在与 TimeZone 无关的容器(时间戳)中提供的,我希望在创建插入语句时能够兑现。

如何让 jOOQ 创建时间戳而不是本地时间?

不幸的是,有些事情对您不利:

  1. PostgreSQL JDBC 驱动程序在 Postgres 会话中将时区设置为您的 JVM 时区。因此,即使您的数据库服务器在 UTC 中是 运行,也会使用您的 JVM 的时区插入一个 TIMESTAMP 字段。当您插入或查询数据时,数据库服务器将始终使用 JVM 时区。
  2. 您正在使用 TIMESTAMP 而不是 TIMESTAMPTZ。这些类型的描述并不反映它们的实际用途。 TIMESTAMPTZ 实际上意味着时区不可知论。无论您插入什么值,它都将使用会话时区调整为 UTC。

由于这两个问题,如果您有两个不同的 JVM——一个使用洛杉矶时间,另一个使用纽约时间——每当您用一个 JVM 编写 TIMESTAMP 时,它都会是不同的 "UTC time" 在另一个 JVM 中。 TIMESTAMP 采用调整后的值并按给定的方式使用它。如果您将 TIMESTAMP 列更改为 TIMESTAMPTZ,那么两个 JVM 中的同一时间将始终是相同的 UTC 时间。

如果您查看 Postgres JDBC 驱动程序的 ConnectionFactoryImpl#openConnectionImp,您可以看到它将本地 JVM 的时区设置为数据库服务器会话区的时区。

所以处理这个问题的唯一明智的方法是只使用 TIMESTAMPTZ 而不是 TIMESTAMP。这里有一些关于此的更多信息:

PostgreSQL/JDBC and TIMESTAMP vs. TIMESTAMPTZ

http://justatheory.com/computers/databases/postgresql/use-timestamptz.html

以下(非常讨厌的)代码对我有用:

eventsRecord.setCreatedOn(new Timestamp(System.currentTimeMillis() 
  - TimeZone.getDefault().getOffset(new Date().getTime())));

唉,jOOQ 在保存到 PostgreSQL "timestamp without timezone" 或 MySQL "datetime" 字段时只使用本地时区。这种讽刺的源代码证据是 here,它没有指定时区,也没有任何工具供用户覆盖此功能并指定时区。这使得来自 jOOQ 的这种非常基本的数据类型的使用完全无用,许多客户端都具有不同的时区,将数据写入同一字段而不记录他们的时区,也没有将数据规范化为 UTC。

JDBC 提供了一个额外的 three-argument setTimestamp,用户可以在其中指定所需的时区(UTC 基本上是唯一有意义的值)。但是 jOOQ "abstracts" 远离 JDBC 并且不提供此功能。