在PostgreSQL中存储OffsetDateTime时如何禁用到UTC的转换

时间:2018-08-30 08:46:01

标签: java postgresql jdbc kotlin

给出此表:

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

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

1 个答案:

答案 0 :(得分:0)

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

引用PostgreSQL documentation

  

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

此外,文档指出:

  

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

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

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

感谢@RobbyCornelissen的见解。