jOOQ returns 将日期时间偏移为 Z(UTC),即使它不是

jOOQ returns offset date time as Z (UTC) even though it's not

我有一个简单的 Postgres 测试 table id,带时区的时间戳。下面的测试和输出应该是不言自明的,但总而言之,我插入了一个时间戳偏移量为 -6 的行。它被正确地插入到数据库中,然后在同一时间从数据库中加载出来,但是偏移量错误,特别是 Z 而不是 -6。

我尝试将我的数据库设置为 UTC,当我在命令行手动 select 时,它正确地显示了 UTC 时间。将数据库设置为 mountain,它显示预期时间,偏移量为 -6。

通过在 jOOQ 中执行的语句将数据库强制到不同的时区似乎无济于事。

context.execute( "set timezone TO 'GMT';" ); // this has no effect

将我的系统时间强制为 UTC 有效地解决了这个问题,但由于许多原因无法接受table。

TimeZone.setDefault( TimeZone.getTimeZone( "UTC" ) ); // this is a band aid that works, but is not sustainable

这是单元测试:

@Test
public void confirmDateRoundTripFromDb() throws SQLException, DatatypeConfigurationException
{
    ZonedDateTime testDate = ZonedDateTime.of( 2019, 05, 30, 12, 54, 32, 203, TimeUtilities.CENTRAL_ZONEID );

    final OffsetDateTime testDateAsOffset = testDate.toOffsetDateTime( );

    try( PGConnection dbConnection = DatabaseUtility.getPostgresConnection( _unitTestConfig.getSection("Postgres").getProperties(), _testDbName ) )
    {
        DSLContext context = DSL.using( dbConnection, SQLDialect.POSTGRES );
        DateTestsRecord dateTestsRecord = context.newRecord( DATE_TESTS );
        dateTestsRecord.setTestTimestamp( testDateAsOffset );
        dateTestsRecord.store();

        int id = dateTestsRecord.getId();

        DateTestsRecord insertedRecord = context.selectFrom( DATE_TESTS ).where( DATE_TESTS.ID.eq( id ) ).fetchAny();
        System.out.println( testDateAsOffset );
        System.out.println( insertedRecord.getTestTimestamp() );
    }
}

并且输出:

2019-05-30T12:54:32.000000203-05:00
2019-05-30T11:54:32Z

有趣的是,如果我在中部添加日期,小时会正确更改为山区,但往返后的输出仍然只是愉快地报告 Z。

我想这不是预期的?难道我做错了什么?如果没有,是否有任何适用于全球的解决方法的想法?有几位开发人员对此感到兴奋,每次我们 select 时都必须使用一些特殊逻辑处理日期,这似乎很脆弱。

我已经 运行 3.10,但刚刚升级到 3.12,结果相同。

这不是 jOOQ 问题。 PostgreSQL 没有对应于 ZonedDateTime 的数据类型。它的 TIMESTAMPTZTIMESTAMP WITH TIME ZONE 类型实际上只是一个 java.time.Instant。考虑手册: https://www.postgresql.org/docs/current/datatype-datetime.html

For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter, and is converted to UTC using the offset for the timezone zone.

jOOQ 无能为力。

请注意,jOOQ 默认将所有 SQL 数据库中的 TIMESTAMP WITH TIME ZONE 类型映射到 java.time.OffsetDateTime,因为那是 JDBC 规范所做的。对于像 JDBC(和 jOOQ)这样的供应商不可知论者 API 来说,这是一个合理的默认值。但是如果你想拥有 PostgreSQL-native 行为,我建议将你所有的 TIMESTAMPTZ 类型重写为 INSTANT (如果你使用的是 jOOQ 3.12+)。

如果出于某种原因,您需要维护此信息,则需要将其存储在单独的列中,或作为格式化值存储在 text 列中。

不可能的 pgjdbc 驱动程序版本 0.7.1 与 jOOQ 之间存在不兼容性,导致在从 postgres 数据库中选择带有时区的时间戳时无法应用偏移量。

万一其他人也是运行这个jar组合,如果需要impossibl驱动程序中的功能,建议更新到pgjdbc 0.8.2,如果不需要,则放弃。