tl;博士
- 您看到的是功能,而不是错误,正在将同一时刻调整到另一个时区。
- 使用实时时区名称,而不是 3-4 个字母的伪时区。
- 使用现代的 java.time 类,而不是设计不佳的遗留类。
同一时刻:拨打withZoneSameInstant
您从比 UTC提前 小时的一个时区调整到比 UTC 落后 10 小时的另一个时区,总共相差 13 小时。了解一个地方的晚上 8 点同时在另一个地方是早上 7 点。
请注意我们在以下代码中对withZoneSameInstant(“即时”)的调用。
ZonedDateTime.of(
2018 , 7 , 9 , 20 , 2 , 0 , 0 , ZoneId.of( "Europe/Athens" ) // 8 PM in Greece.
)
.withZoneSameInstant( // Adjust into another time zone to view the same moment with another wall-clock time.
ZoneId.of( "Pacific/Honolulu" ) // 7 AM in Hawaii.
)
.toString()
2018-07-09T07:02-10:00[太平洋/檀香山]
不同时刻:拨打withZoneSameLocal
显然您想要一个不同的时刻,时间轴上的不同点,具有相同的日期和时间,但时区不同。
请注意我们在以下代码中对withZoneSameLocal(“本地”,而不是“即时”)的调用。
ZonedDateTime.of(
2018 , 7 , 9 , 20 , 2 , 0 , 0 , ZoneId.of( "Europe/Athens" ) // 8 PM in Greece.
)
.withZoneSameLocal( // Different moment, coincidentally having the same date and same time-of-day. But different time zone means this is a different point on the timeline.
ZoneId.of( "Pacific/Honolulu" ) // Also 8 PM in Hawaii, which happens many hours later than 8 PM in Greece. Different moment, same wall-clock time.
)
.toString()
2018-07-09T20:02-10:00[太平洋/檀香山]
详情
实时时区
HST & EEST 是伪时区,不是实时时区。避免使用这些 3-4 字母代码,因为它们不是标准化的,甚至不是唯一的(!)。
使用tzdata 中由IANA 定义的实时时区名称。请参阅list in Wikipedia(可能已过时)。这些名称采用Continent/Region 格式,例如America/Montreal 或Europe/Tallinn。
ZoneId z = ZoneId.of( "Europe/Vilnius" ) ;
避免使用旧的日期时间类
避免使用非常麻烦的课程Calendar 和SimpleDateFormat。这些在几年前被 java.time 类所取代。使用 java.time 更加清晰和容易。
让我们开始吧。我猜EEST 你想到的是东欧时区之一。我随意选择一个。
ZoneId zAthens = ZoneId.of( "Europe/Athens" ) ;
LocalDate ld = LocalDate.of( 2018 , Month.JULY , 9 ) ; // 2018-07-09.
LocalTime lt = LocalTime.of( 20 , 2 ) ; // 8:02 PM.
ZonedDateTime zdtAthens = ZonedDateTime.of( ld , lt , zAthens ) ;
生成一个String 表示该ZonedDateTime 对象的值。默认情况下,使用标准的ISO 8601 格式,明智地扩展以在方括号中附加时区名称。
String outputAthens = zdtAthens.toString() ; // Generate `String` in a format extending standard ISO 8601 format.
2018-07-09T20:02+03:00[欧洲/雅典]
HST 我猜你的意思是夏威夷时间。该区域的正确名称是Pacific/Honolulu。
ZoneId zHonolulu = ZoneId.of( "Pacific/Honolulu" ) ;
让我们将雅典时刻调整到夏威夷的另一个区域。同一时刻,时间轴上的同一点,但挂钟时间不同。想象一下,每个地方都有一对朋友打电话给每个人,同时抬头看着墙上的时钟。每个人都看到不同的时间,也可能是不同的日期,但他们同时经历了相同的时刻,时间轴上的相同点。
ZonedDateTime zdtHonolulu = zdtAthens.withZoneSameInstant( zHonolulu ) ; // Same moment (same `Instant` inside the `ZonedDateTime`) but a different time zone.
在那一天,檀香山比世界标准时间晚十小时,而雅典则比世界标准时间早三小时。那是十三个小时的总增量。因此,晚上 8 点(20:00)减去 13 是早上 7 点。我们预计会在夏威夷看到早上 7 点。让我们通过生成另一个 ISO 8601 格式的字符串来验证。
String outputHonolulu = zdtHonolulu.toString() ; // Generate `String` representing the value of the `ZonedDateTime` object.
2018-07-09T07:02-10:00[太平洋/檀香山]
果然,早上 7 点。
也许您想要的是位于夏威夷的同一日期和同一时间。这意味着您不代表同一个同时的时刻。您将代表时间轴上的不同点,相差几个小时。
ZonedDateTime 确实提供了这个功能。调用ZonedDateTime::withZoneSameLocal 概念上的含义:使用相同的内部LocalDate 和相同的内部LocalTime,但使用不同的分配ZoneId。
ZonedDateTime eightPmOnJuly9InPacificHonolulu = zdtAthens.withZoneSameLocal( zHonolulu) ;
String outputDifferentMoment= eightPmOnJuly9InPacificHonolulu.toString() ;
2018-07-09T20:02-10:00[太平洋/檀香山]
UTC
所有这些在时区之间来回切换都会让人发疯。通过专注于 UTC 来扎根。将 UTC 视为 真正的时间,而所有其他时区只是变体。
要从时区调整为 UTC,请从我们的 ZonedDateTime 对象中提取 Instant 对象。根据定义,Instant 始终采用 UTC。
Instant instantAthens = zdtAthens.toInstant() ;
Instant instantHonolulu = zdtHonolulu.toInstant() ;
Instant instantDifferentMoment = eightPmOnJuly9InPacificHonolulu.toInstant() ;
2018-07-09T17:02:00Z
2018-07-09T17:02:00Z
2018-07-10T06:02:00Z
末尾的 Z 表示 UTC,发音为 Zulu,由 ISO 8601 和其他标准定义。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。