Java 日历时区

Java Calendar Timezone

我正在尝试将时间戳(毫秒)转换为另一个时区 (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+5:30,实际上是 UTC 时间 2019 年 9 月 5 日星期四 15:30:00。现在您只需将这个时间转换为洛杉矶时区。这为您提供了 2019 年 9 月 5 日星期四 08:30:00 GMT-7:00。

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

An instant in time can be represented by a millisecond value that is an offset from the Epoch, January 1, 1970 00:00:00.000 GMT (Gregorian).

正如 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。有关进行转换的正确和现代方法,请参见 (您的问题可能被视为与其他问题重复,这取决于您如何看待它)。

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

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.

详情

其他两个答案 and by 都是正确的。我将添加一些示例代码。

您使用的 类 年前被 java.time 类[=118= 取代的糟糕日期时间].

Instant

I am trying to convert a timestamp (milliseconds)

如果您的毫秒计数是从 1970 年第一时刻开始的 UTC 纪元参考,请解析为 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

时区

to another time zone (GMT-7:00 America/Los Angeles)

使用 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[America/Los_Angeles]

始终指定时区

My Local timezone is "GMT+5:30"

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

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

如果您愿意,我们可以调整到您自己的时区。 明确询问 JVM 当前的默认时区 以使您的代码的意图 crystal-对 reader.

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

zdtDefault.toString(): 2019-09-05T21:00+05:30[Asia/Kolkata]

IdeOne.com 演示

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


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time 类.

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

在哪里获取java.time类?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.