我在类似问题上已经阅读了很多答案,但仍然无法理解整个逻辑。 我的目标是在我的应用程序内部处理日期(包括时间,而不仅仅是年月等),并考虑夏令时时间和服务器的时区。我正在使用hibernate和postgresql db。我的错误是使用 来自DB的所有日期的java.util.Date类型。很快我意识到它不包含时区信息。现在问题出现了,如何使用正确的日期(考虑日光和时区)?
这样做的正确方法是什么?使用不同类型的课程如日历?或者只是保持原样但是在我使用日期的每个地方我应该将它转换为带有日历的本地时区吗?
让我感到困惑的是,日期是如何从db进入Date对象的,以及将Date对象转换为Calendar以包含时区等是否安全??
答案 0 :(得分:2)
java.util.Date
不包含时区信息并不是真的。按照惯例,它是一个UTC时间戳(好吧,这不是因为Javadoc说它跳过了leap seconds)。
但是,如果你认为它是UTC,那么你很好,至少是Calendar.getTime()
方法。
在应用程序逻辑中使用Calendar
而不是Date
来构建/创建Date实例是一个很好的第一步。使用日历方法setTimeZone
管理应用程序中的时区,它会为您重新计算HOUR_OF_DAY
等字段值。
Hibernate使用JDBC来执行SQL语句,因此它绑定了JDBC API的可能性和您使用的JDBC驱动程序的行为,并导致数据库的可能性。
我的经验是,JDBC驱动程序和数据库在日期/时间处理方面确实不同,特别是在涉及时区时。例如,postgres sql具有带时区和不带时区的日期/时间字段类型。不幸的是,带有时区的类型不是SQL规范的一部分,因此它们不是JDBC规范的一部分。所以Hibernate不支持开箱即用。
简短解释的简短解释:
解决此问题的最常用方法是确保始终使用相同的时区存储和加载来自Hibernate的数据。一种简单的方法是使用UTC时区,因为Calendar.getTime()
方法总是将当前时间作为UTC Date
实例返回,您可以将其直接传递给Hibernate。
从数据库加载值,您可以使用Calendar
方法将其转换为Calendar.setTime(Date)
。
答案 1 :(得分:1)
确保在读取/写入数据库时日期(及其时区)一致的一种方法是确保在数据库中使用UTC时区。如前所述,JDBC存在一个常见问题,因为当它从数据库中读取日期时,它默认将日期视为本地JVM时区。因此,如果您的本地JVM时区与DB中使用的时区不同,您将面临意外的时区转换(例如,从不同时区的两个位置运行您的应用程序时)。
有一篇很好的文章解释问题的根源here。还有一个小的开源库DbAssist
,您可以在项目中使用它来确保JDBC和Hibernate从UTC时区处理数据库中的日期。
要包含此修复程序,请将以下依赖项添加到POM文件中:
<dependency>
<groupId>com.montrosesoftware</groupId>
<artifactId>DbAssist-5.2.2</artifactId>
<version>1.0-RELEASE</version>
</dependency>
此特定修复程序适用于Hibernate 5.2.2版本,但如果您使用任何其他Hibernate,请参阅此link以获取正确的修复版本。它还包括安装指南,根据您使用的是HBM文件还是JPA Annotations(带或不带Spring Boot)来映射您的实体字段,该指南略有不同。在这两种情况下,应用修复只是一行code。
这个库(修复)的好处是你不必更改实体类中的任何内容。添加依赖项并应用修复后,实体中的java.util.Date
字段将被视为UTC时区(从DB写入/读取时)。