偏移量与时区
"Europe/Rome"
是时区的名称,而不是偏移量。
与 UTC 的偏移只是比 UTC 的基线提前或落后几个小时-分钟-秒。当您向东移动时,当地时间比 UTC 时间越来越早,而向西移动到美洲,当地时间比 UTC 时间越来越多。
时区更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。
以、或等格式指定适当的时区名称。永远不要使用 2-4 个字母的缩写,例如或因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。Continent/Region
America/Montreal
Africa/Casablanca
Pacific/Auckland
EST
IST
你说:
与日期和时间相关的所有内容都由OffsetDateTime
对象处理
不,你不应该在所有情况下都使用那个单一的类。OffsetDateTime
类用于表示您知道偏移量但不知道时区的时刻。ZonedDateTime
当您知道时区时应该使用。对于 UTC 中的特定时刻(偏移量为零),通常最好使用Instant
.
OffsetDateTime
很少有合适的课程可供选择。时区总是比单纯的偏移更可取。所以通常你会想要ZonedDateTime
或Instant
。一般来说,程序员和系统管理员应该在 UTC 中思考、工作、记录和调试,因此使用Instant
. 要在预期时区或业务规则需要的地方向用户展示,请使用ZonedDateTime
. 一个大的例外是用于数据库访问的JDBC工作——JDBC 4.2 规范莫名其妙地需要兼容的驱动程序来支持OffsetDateTime
,但Instant
也不支持ZonedDateTime
。幸运的是,类之间的转换非常简单易行。
片刻
您需要了解片刻和非片刻之间的区别。片刻是时间线上的特定点。忘记时区和偏移量,如果摩洛哥卡萨布兰卡的某个人给日本东京的某个人打电话,他们分享的是同一时刻。要跟踪某个时刻,请使用上面以蓝色块显示的三个类之一:Instant
、OffsetDateTime
、ZonedDateTime
。
Instant phoneCallStartedUtc = Instant.now() ; // A moment as seen in UTC.
ZonedDateTime phoneCallStartedCasablanca = phoneCallStartedUtc.atZone( ZoneId.of( "Africa/Casablanca" ) ) ;
ZonedDateTime phoneCallStartedTokyo = phoneCallStartedUtc.atZone( ZoneId.of( "Asia/Tokyo" ) ) ;
请参阅在 IdeOne.com 上实时运行的代码。
phoneCallStartedUtc.toString(): 2021-03-28T20:33:58.695669Z
phoneCallStartedCasablanca.toString(): 2021-03-28T21:33:58.695669+01:00[Africa/Casablanca]
phoneCallStartedTokyo.toString(): 2021-03-29T05:33:58.695669+09:00[Asia/Tokyo]
所有这三个对象,phoneCallStartedUtc
,phoneCallStartedCasablanca
, 和phoneCallStartedTokyo
,都代表同一个同时发生的时刻。他们唯一的区别是通过不同的挂钟时间来查看(人们抬头看墙上的时钟时所看到的)。
一刻都没有
未来的约会,例如餐厅何时开业,或患者下次看牙医的时间,都无法被跟踪为片刻。
我们可能知道我们打算让餐厅开业的日期和时间,但我们不知道那一刻。这是因为时区使用的时钟时间是由政客定义的。
世界各地的政治家都表现出改变其管辖范围内时区的时钟定义规则的倾向。他们将时钟作为一种政治声明,以将自己与邻近的司法管辖区区分开来,或者相反,以与邻居保持一致。政客们改变他们的区域规则来操纵金融市场。出于象征和后勤原因,他们在战争和占领时期移动时钟。政客们在加入时尚时会改变时钟,例如夏令时 (DST)。后来,当他们加入不同的时尚时,他们会再次移动时钟,例如全年都在夏令时。
这里要吸取的教训是,时区规则会发生变化,它们会以惊人的频率变化,而且它们会在很少甚至没有预警的情况下发生变化。因此,例如,下周二的下午 3 点实际上可能不会在您期望的周三出现。下一个下午 3 点可能比现在预想的早一个小时,比你现在预想的晚半小时,没人知道。
要跟踪由时钟跟踪的未来约会,例如您的餐厅开业,请使用LocalTime
一天中的时间和LocalDate
日期。在适当的情况下,您可以将这两者组合成LocalDateTime
对象。单独跟踪预期的时区。然后在运行时需要计算事件的调度时,将时区应用LocalDateTime
到ZonedDateTime
. 意志决定了ZonedDateTime
一个时刻,但你不能储存那个时刻,因为政客们可能会把时钟移到你身上,从你脚下撕下地毯。
// Store these three values.
ZoneId zoneIdRome = ZoneId.of( "Europe/Rome" ) ;
LocalTime restaurantOpening = LocalTime.of( 9 , 30 ) ; // 09:30 AM.
LocalDate nextTuesday = LocalDate.now( zoneIdRome ).with( TemporalAdjusters.next( DayOfWeek.TUESDAY ) ) ;
// Do NOT store this next value. Use this next value only on-the-fly at runtime.
ZonedDateTime momentWhenRestaurantIsExpectedToOpenNextTuesday =
ZonedDateTime.of( nextTuesday , restaurantOpening , zoneIdRome ) ;
查看在 IdeOne.com 上实时运行的代码。
restaurantOpening.toString(): 09:30
nextTuesday.toString(): 2021-03-30
momentWhenRestaurantIsExpectedToOpenNextTuesday.toString(): 2021-03-30T09:30+02:00[Europe/Rome]
如果您想跟踪餐厅实际营业的时间,那么您将跟踪时刻。然后,您可能会使用上面讨论的三个类之一Instant
。
Instant momentWhenRestaurantActuallyOpened = Instant.now() ; // 09:41 AM, eleven minutes late because the manager could not find the door keys that had dropped to the floor.
顺便说一句,一些未来的约会不是由时钟驱动的。例如,火箭发射是根据自然、预期的天气条件安排的。政客们可能会改变时钟,改变时区规则,但这不会改变火箭发射的时刻。政客们对时区的这种改变将改变媒体对计划发射的报道方式,但发射本身不会早晚发生。因此,对于火箭发射等,我们将使用上面讨论的三个面向时刻的课程之一,几乎可以肯定Instant
课程只关注 UTC 并完全避开所有时区。
这些概念和类都已经在 Stack Overflow 上多次讨论过。搜索以了解更多信息。