【问题标题】:ZonedDateTime to MySql -- Wrong Offset?ZonedDateTime 到 MySql——错误的偏移量?
【发布时间】:2021-07-26 02:16:25
【问题描述】:

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

问题如何使用 java.time* 保存日期(德国时间)以便 runtime = 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'

【问题讨论】:

  • MySQL中的数据类型是什么? timestamp? datetime?据我所知,MySQL 无法从ZonedDateTime 保存时区,那么您希望它如何工作?另外,当您再次检索该值时,您希望返回哪个ZonedDateTime
  • 1) Datetime(6) 2) 我不知道 ZoneDateTime 是否是正确的类。我只想根据德国时间线保存一个日期,例如:Year-Month-Day HH:MM:SS:xxxxxx 2021-05-03 16:11:42.021236 并将其恢复为已保存
  • A LocalDateTime 应该适用于在 MySQL 中保存到 datetime 和从 datetime 中检索。然而,Java LocalDateTime 和 MySQL datetime 都没有定义时间点,因为它们不保存时区或 UTC 偏移量。推荐的方法是存储到 MySQL 中的timestamp,然后使用 Basil Bourque 的答案。 MySQL 中的 timestamp 始终采用 UTC(与您所说的相反)。
  • 感谢您提供这些指导性问题和答案 :)
  • 您能否详细说明什么是“时间点”,什么不是?

标签: spring-boot hibernate spring-data-jpa java-time zoneddatetime


【解决方案1】:

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

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

关于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 中,这意味着 TIMESTAMP不是 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 格式存储。不幸的是,一些中间件和工具可能会选择对您从数据库中检索到的值进行时区调整。使用上面的代码,相信你不会有这个问题。

【讨论】:

  • 所以我想问的正确问题是:“我需要将特定区域的日期保存到数据库吗?” --> “可能不是,只使用 UTC 标准,使用 Instant.now() == 我可以写入 db 的时间点 (UTC+-0),但如果是这样,则使用 OffsetDateTime”--> 稍后,例如当前端要求时:转换为 ZonedDateTime 尊敬的先生,非常感谢您抽出宝贵时间进行详细说明!
  • 另外:如果使用 TIMESTAMP,它将仅自动保存为 UTC。我将使用您提供的有用资源来了解不同的 DATE、DATETIME 和 TIMESTAMP 类型
猜你喜欢
  • 2016-06-11
  • 1970-01-01
  • 2013-02-02
  • 1970-01-01
  • 1970-01-01
  • 2020-06-05
  • 2019-04-03
  • 2016-03-04
  • 1970-01-01
相关资源
最近更新 更多