UTC(ZoneId)和(ZoneOffSet)有什么区别

What is the difference between UTC (ZoneId) and (ZoneOffSet)

提问人:M. A. Tanaka 提问时间:11/4/2023 最后编辑:user85421M. A. Tanaka 更新时间:11/10/2023 访问量:110

问:

出于好奇,有没有人知道 Java 中 UTC 作为 ZoneId 和 ZoneOffset 之间的区别。

我有点惊讶,在比较 zoneid 和 zoneoffset 的 utc 时,情况并非如此

我试图用 spock 编写这个测试,我唯一能找到的是区域不同。

有没有人建议何时使用哪一个?

从示例中不清楚,但我实际上使用了 spock/groovy,其中 == 对应于 java 中的 equals 方法,因此对此表示歉意

但是无论我是否编写 lhs.equals(rhs),它仍然会返回 false

LocalDateTime now = LocalDateTime.now() // 2023-11-03T19:42:41.772234517
ZonedDateTime lhs = now.atZone(ZoneOffset.UTC) 
String lhsString = lhs.toString() // 2023-11-03T19:42:41.772234517Z
ZonedDateTime rhs = now.atZone(ZoneId.of('UTC'))
String rhsString = rhs.toString()  // 2023-11-03T19:42:41.772234517Z[UTC]
Duration duration = Duration.between(lhs, rhs)  // PT0S
boolean equal = lhs == rhs   // this is false
java groovy spock UTC zoneddatetime

评论

2赞 user85421 11/4/2023
顺便说一句,比较引用类型(对象)通常(几乎总是)是一个错误......它只是比较参考,而不是内容 - 我希望甚至会返回,尽管==now.atZone(ZoneOffset.UTC) == now.atZone(ZoneOffset.UTC)falsenow.atZone(ZoneOffset.UTC).equals( now.atZone(ZoneOffset.UTC) )true
1赞 user85421 11/4/2023
(甚至在文档中:“该方法应该用于比较。ZonedDateTimeequals
0赞 Sören 11/4/2023
如果你使用的是 Spock,这是 Groovy 而不是 Java,对吧?
0赞 M. A. Tanaka 11/5/2023
@sören你是对的,这很可能是用时髦写的,这就是我得到否定回应的原因。但是 groovy 中的 == 与 java 中的 equals 方法相同
0赞 M. A. Tanaka 11/5/2023
@user85421不清楚代码是用 Groovy 编写的,因此 == 被认为与 Java 中的 equals 相同。

答:

1赞 Robert 11/4/2023 #1

ZoneId 文档

ZoneId 用于标识用于在 Instant 和 LocalDateTime 之间进行转换的规则。

ZoneOffset 文档

世界不同地区有不同的时区偏移量。ZoneId 类中捕获了偏移量如何随地点和时间而变化的规则。

例如,巴黎在冬季比格林威治/UTC 早一小时,在夏季比格林威治早两小时。Paris 的 ZoneId 实例将引用两个 ZoneOffset 实例 - 一个用于冬季的 +01:00 实例,一个用于夏季的 +02:00 实例。

因此,如果该地区使用夏令时,则区别在于夏令时与冬令时(夏令时)。同一时区中可能有不同的州或国家,例如,墨西哥城和芝加哥位于同一区域偏移量 (GMT-5),但墨西哥在 2023 年春季停止在夏令时和冬令时之间切换,因此在夏季他们休息一个小时,在冬季他们会再次匹配。

因此,如果要显示或转换为本地时间,请始终使用 ZoneId,它考虑了位置(州)和当地夏令时规则。

评论

0赞 Sören 11/4/2023
@JimGarrison 请注意,OP 使用的是 Groovy,其中 == 确实调用了 .equals()
0赞 M. A. Tanaka 11/5/2023
@robert,但 UTC 作为一个时区,那是什么,在这种情况下,它在夏令时方面有什么样的政治,以及哪些国家/地区在内。真正的问题是,我正在一个 20+ 人的项目中运行,人们有时选择了时区,而其他时间选择了偏移量,导致即使他们 IMO 也应该平等,他们不是。
0赞 Robert 11/5/2023
@M.A.Tanaka:出于这个原因,我建议在UTC的日志记录中始终使用时间戳。这使得从不同位置进行比较/讨论变得更加容易。UTC 只是位置,没有夏令时、政治或其他任何东西。UTC 是协调世界时。
0赞 kriegaex 11/9/2023
OTOH,Z 只是相当于 UTC 的军事等价物,那里的方法应该更精确一些。在 JDK 中进行调试时,最终归结为两个区域 ID 的比较,以及计算结果为 .JDK 本身在不同的上下文中使用两者,因此它也应该知道它们是等价的。equals"UTC".equals("Z")false
0赞 M. A. Tanaka 11/10/2023
谢谢,这就是我一直在寻找的答案
2赞 Reilas 11/4/2023 #2

在这种情况下,双等相等运算符将简单地比较对象的实例

由于 lhsrhs 被分配了不同的实例,因此这些值将返回 false

通常,Object#equals 方法将返回内容是否相等。

boolean equal = lhs.equals(rhs)

如果没有,请实现您自己的比较方法。

评论

1赞 Sören 11/4/2023
请注意,OP 使用的是 Groovy,其中 == 确实调用了 .equals()
1赞 M. A. Tanaka 11/5/2023
如果它是 java,你是对的(但我不清楚。对此表示歉意)。
2赞 kriegaex 11/9/2023 #3

引用我自己的评论:

Z 只是 UTC 的军事等价物,那里的方法应该更精确一些。在 JDK 中进行调试时,最终归结为两个区域 ID 的比较,以及计算结果为 .JDK 本身在不同的上下文中使用两者,因此它也应该知道它们是等价的。equals"UTC".equals("Z")false

话虽如此,您的解决方法是使用:ZoneId.normalized()

package de.scrum_master.stackoverflow.q77419050

import spock.lang.Specification

import java.time.Duration
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.temporal.ChronoUnit

class TimeZoneTest extends Specification {
  def test() {
    given:
    def now = LocalDateTime.now()
    
    when:
    def lhs = now.atZone(ZoneOffset.UTC.normalized())
    def rhs = now.atZone(ZoneId.of('UTC').normalized())
    def timeDifference = Duration.between(lhs, rhs).get(ChronoUnit.SECONDS)

    then:
    lhs == rhs
    timeDifference == 0
  }
}

在 Groovy Web 控制台中尝试一下,

更多细节可以在这个答案中找到。

评论

0赞 kriegaex 11/10/2023
请注意我的更新,链接到一个更详细的 Groovy Web 控制台示例。
0赞 M. A. Tanaka 11/10/2023
谢谢,这就是我一直在寻找的答案。也许是 OT 的事情,但我无法弄清楚任何用例,在这些用例中,将它们视为平等是有意义的。这有点像说 0x10 与 16 不同,因为它们是以十六进制和十进制格式编写的。恕我直言。但我想一个人也接受了语言中存在的那些怪癖。