【问题标题】:Java Calendar TimezoneJava 日历时区
【发布时间】:2019-09-07 06:47:48
【问题描述】:

我正在尝试将时间戳(毫秒)转换为另一个时区(GMT-7:00 America/Los Angeles),但转换后的时间不是我所期望的。有人可以向我解释为什么会发生这种情况,我该如何正确地做到这一点?我的本地时区是“GMT+5:30”

long timeMillis = 1567697400000l; // Thu 5 September 2019 21:00:00

TimeZone laTimeZone = TimeZone.getTimeZone("GMT-07:00");

Calendar losAngelesTime = Calendar.getInstance(laTimeZone);
losAngelesTime.setTimeInMillis(timeMill);

// I expect the date to be Thu 5 September 2019 14:00:00, 
// but I am getting Thu 5 September 2019 08:30:00

System.out.println(losAngelesTime.get(Calendar.HOUR_OF_DAY) + ":" + losAngelesTime.get(Calendar.MINUTE)+"  "+ losAngelesTime.get(Calendar.DAY_OF_WEEK));

【问题讨论】:

  • 您拥有的时间不是 2019 年 9 月 5 日星期四 21:00:00。现在是 2019-09-05T15:30:00Z,即 UTC 时区的 9 月 5 日 15:30。如果您从这个时间中删除 7 个小时,您将得到 8:30。请停止使用时区和日历。使用 java.time 包中的类。如果您想要洛杉矶时区,请使用ZoneId.of("America/Los_Angeles")
  • 你是对的,我使用了错误的时间戳。我用于毫秒到日期转换的网站显示的是我当地时间的日期。

标签: java date time calendar


【解决方案1】:

您提供的时间(毫秒)是格林威治标准时间 2019 年 9 月 5 日星期四 21:00:00 + 5:30,实际上是 UTC 时间 2019 年 9 月 5 日星期四 15:30:00。现在您只需将此时间转换为 LA 时区。这将为您提供 2019 年 9 月 5 日星期四 08:30:00 GMT-7:00。

每当时间以毫秒表示时,它实际上是从纪元算起的 UTC 毫秒时间。 documentation 明确说明了这一点。

一个瞬间可以用一个毫秒值来表示,该值是从 1970 年 1 月 1 日 00:00:00.000 GMT(格里高利)纪元开始的偏移量。

【讨论】:

【解决方案2】:

tl;博士

Instant                                 // Represent a moment in UTC with resolution of nanoseconds. 
.ofEpochMilli(                          // Parsing a count of milliseconds. 
    1_567_697_400_000L                  // Count of whole seconds since first moment of 1970 in UTC.
)                                       // Returns an `Instant` object.
.atZone(                                // Adjust from UTC to some time zone. Same moment, different wall-clock time.
    ZoneId.of( "America/Los_Angeles" )  // Use proper time zone names in `Continent/Region` format rather than mere offset-from-UTC (hours-minutes-seconds). 
)                                       // Returns a `ZonedDateTime` object. 
.toString()                             // Generate text in standard ISO 8601 format, wisely extended to append name of time zone in square brackets.

详情

另外两个答案by Aditi GuptaOle V.V. 都是正确的。我将添加一些示例代码。

您在一年前使用的糟糕的日期时间类被 java.time所取代。

Instant

我正在尝试转换时间戳(毫秒)

如果您的毫秒数是自 UTC 1970 年第一刻的纪元参考以来,则解析为 Instant

long input = 1_567_697_400_000L ;  // Count of milliseconds since 1970-01-01T00:00:00Z.
Instant instant = Instant.ofEpochMilli( input ) ;

instant.toString(): 2019-09-05T15:30:00Z

时区

到另一个时区(GMT-7:00 美国/洛杉矶)

使用proper time zone name 而不是仅仅从 UTC 偏移。

将时区 (ZoneId) 应用到您的 Instant 以调整到时区,从而生成 ZonedDateTime 对象。

ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

zdt.toString(): 2019-09-05T08:30-07:00[美国/洛杉矶]

始终指定时区

我的本​​地时区是“GMT+5:30”

您自己的本地时区应该与您的日期时间处理无关,JVM 的默认时间时间也应该如此。

java.time 中,时区和偏移量参数是可选的。如果省略,则隐式应用 JVM 的当前默认时区。在我看来,这是 java.time 设计中为数不多的缺陷之一——这些区域参数应该是必需的。我建议您始终明确指定所需/预期的时区

如果您愿意,我们可以调整到您自己的时区。 明确询问 JVM 的当前默认时区,以使您的代码意图对读者一目了然。

ZoneId zDefault = ZoneId.systemDefault() ;
ZonedDateTime zdtDefault = zdt.withZoneSameInstant( zDefault ) ;

zdtDefault.toString(): 2019-09-05T21:00+05:30[亚洲/加尔各答]

IdeOne.com 演示

看到这一切code run live at IdeOne.com


关于java.time

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

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

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

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

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案3】:

    正如 Aditi Gupta 已经说过的,8:30 是正确的。 21:00+05:30 等于 15:30 UTC,后者又等于 08:30-07:00,这与洛杉矶的太平洋夏令时间一致。您还可以通过this one 等在线纪元转换器检查 1 567 697 400 (seoncds) 等于 2019 年 9 月 5 日星期四 15:30:00 UTC。我相信您的期望来自于从本地时间减去 7 小时,而您应该从 UTC 时间减去 7 小时。

    我建议你不要使用TimeZoneCalendar。这些课程设计不良且早已过时。而是使用InstantZonedDateTimeZoneId,它们都来自java.time,现代Java 日期和时间API。有关进行转换的正确和现代方式,请参见例如this answer by Basil Bourque(您的问题可能被视为与其他问题的重复,这取决于您如何看待它)。

    不要使用GMT+5:30GMT-07:00 作为时区。后者在一年中的标准时间对于洛杉矶是不正确的。使用Asia/ColomboAsia/Kolkata 之类的内容,这更适合您的区域,然后使用America/Los_Angeles。它们更好地传达了您的意图,并且它们在历史和现在的日期全年都能正常工作。

    【讨论】:

    • 感谢您对新 Time API 的建议,我一定会去看看。
    猜你喜欢
    • 2015-10-15
    • 1970-01-01
    • 2015-10-02
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 2019-03-27
    • 1970-01-01
    相关资源
    最近更新 更多