【问题标题】:jOOQ Timestamp being stored with local Timezone offsetjOOQ 时间戳与本地时区偏移量一起存储
【发布时间】:2015-03-10 21:54:16
【问题描述】:

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

我有一个包含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 偏移量。

鉴于时间是在与时区无关的容器 (Timestamp) 中提供的,我希望在创建插入语句时能够遵守这一规定。

我如何让 jOOQ 创建不在本地时间的时间戳?

【问题讨论】:

  • 那么你的域模型直接定义了Timestamp字段?所以它没有通过 jOOQ 转换器jooq.org/doc/3.5/manual/sql-execution/fetching/…
  • 正确 - java.util.Timestamp 是 jOOQ 生成的,也是我填充的。没有中间转换器。
  • 我看到一些对 PostgreSQL 驱动程序的引用是造成这种情况的原因。如果是这种情况,我可以利用 jOOQ 中的任何方法将时间戳作为 epoch 值的函数,例如 TIMESTAMP WITHOUT TIME ZONE 'epoch' + 1421109419 * INTERVAL '1 second'
  • 我不确定此信息是否适用于您的情况,但在我的情况下,我尝试使用 between( date1, date2 );无论两个日期对象在哪个时区,查询总是将它们转换为本地时间。我可以使用between( date1.toString(), date2.toString() ) 绕过它,两个日期对象都在UTC。

标签: postgresql jdbc timezone timestamp jooq


【解决方案1】:

不幸的是,有几件事对你不利:

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

由于这两个问题,如果您有两个不同的 JVM — 一个使用洛杉矶时间,另一个使用纽约时间 — 每当您使用一个 JVM 编写 TIMESTAMP 时,在其他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

【讨论】:

    【解决方案2】:

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

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

    唉 jOOQ 在保存到 PostgreSQL“没有时区的时间戳”或 MySQL“日期时间”字段时只使用本地时区。这个讽刺的源代码证据是here,它没有指定时区,也没有任何工具让用户覆盖这个功能并指定一个时区。这使得 jOOQ 中这种非常基本的数据类型的使用完全无用,许多客户端都使用不同的时区将数据写入同一个字段,而不记录他们的时区,也没有将数据标准化为 UTC。

    JDBC 提供了一个额外的three-argument setTimestamp,用户可以在其中指定所需的时区(UTC 基本上是唯一有意义的值)。然而 jOOQ 从 JDBC 中“抽象”出来并且不提供这种功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-04
      • 2017-11-22
      • 2017-07-04
      • 2012-07-15
      • 1970-01-01
      • 2011-01-07
      • 2019-10-08
      相关资源
      最近更新 更多