我使用Hibernate(4.2)作为我的持久性提供程序,我有一个包含Date字段的JPA实体:
@Entity
@Table(name = "MY_TABLE")
public class MyTable implements Serializable {
. . .
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "START_DATE")
private Date startDate;
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
. . .
}
与START_DATE对应的列定义为START_DATE TIMESTAMP
(无时区)。
我在我的应用程序内部使用Joda-Time(2.3)处理日期(始终以UTC格式),并且在持久化实体之前,我使用Joda的toDate()
方法{{1} } class获取JDK DateTime
对象以遵守映射:
Date
当我在DB中查看存储的值时,我注意到某处(JDK?Hibernate?)使用代码运行的JVM的默认时区转换Date值。在我的情况下是“美国/芝加哥”。
问题确实在夏令时(DST)附近显现出来。例如,如果内部时间是
public void myMethod(DateTime startDateUTC) {
. . .
MyTable table = /* obtain somehow */
table.setStartDate(startDateUTC.toDate());
. . .
}
它存储为
2014-03-09T02:55:00Z
我想要的是将其存储为
09-Mar-14 03:55:00
然而,在CDT,3月9日凌晨2:55不存在(“春季前进”)。所以(JDK?Hibernate?)正在向前滚动日期。
我希望存储在数据库中的瞬间为UTC。毕竟,这就是我在我的应用程序内部处理它的方式,但是一旦我将其交给持久化,它就会转换为我的默认时区。
注意:我无法使用
设置默认TimeZone09-Mar-14 02:55:00
因为我正在运行的JVM在多个应用程序之间共享。
如何在不将JVM默认时区设置为UTC的情况下以UTC格式存储日期?
答案 0 :(得分:7)
我自己遇到了这个问题。我看到的是,即使您已将UTC指定为日期中的时区(并且可以通过将其打印出来并在末尾看到'Z'来看到这一点),出于某种原因,JVM希望接管并且使用JVM的默认时区为您转换日期。
无论如何,你需要的是一个自定义映射来解决这个问题。尝试使用Jadira:
@Entity
@Table(name = "MY_TABLE")
public class MyTable implements Serializable {
. . .
@Column(name = "START_DATE")
@Type(type="org.jadira.usertype.dateandtime.legacyjdk.PersistentDate")
private Date startDate;
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
. . .
}
默认情况下,Jadira的PersistentDate
类在将日期转换为存储在数据库中的毫秒值时使用UTC作为时区。您可以指定其他时区,但听起来像是您要存储的UTC。
正如您对帖子的评论所暗示的那样,有时您用来查询数据库的工具正在为您进行无意识的愚蠢自动 - 我 - 我 - JDK - 默认 - TZ转换,导致您认为该值仍然不正确
您也可以尝试存储原始值(作为INTEGER),以说服自己存储正确的毫秒值。
HTH,
摩西
答案 1 :(得分:4)
有一篇关于此意外时区转换问题的文章,您可以查看here。它提供了问题根源的解释,并说明了如何处理它。当然,主要的假设是我们希望在数据库中将日期存储为UTC。
例如,每当您从数据库中读取日期时(比如说:9:54 UTC),JDBC就会跳过有关时区的任何信息。所以JVM通过JDBC收到的是一个日期,解释为它在本地时区(对于我的情况,9:54 UTC + 2)。如果本地时区与UTC不同(通常也是如此),我们最终会得到不正确的时移。
写入数据库时会出现类似的情况。
有一个小型开源项目DbAssist为不同版本的Hibernate提供修复。因此,如果您正在使用Hibernate 4.2.21,只需将以下Maven依赖项添加到您的POM文件中,您的问题就解决了(可以在库github上找到详细的安装说明,例如Spring Boot)。
<dependency>
<groupId>com.montrosesoftware</groupId>
<artifactId>DbAssist-4.2.21</artifactId>
<version>1.0-RELEASE</version>
</dependency>
应用此修复程序后,实体类中的java.util.Date
字段将按预期读取和保留:就像它们在数据库中存储为UTC一样。如果您正在使用JPA注释,则不必更改实体中的映射(如上一个响应中所示);它是自动完成的。
在内部,修复程序使用自定义UTC日期类型,该日期类型会覆盖Hibernate的日期类型,以强制它将数据库中的所有日期视为UTC。然后,要应用从java.util.Date
到UtcDateType
的映射,它会使用@Typedef
注释。见下文:
@TypeDef(name = "UtcDateType", defaultForType = Date.class, typeClass = UtcDateType.class),
package com.montrosesoftware.dbassist.types;
如果您的项目依赖于Hibernate HBM文件或其他版本的Hibernate,请转到项目的github wiki以获取有关如何安装正确修复的更详细说明。
答案 2 :(得分:-2)
您是否尝试过使用DateTime对象的getDateTime(DateTimeZone x)?
这样的事情:
public void myMethod(DateTime startDateUTC) {
. . .
MyTable table = /* obtain somehow */
table.setStartDate(startDateUTC.toDateTime(DateTimeZone.UTC));
. . .
}
希望它可以帮到你!