【发布时间】:2014-02-07 05:35:39
【问题描述】:
这似乎是一个愚蠢的问题,但我无法理解这种令人毛骨悚然的行为。我完全知道 java Date 类不会在其中存储任何 TimeZone 信息这一事实。它只存储自 1970 年 1 月 1 日 00:00:00 GMT 以来的毫秒数
问题是,我正在使用驻留在具有 UTC 时区的服务器上的 MySql,并且我也仅将 DateTime 存储在 UTC 中。如果我进行选择查询,那么我会得到这个日期2014-01-17 16:15:49
通过使用http://www.epochconverter.com/ 我明白了:
Epoch timestamp: 1389975349
Timestamp in milliseconds: 1389975349000
Human time (GMT): Fri, 17 Jan 2014 16:15:49 GMT
Human time (your time zone): Friday, January 17, 2014 9:45:49 PM
现在是 Hibernate 的一部分。我在以 IST 作为系统时区的机器上运行我的 Java Web 应用程序。我使用 Id 做了一个简单的对象提取,并提取了 createdDate 属性,这是一个 Date 对象。我写了一个简单的代码来理解它的输出,这里是代码:
Date dt = c.getCreatedDate();
System.out.println(dt.getTime());
System.out.println(dt);
DateFormat df = new SimpleDateFormat("dd/MM/yyyy hh:mm a z");
df.setTimeZone(TimeZone.getTimeZone("IST"));
System.out.println(df.format(dt));
df.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(df.format(dt));
下面是这个的输出:
1389955549000
2014-01-17 16:15:49.0
17/01/2014 04:15 PM IST
17/01/2014 10:45 AM UTC
如果你把这个1389955549000 放在http://www.epochconverter.com/ 中,那么你会得到以下输出:
GMT: Fri, 17 Jan 2014 10:45:49 GMT
Your time zone: Friday, January 17, 2014 4:15:49 PM GMT+5.5
这不是预期的输出,对吧。它给了我以毫秒为单位的时间,即从 UTC 时间开始的 -5:30,所以如果我尝试在 IST 时区获取时间,那么它实际上给了我以 UTC 为单位的时间
有人知道我哪里做错了吗?
--------------我是如何解决的---------- ------------------
基于 - Ako 和 Basil Bourque 的建议
Hibernate 在从数据库中获取日期/时间字段时会考虑系统时区。如果您已将DateTime 存储在数据库中的 UTC 中,但您的系统时间或至少您的 java 应用程序时区位于其他时区(例如,IST - 印度标准时间),则休眠认为 DateTime 已存储在数据库中也在 IST 中,这就是导致整个问题的原因。
为此,正如 Basil 建议的那样,在不同的服务器上使用相同的时区。应该首选UTC。我应用的修复是我添加了以下代码:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
在ServletContextListener 中,这使我的应用程序时区位于UTC,现在休眠正在按预期获取和存储日期。
【问题讨论】:
-
为什么不正确? UTC 中的“17/01/2014 10:45”与 IST 中的“17/01/2014 16:15”相同。对我来说看起来是正确的。
-
@ako 这是不正确的,因为看看顶部,mysql 输出是格林威治标准时间 16:15:49,而 java 输出同样是 IST 下午 04:15,或者你可以说它是 16:15 IST 而UTC 或根据java,格林威治标准时间是上午10:45。并且您评论中的 16:15 不应该是 IST,根据 mysql 中的条目应该是 UTC
-
您可以使用选项 -Duser.timezone=UTC 运行 JVM。它应该有帮助。请参阅link 了解更多信息。
-
但是为什么会这样呢?它应该以毫秒为单位获取正确的时间。因为
Date不知道时区。您在hibernate中给出的链接中提到的解决方案有什么方便的方法吗? -
来自link,方法
setDate(int, java.sql.Date, java.util.Calendar):驱动程序使用Calendar 对象构造一个SQL DATE 值,然后驱动程序将其发送到数据库。使用 Calendar 对象,驱动程序可以在考虑自定义时区的情况下计算日期。如果未指定日历对象,则驱动程序使用默认时区,即运行应用程序的虚拟机的时区。