从时间戳构造java.util.Date对象时的时区问题

时间:2014-01-20 07:55:34

标签: java date timezone

我正面临时区问题。让我试着描述:

我的网络应用程序& DB在具有与我的本地时区(India / Kolkata,即UTC + 5:30)不同的时区(欧洲/马德里,即UTC + 1)的服务器上运行。

让我们说当地时间是:1月15日星期三14:35:00 IST 2014和 该服务器当前时间是:2014年1月15日星期三10:05:00 CET,即滞后4:30。

现在,当我尝试使用我的网页(GUI)中的日历选择来保存数据库上的日期时间时,我选择的日期时间是:Wed Jan 15 18:30:00,最后的日期是正在保存在DB正在成为1月15日星期三14:00:00(看到差异正好是4:30小时滞后时间)。

让我告诉你上述流程的编码部分。

从我的网页日历中选择后 - >将所选日期时间的时间戳(以长整数)传递给我的服务器端控制器 - >在服务器端,我正在构建java.util.Date对象并保存到DB,如下所示:

java.util.Date newDt = new java.util.Date(timeStampInMillis);

这里,timeStampInMillis是long,我是从GUI中选择的日期时间转换而来的。

这里是将该服务器的当前时区(UTC + 01:00)中的给定时间戳保存,这就是保存的日期时间在4:30之后滞后的时间。

但我想保存,因为它是从日历GUI中选择的日期时间。

我有一些解决方案,比如在我的服务器端控制器中传递构造Date对象的时间戳,我可以从我的日历GUI传递所选日期时间的String表示,然后解析格式并保存到DB中。

但我想知道我是否可以使用Timezone API。

希望我的问题很清楚。如果没有,请澄清您的疑问。

需要你的帮助......

感谢。

2 个答案:

答案 0 :(得分:5)

不明确问题

你的问题可以写得更好。您应该尝试将其缩小到一个非常具体的示例。您甚至没有指定讨论中的毫秒值。

服务器时间

服务器几乎总是设置为没有夏令时的UTC / GMT时区。在某些系统(如Mac OS X)上,这很困难。在这种情况下,将机器的时区设置为“Atlantic / Reykjavik”,因为冰岛全年保持UTC / GMT状态而没有任何夏令时废话。

避免使用java.util.Date

java.util.Date&与Java捆绑在一起的.Calendar类非常麻烦。

其中一个难点是,虽然Date没有分配时区,但其toString方法在渲染字符串时使用默认时区。所以对于天真的程序员来说,似乎就像Date没有时区一样。

使用Joda-Time库或与Java 8捆绑在一起的新java.time。*类。搜索StackOverflow以获取两者的许多示例。

全球思考,本地出现

您的大多数业务逻辑和数据库存储都应以UTC / GMT(无时区偏移)完成。如Postgres这样称职的数据库默认会这样做。

作为一般规则,仅切换到时区以便呈现给用户。

时区

始终指定时区。不要依赖默认时区,因为这会导致生产出现意外或任何时候机器改变时区。

避免使用三个字母代码,因为它们既不标准也不唯一。使用适当的时区名称。

this one之类的列表中查找您的时区名称(稍微过时,阅读详细信息)。在你的问题中,你提到“印度/加尔各答”,我认为是错误的。应该是“亚洲/加尔各答”。

ISO 8601

如果您必须serialize,请仅使用ISO 8601格式。这种格式是人类可读的,明确的,并且有明确的定义。

印度时区示例:2014-01-19T12:38:31 + 05:30

UTC / GMT“祖鲁”的例子:2013-11-22T18:28.023Z

java.sql。* Classes

使用java.sql。*类通过JDBC与数据库进行通信。

通过传递1970年以来的毫秒来构造java.sql.Timestamp对象。在Joda-Time中,调用getMillis以获取要传递的值。

避免毫秒

通常,我更愿意避免处理毫秒来跟踪时间。人们往往会遇到麻烦,因为有些系统会在几秒,几毫秒或几纳秒的时间内跟踪时间。此外,有许多时代在使用,并不总是1970年第一天的Unix风格。

我试图绕过:

示例代码

但是如果你确定你的毫秒值代表自1970年第一天起以UTC / GMT为单位的真实毫秒数,那么就将这种代码与Joda-Time一起使用。注意'L'将数字标记为长整数。

DateTime dateTime = new DateTime( 1390276603054L );

DateTime dateTimeSpain = dateTime.toDateTime( DateTimeZone.forID( "Europe/Madrid" ) );
DateTime dateTimeIndia = dateTime.toDateTime( DateTimeZone.forID( "Asia/Kolkata" ) );
DateTime dateTimeUtcGmt = dateTime.toDateTime( DateTimeZone.UTC );

// For database.
java.sql.Timestamp timestamp = new java.sql.Timestamp( dateTimeSpain.getMillis() );

转储到控制台...

System.out.println( "dateTime (default time zone): " + dateTime );
System.out.println( "dateTimeSpain: " + dateTimeSpain );
System.out.println( "dateTimeIndia: " + dateTimeIndia );
System.out.println( "dateTimeUtcGmt: " + dateTimeUtcGmt );
System.out.println( "timestamp: " + timestamp ); // "toString" uses default time zone.

跑步时......

dateTime (default time zone): 2014-01-20T19:56:43.054-08:00
dateTimeSpain: 2014-01-21T04:56:43.054+01:00
dateTimeIndia: 2014-01-21T09:26:43.054+05:30
dateTimeUtcGmt: 2014-01-21T03:56:43.054Z
timestamp: 2014-01-20 19:56:43.054

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 1 :(得分:0)

毫秒时间戳没有时区。如果您在加尔各答和马德里同时创建新的Date(),则日期将具有相同的毫秒时间戳。只有在解释文本表示时,问题才会出现。同样,如果文本表示在马德里包含像Wed Jan 15 14:35:00 IST 2014 SimpleDateFormat.parse这样的时区,那么Kolkata将生成具有相同时间戳的Date对象。