JPA TemporalType.Date 给出错误的日期

2022-09-03 04:07:44

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

@Temporal( TemporalType.DATE )
private Date validFrom;

一切似乎都运行良好,直到我从数据库中提取日期并显示它。如果我在前端选择日期18-Sep-2003,那么当我签入数据库时,请保存它,以确保是持有的日期(数据库是MySQL 5.5.9列类型是DATE)。但是,当我提取列表记录时,显示的日期是2003年9月17日 - 提前一天。

如果我选择一个日期,比如2003年3月26日或2003年12月25日,一切都很好,所以我想这与夏令时有关,但错误在哪里?由于数据库似乎保存了正确的日期,我猜它一定是JPA转换回java.util.Date的时候 - java.util.Date是用于日期的最佳类吗?我见过一些人们使用日历的例子,但这似乎很重,我不确定它与基于JSF的前端的配合程度。


答案 1

非常抱歉,但到目前为止,所有答案通常都是不正确的。答案很简单,但需要我们分开五点:

  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时间转换为用户“本地”时区的地方。

摘要:在服务器、数据库和 Java 对象中使用 UTC (GMT+0)。

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

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

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

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


答案 2

经过大量的实验和搜索,我很确定我已经找到了问题的原因。该日期保存在java.util.Date中,该日期带有所有时间和时区的包袱。JPA 似乎正在从数据库中读取 2003 年 9 月 18 日的日期,然后像这样填充日期:“星期四 9 月 18 日 00:00:00 BST 2003” - 请注意,时区已设置为 BST,可能是因为数据库没有明确设置。无论如何,如果您只想看到这样的日期,则必须在JSF页面中格式化输出:

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

但是,这假设时区是计算机上当前有效的时区。在我的情况下,时区目前是GMT(因为它是冬天),所以当显示日期“星期四9月18日00:00:00 BST 2003”时,它通过减去一个小时将其转换为GMT,使显示显示2003年9月17日。


推荐