我的要求是在数据库中以 UTC 时区存储所有日期和日期时间。我正在使用 Java 8 LocalDate
& LocalDateTime
在我的 Hibernate 实体中。
是否正确为 LocalDate
& LocalDateTime
没有与它们关联的时区?
如果没有,我应该回退使用旧的(或遗留的?)Date
& Timestamp
?
或者我应该使用 Java 8 的 Instant
?如果使用 Instant
,是否有可能只存储日期部分而不存储时间?
数据库是 MySQL 和 SQL Server,这是一个 Spring Boot 应用程序。
最佳答案
“本地...”类型故意没有时区的概念。所以它们不代表时间线上的某个时刻。一个 LocalDateTime
表示可能时刻的模糊范围,但在分配偏移量或时区之前没有真正的意义。这意味着应用 ZoneId
获得 ZonedDateTime
.
例如,要说今年的圣诞节从 12 月 25 日的第一个时刻开始,我们说:
LocalDateTime ldt = LocalDateTime.of( 2017 , 12 , 25 , 0 , 0 , 0 , 0 );
但是,午夜的钟声在东方比在西方发生得更早。
这就是为什么 Sprite 的后勤部门从Kiribati开始绘制圣诞老人的路线的原因。在太平洋,世界上最早的时区比 UTC 早 14 小时。在那里交付后,他们将圣诞老人向西运送到新西兰等地,等待午夜过后。然后在午夜后前往亚洲。然后是印度,以此类推,几个小时后到达欧洲的午夜,然后几个小时后到达北美东海岸的午夜。所有这些地方都经历了同样的
LocalDateTime
在不同的时刻,每次交付都代表不同的 ZonedDateTime
目的。所以…
LocalDateTime
并写入 TIMESTAMP WITHOUT TIME ZONE
类型的数据库列中. ZonedDateTime
并写入 TIMESTAMP WITH TIME ZONE
类型的数据库列中. 关于第二个要点,请注意几乎每个数据库系统都将使用区域信息将日期时间调整为 UTC 并存储该 UTC 值。有些也保存区域信息,但有些如 Postgres 在使用它调整为 UTC 后丢弃区域信息。所以“with time zone”有点用词不当,真正的意思是“尊重时区”。如果您想记住那个原始区域,您可能需要将其名称存储在一个单独的列中。
使用
Local…
的另一个原因类型用于 future 的约会。政治家喜欢经常改变他们管辖的时区。他们喜欢采用夏令时 (DST)。喜欢改变他们的 DST 转换日期。他们喜欢放弃采用 DST。他们喜欢重新定义他们的时区,改变界限。他们喜欢有时以 15 分钟之类的数量重新定义他们与 UTC 的偏移量。而且他们很少提前通知,只需一两个月的警告就做出这样的改变。所以要预约明年或六个月后的体检,时区定义是无法预测的。所以如果你想预约上午 9 点,你应该使用
LocalTime
或 LocalDateTime
记录在 TIMESTAMP WITHOUT TIME ZONE
类型的数据库列中.否则,上午 9 点的约会,如果划在 DST 转换被推迟的地方,可能会显示为上午 8 点或上午 10 点。生成计划时间表时,您可以将时区 (
ZoneId
) 应用于那些“本地”(未分区)值以创建 ZonedDateTime
对象。但是,当政治家可能通过改变区域来破坏他们的意义时,不要依赖那些太远的时间。提示:这些对 DST 和时区的频繁更改意味着您必须保留时区 tzdata数据库是最新的。在您的主机操作系统、JVM 以及您的数据库系统(例如 Postgres)中都有一个 tzdata。这三个都应该经常更新。有时区域的变化比这些产品的计划更新周期更快,例如土耳其去年决定继续使用夏令时,只有几周的通知。因此,您可能偶尔需要手动更新这些 tzdata 文件。 Oracle 提供了一个用于更新其 Java 实现的 tzdata 的工具。
处理精确时刻的一般最佳实践是在 UTC 中跟踪它们。仅在必要时应用时区,例如在向用户展示时,他们希望看到他们自己狭隘时区的值。在 java.time 中,
Instant
class 代表时间线中的一个时刻。在 UTC 中,分辨率为纳秒。Instant instant = Instant.now() ; // Current moment on the timeline in UTC.
ZonedDateTime zdt = instant.atZone( z ) ; // Assign a time zone to view the same moment through the lens of a particular region’s wall-clock time.
Instant instant = zdt.toInstant(); // revert back to UTC, stripping away the time zone. But still the same moment in the timeline.
顺便说一句,符合 JDBC 4.2 及更高版本的驱动程序可以通过以下方式直接处理 java.time 类型:
PreparedStatement::setObject
ResultSet::getObject
奇怪的是,JDBC 4.2 规范不需要支持两种最常见的 java.time 类型:
Instant
& ZonedDateTime
.该规范确实需要支持 OffsetDateTime
.因此,您可以轻松地来回转换。避免使用旧的遗留数据类型,例如
java.util.Date
和 java.sql.Timestamp
只要有可能。它们设计不佳、令人困惑且有缺陷。理解所有这四个都是 UTC 时间线上的一个时刻的表示:
java.time.Instant
java.time.OffsetDateTime
分配的偏移量为 ZoneOffset.UTC
java.util.Date
java.sql.Timestamp
如果您想要一个没有时间和时区的仅日期值,请使用
java.time.LocalDate
.本类(class)取代 java.sql.Date
.对于特定的数据库,请注意 SQL 标准几乎没有涉及日期时间类型及其处理的主题。此外,各种数据库在支持日期时间特性方面差异很大,我的意思是广泛。有些几乎没有支持。有些将 SQL 标准类型与专有类型混合在一起,这些专有类型要么早于标准类型,要么旨在作为标准类型的替代品。此外,JDBC 驱动程序在向/从数据库编码(marshal)日期时间值的行为方面有所不同。一定要研究文档和练习,练习,练习。
关于java - 在数据库中使用 Java 8 LocalDate 和 LocalDateTime 进行 hibernate ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43476364/