首页 > 解决方案 > 使用 jdbc 时如何使用时区保存正确的日期时间

问题描述

我正在使用 Java 和 JDBC 连接到 PostgreSQL 数据库。我在节省数据库时间时遇到问题。我的数据库有表学生,列看起来像

Id,Name,Created_at(timestamp)

//some row here

我使用 JDBC 连接到 PostgreSQL 并保存我的数据

jdbc:postgresql://localhost:3306/someDatabase?useSSL=false&serverTimezone=UTC

当我插入数据时,我的数据看起来像

Student student = new Student()
student.name = "some name";
student.createdAt = LocalDateTime.now()

//一些逻辑保存

但是,当我插入数据库时​​,PostgreSQL 会保存与我的服务器相同的时间。我想如果我的时间服务器是“2021-10-10 00:00:00”,当我保存到数据库时,它必须保存到数据库为“2021-10-09 15:00:00”。我希望它保存时必须转换为日本时区。

当我选择数据库时,PostgreSQL +9 上的时区。如果我更改代码,例如:

TimeZone.setDefault(TimeZone.getTimeZone("UTC"))

它对我没有帮助。

标签: javapostgresqljdbctimezone

解决方案


您使用了错误的类和类型。

永远不要使用TimeZoneTimestamp类。这些是 Java 最早版本中糟糕的日期时间类的一部分,几年前被 JSR 310 中定义的现代java.time类所取代。特别由ZoneIdand Instant/取代OffsetDateTime

在 Postgres 中,确保您的created_at列的类型是TIMESTAMP WITH TIME ZONE,而不是WITHOUT

通常最好将当前时刻捕获为Instant. 该类代表 UTC 中的一个时刻。

Instant instant = Instant.now() ;

不幸的是,编写 JDBC 4.2 规范的人莫名其妙地需要对OffsetDateTime两个更常用的类InstantZonedDateTime. 无论如何,如果您的特定 JDBC 驱动程序不支持Instant.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 

或者,跳过Instant.

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

发送到数据库。

myPreparedStatement.setObject( … , odt ) ;

恢复。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

这里的LocalDateTime班级是错误的。该类表示没有时区上下文或与 UTC 偏移的日期和时间。所以那个类不能代表一个时刻,时间轴上的一个点。所以这个类不能用来记录什么时候发生的事情。我想不出任何情况下打电话LocalDateTime.now()是正确的做法。

这些主题已在许多现有的问题和答案中得到解决。搜索以了解更多信息。


推荐阅读