JPA TemporalType.Date提供错误的日期

时间:2011-03-14 14:36:53

标签: java mysql jpa

我有一个类,其日期字段表示一段数据的“有效起始日期”。它的定义如下:

@Temporal( TemporalType.DATE )
private Date validFrom;

所有似乎都正常工作,我从数据库中提取日期并显示它。如果我在前端选择2003年9月18日的日期,那么当我在数据库中检查确定的日期是保存日期时保存它(数据库是MySQL 5.5.9列类型是DATE)。但是,当我拉出一份清单时,显示的日期是2003年9月17日 - 前一天。

如果我选择在2003年3月26日或2003年12月25日之前或之后的一个日期,一切都很好,所以我猜这是与夏令时有关但错误在哪里蔓延?由于数据库似乎保持正确的日期,我猜测它必须在JPA转换回java.util.Date时 - java.util.Date是用于日期的最佳类吗?我已经看过一些人们使用Calendar的例子,但这看起来非常重,我不确定它能用于基于JSF的前端。

6 个答案:

答案 0 :(得分:24)

非常抱歉,到目前为止所有答案通常都是错误的。 答案很简单,但要求我们将五点分开:

  1. DATE = java.sql.Date,它是java.util.Date的包装器,它是UTC时区中自Epoch以来的毫秒数。因此,这具有固定GMT + 0(UTC)时区中的年/月/日/小时/分/秒。但请注意,java.sql.Date将时间组件设置为零!
  2. TIMESTAMP = java.sql.TimeStamp,它是Date的组件包装器,它添加小数秒以支持SQL DATE类型标准。此类/类型与此问题无关或不需要,但简而言之,此日期加上时间。
  3. 数据库按定义存储DATE对象(使用UTC作为Java的偏移量),但可以将数据库中配置的时间转换为不同的时区。默认情况下,大多数数据库默认使用本地服务器时区,这是一个非常糟糕的主意。女士们,先生们......总是以UTC格式存储DATE对象。继续阅读...
  4. JVM和时区的时间必须正确。由于Date对象使用UTC,是否为您的服务器时间计算了偏移量?考虑到强烈建议将服务器时间设置为GMT + 0(UTC)。
  5. 最后,当我们想要从数据库渲染DATE时(使用JSF或其他), 应设置为GMT + 0时区,如果从服务器上方也完成..你的日期和时间总是一致的,参考的和所有美好的事物。剩下的就是渲染时间,这就是用户代理(例如用于web应用程序)可以用于将GMT + 0时间转换为用户“本地”时区的地方
  6. 摘要:在服务器上,数据库中,在Java对象中使用UTC(GMT + 0)。

    DATE和TIMESTAMP仅与数据库视角不同,因为TIMESTAMP会携带额外的秒数。两者都使用GMT + 0(隐含)。 JodaTime是一个首选的日历框架来处理所有这些,但不会解决不匹配的JVM与数据库时区设置的问题。

    如果从JVM到数据库的应用程序设计不使用GMT,由于夏令时,时钟调整以及在世界本地时钟中播放的各种其他区域游戏......交易时间和其他一切都将永远是倾斜的,非指涉的,不一致的等等。

    关于数据类型的另一个好的相关答案:java.util.Date vs java.sql.Date

    另请注意,Java 8具有更好的日期/时间处理(最终),但这并不能解决JVM运行的服务器时钟是在一个时区中而数据库在另一个时区中。此时总会发生翻译。在我使用的每个大型(智能)客户端中,由于这个原因,数据库和JVM服务器时区设置为UTC,即使它们的操作主要发生在其他某个时区。

答案 1 :(得分:5)

经过多次实验和搜索,我很确定我找到了问题的原因。日期保存在java.util.Date中,该日期包含所有时间和时区的行李。似乎JPA正在从数据库中读取2003年9月18日的日期,然后像这样填写日期:“Thu Sep 18 00:00:00 BST 2003” - 注意时区已设置为BST可能因为它不是由数据库显式设置。无论如何,如果你只想看到这样的日期,有必要格式化JSF页面中的输出:

<h:outputText value="#{t.validFrom}">
    <f:convertDateTime pattern="dd MMM yyyy"/>
</h:outputText>

然而,这假设时区是机器上当前有效的任何时区。在我的情况下,时区目前是格林尼治标准时间(因为它是冬天),所以当提交日期“星期四9月18日00:00:00 BST 2003”时,它将其转换为GMT,减去一小时,显示器显示在2003年9月17日。

答案 2 :(得分:2)

但是使用DATETIME而不是日期将导致一小时(更多取决于时区)差异,如果您处理日期而不是时间值,则可以忽略。 对我来说,来自mysql数据库的数据是正确的值,但是当使用没有timeZone参数的f:convertDateTime时会出现差异,导致默认使用GMT!

<h:outputText value="#{test.dt}">
  <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss" timeZone="CET"/>
</h:outputText>

工作正常,但我认为当我们切换到CEST时,这将不再有效....

答案 3 :(得分:1)

我遇到了同样的问题。不知道原因,但我的解决方法如下:

在数据库中,我将列类型从DATE更改为DATETIME

在实体类中,我更改了@Temporal注释但保留了数据类型Date

@Temporal(TemporalType.TIMESTAMP)
private Date myDate;

答案 4 :(得分:1)

我遇到了同样的问题,但我使用JSF 2作为前端。如果您正在使用JSF组件,请查看此其他stackoverflow讨论,并查看JSF 2无法按预期的TimeZone规则播放。设计师实施它以始终使用GMT。在我的情况下,这导致我的日期在数据库中关闭了5或6个小时,但显示正确。

JSF convertDateTime renders the previous day

答案 5 :(得分:0)

与SQL Server有同样的问题。问题是旧的SQL JDBC驱动程序。从2010年4月开始,sqljdbc4.jar与SQL 2000兼容,并且日期有一两天的问题。 然后更新到最新的驱动程序,甚至从2012年更新到一个,问题就消失了。