给定此表:

CREATE TABLE a(
  t TIMESTAMP WITH TIME ZONE
);


这个简单的JDBC代码片段:

DriverManager.getConnection(
    "jdbc:postgresql://localhost:5432/dbname", "user", "password"
).use { connection ->
    val nowSomeTimeZone = OffsetDateTime.now(ZoneOffset.of("+4"))
    connection.prepareStatement("insert into a(t) values (?)").use { insertStmt ->
        insertStmt.setObject(1, nowSomeTimeZone)
        insertStmt.executeUpdate()
    }
    connection.createStatement().use { stmt ->
        stmt.executeQuery("select * from a").use { resultSet ->
            resultSet.next()
            val t = resultSet.getObject(1, OffsetDateTime::class.java)
            println("$nowSomeTimeZone -> $t")
        }
    }
}


必须在JDBC堆栈内的某个位置发生从+04:00到UTC的自动转换,因为这是println输出:

2018-08-30T10:35:33.594+04:00 -> 2018-08-30T06:35:33.594Z


更奇怪的是,当我使用psql控制台客户端查看表格时,它向我显示了另一个时区(这是我的本地时区)中的时间戳:

$ psql -h localhost -U username
dbname=> select * from a;
             t
----------------------------
 2018-08-30 08:35:33.594+02


为什么会发生这种转换,如何禁用它?

最佳答案

禁用转换是不可能的,因为PostgreSQL服务器会剥离时区信息并将时间戳始终存储在UTC中,即使您明确使用TIMESTAMP WITH TIME ZONE类型也是如此。

引用PostgreSQL documentation


  对于带有时区的时间戳,内部存储的值始终以UTC(通用协调时间,传统上称为格林威治标准时间,GMT)表示。使用该时区的适当偏移量,将指定了明确时区的输入值转换为UTC。如果在输入字符串中未指定时区,则假定该时区位于系统的TimeZone参数指示的时区中,并使用时区时区的偏移量转换为UTC。


此外,文档指出:


  我们不建议使用带时区的时间类型(尽管PostgreSQL支持遗留应用程序并符合SQL标准)。 PostgreSQL假定您的本地时区为仅包含日期或时间的任何类型。


问题中描述的怪异行为是因为


JDBC驱动程序始终以UTC返回时间戳记
psql控制台客户端在显示时间戳之前将时间戳转换为用户的本地时区,在这种情况下为德国时间(+02:00)


感谢@RobbyCornelissen的见解。

关于java - 在PostgreSQL中存储OffsetDateTime时如何禁用到UTC的转换,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52092746/

10-11 22:47
查看更多