Abh*_*bhi 4 java datetime hibernate
这似乎是一个愚蠢的问题,但我无法理解这种令人毛骨悚然的行为。我完全知道 javaDate类没有在其中存储任何 TimeZone 信息的事实。它只存储自 1970 年 1 月 1 日格林威治标准时间 00:00:00 以来的毫秒数
问题是,我使用的是驻留在具有 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
Run Code Online (Sandbox Code Playgroud)
现在是 Hibernate 的部分。我在以 IST 作为系统时区的机器上运行我的 Java Web 应用程序。我使用 Id 和createdDate作为Date对象的fetched属性做了一个简单的对象提取。我写了一个简单的代码来理解它的输出,这里是代码:
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));
Run Code Online (Sandbox Code Playgroud)
以下是此操作的输出:
1389955549000
2014-01-17 16:15:49.0
17/01/2014 04:15 PM IST
17/01/2014 10:45 AM UTC
Run Code Online (Sandbox Code Playgroud)
如果你把它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
Run Code Online (Sandbox Code Playgroud)
这不是预期的输出,对吧。它以毫秒为单位提供时间,距 UTC 时间为 -5:30,因此如果我尝试在 IST 时区中获取时间,那么它实际上为我提供了 UTC 时间
有没有人知道我做错了什么?
--------------------------我是如何修复它的-------------------- --------
基于Ako和Basil Bourque 的建议
Hibernate 在从数据库中获取日期/时间字段时会考虑系统时区。如果您已将UTC存储DateTime在数据库中,但您的系统时间或至少您的 Java 应用程序时区在其他时区(例如,IST - 印度标准时间),则休眠认为存储在数据库中的也在 IST 中,这就是导致整个问题的原因.DateTime
为此,正如Basil建议的那样,在不同的服务器上使用相同的时区。UTC 应该是首选。我应用的修复是我添加了以下代码:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Run Code Online (Sandbox Code Playgroud)
在ServletContextListener这让我的应用程序时区UTC现在Hibernate是获取和存储日期预期。
你的问题可以使用一些重写。
If I make a select query – 你应该解释这一点并给出准确的查询语句。
由于处理两个独立的服务器(数据库服务器、Web 应用程序服务器),每个服务器都有不同的时区设置,因此您应该更清晰地分离问题。确实,MySQL 服务器似乎只是一个红鲱鱼,分散注意力。
与其谈论无关的 MySQL 服务器,您应该测试并报告实际时间。很容易做到......只需谷歌“UTC当前时间”。
因此,老水手的格言是:使用一个或三个指南针,但永远不要使用两个。
三字母时区代码已经过时,既不标准化也不唯一。例如,“IST”表示“印度标准时间”和“爱尔兰标准时间”。
使用时区名称,如这个略显过时的列表所示。在您的情况下,+05:30,您可以使用“亚洲/加尔各答”,在旧表中也称为“亚洲/加尔各答”。这比UTC / GMT早 5.5 小时。
正如您所看到的,java.util.Date 和 Calendar 类是出了名的糟糕和混乱。避开它们。使用开源第三方Joda-Time,或者在 Java 8 中使用新的java.time.* 类(受 Joda-Time 启发)。
下面的代码在美国西海岸时区的 Mac 上使用 Joda-Time 2.3 和 Java 8。
让我们确定 1389975349000L ? 16:15 UTC?21:45 印度。
正如问题所述,这与 EpochConverter.com 一致。
// Specify a time zone rather than depend on defaults.
DateTimeZone timeZoneKolkata = DateTimeZone.forID( "Asia/Kolkata" );
long millis = 1389975349000L;
DateTime dateTimeUtc = new DateTime( millis, DateTimeZone.UTC );
DateTime dateTimeKolkata = dateTimeUtc.toDateTime( timeZoneKolkata );
Run Code Online (Sandbox Code Playgroud)
转储到控制台...
System.out.println( "millis: " + millis );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeKolkata: " + dateTimeKolkata );
Run Code Online (Sandbox Code Playgroud)
运行时…
millis: 1389975349000
dateTimeUtc: 2014-01-17T16:15:49.000Z
dateTimeKolkata: 2014-01-17T21:45:49.000+05:30
Run Code Online (Sandbox Code Playgroud)
问题提到了第二个数字:1389955549000L。
结果证明该数字与第一个数字的日期相同,但时间不同。
让我们确定 1389955549000L ? 10:45 UTC?16:15 印度。
long mysteryMillis = 1389955549000L;
DateTime mysteryUtc = new DateTime( mysteryMillis, DateTimeZone.UTC );
DateTime mysteryKolkata = mysteryUtc.toDateTime( timeZoneKolkata );
Run Code Online (Sandbox Code Playgroud)
转储到控制台...
System.out.println( "mysteryMillis: " + mysteryMillis );
System.out.println( "mysteryUtc: " + mysteryUtc );
System.out.println( "mysteryKolkata: " + mysteryKolkata );
Run Code Online (Sandbox Code Playgroud)
运行时…
mysteryMillis: 1389955549000
mysteryUtc: 2014-01-17T10:45:49.000Z
mysteryKolkata: 2014-01-17T16:15:49.000+05:30
Run Code Online (Sandbox Code Playgroud)
我不是 100% 确定,但是……
? 您的 Web 应用服务器计算机的时钟设置似乎不正确,设置为 UTC 时间而不是印度时间。
网络应用程序服务器显然是在印度时区的 16:15 时间,但显然当时印度的真实时间是 21:45。换句话说,时间与时区不匹配。
将 UTC 时间与非 UTC 时区混合 = 错误。
如果您设置印度时区,则设置印度时间以匹配。
请注意,我们在两组数字中都有共同的“16:15”。
java.util.Date 类的设计非常糟糕,它本身没有时区信息,但在其实现中应用了 Java 虚拟机的默认时区toString。这是避免此类的众多原因之一,但可能是您问题的关键。
避免使用 java.util.Date、java.util.Calendar 和 java.text.SimpleDateFormat 类。仅在需要时使用,以根据需要与其他类交换值。
使用 Joda-Time 或新的 java.time.* 类 (JSR 310)。
指定时区,不要依赖默认时区。
将时间与每个服务器上的时区相匹配。通过谷歌搜索“UTC 中的当前时间”进行验证。
尽可能将主机服务器的操作系统时区设置为UTC或GMT。在某些操作系统上没有这样的选择,所以选择“Atlantic/Reykjavik”作为一种解决方法,因为冰岛全年保持 UTC/GMT,没有任何夏令时废话。
不要不设定给呼叫的JVM的默认时区TimeZone.setDefault(除在最坏的情况下不得已而为之)。设置默认值是粗鲁的,因为它会立即影响在该 JVM 中运行的所有应用程序的所有线程中的所有代码。它是不可靠的,因为任何其他代码都可以在运行时在您的应用程序上更改它。相反,在所有日期时间代码中指定一个时区。永远不要隐含地依赖 JVM 的当前默认值。Joda-Time 和 java.time 都有接受时区参数的方法。因此,虽然将所有服务器计算机的主机操作系统的时区设置为 UTC 是一种很好的做法,但您永远不应该在 Java 代码中依赖它。
| 归档时间: |
|
| 查看次数: |
7821 次 |
| 最近记录: |