ZonedDateTime 到 MySql -- 错误的偏移量?

问题描述

设置:Spring Boot '2.4.1' 应用程序,(休眠 5.4.25),Java 11

问题 如何使用 java.time* 保存日期(德国时间),以便运行时 = db

问题

调试期间: zoneddatetime = 2021-05-03 16:11:42.021236

数据库中: 日期时间(6) 2021-05-03 14:11:42.021236

@Entity
public class User {
  private zoneddatetime createdAt;

//getter&setter
//calling the setter like like: 
user.setCreatedAt(zoneddatetime.Now(ZoneId.of("Europe/Berlin")))
//saving via JPARepo...
Repository.save(user);

application.properties:

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MysqL57Dialect

#tried but no effect
hibernate.jdbc.time_zone=CET

#tried but no effect
hibernate.jdbc.time_zone=UTC

毕业

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'MysqL:mysql-connector-java'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

解决方法

我既不使用 Spring 也不使用 Hibernate,所以我无法专门提供帮助。并且您省略了关键细节,例如数据库列的确切数据类型,因此无法提供具体答案。但我可以给你一些一般性的建议。

使用 OffsetDateTimeZoneOffset.UTC 在 JDBC 中交流时刻

关于 ZonedDateTime,要知道 JDBC 4.2 spec 需要支持 OffsetDateTime。奇怪的是,规范省略了对更常用的 InstantZonedDateTime 类的支持。 Java 团队的这种设计选择让我感到困惑,尤其是在 Instant 方面,它来回转换非常简单。

➥ 无论如何,请专注于使用 OffsetDateTime 进行 JDBC 工作。

通常最好在 UTC 中完成所有业务逻辑、数据交换、数据存储、日志记录和调试。也就是说,与 UTC 的零时分秒偏移量,并且没有时区。仅在业务规则要求或用户对演示文稿的期望时使用时区。

在UTC 中看到的时刻的基本类是Instant。通常,这应该是您的班级成员字段的类型,用于跟踪某个时刻、时间轴上的特定点。

使用 Instant.now 捕捉当前时刻。记录何时创建“用户”对象:

Instant userCreated = Instant.now() ;

要在数据库中记录片刻,您的列的类型必须类似于 SQL 标准 TIMESTAMP WITH TIME ZONE。在 MySQL 8 中,这意味着 TIMESTAMPnot DATETIME。见the documentation

要将 Instant 对象的值写入数据库,您的 JDBC 驱动程序可能能够处理 Instant 对象,尽管在 JDBC 规范中是可选的。如果没有,或者如果您想编写更可移植的代码,convert to OffsetDateTime。指定 ZoneOffset.UTC 的常量以获取零时-分-秒的偏移量。

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

将该对象传递给准备好的语句。

myPreparedStatement.setObject( …,odt ) ;

检索。

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

轻松转换回简单的 Instant

Instant instant = odt.toInstant() ;

要适应德国时间,请应用 ZoneId 以获得 ZonedDateTime

ZoneId z = ZoneId.of( "Europe/Berlin" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

了解 MySQL 以 UTC 存储所有 TIMESTAMP 值,只有 UTC。不幸的是,一些中间件和工具可能会选择对您从数据库中检索到的值进行时区调整。使用上面的代码,相信你不会有这个问题。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...