16

我创建了两个 ZonedDateTime 对象,我认为它们应该相等:

public static void main(String[] args) {
    ZoneId zid = ZoneId.of("America/New_York");
    ZoneOffset offset = ZoneOffset.from(LocalDateTime.now().atZone(zid));
    ZonedDateTime zdt0 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, offset);
    ZonedDateTime zdt1 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, zid);
    boolean equals = Objects.equals(zdt0, zdt1);
    System.out.println("equals: " + equals);
}

在调试器中,我看到 ZonedDateTime区域的成员类在第一种情况下是java.time.ZoneOffset,在第二种情况下是 java.time.ZoneRegion ,这使得 ZonedDateTime 对象不相等。这令人困惑......有什么想法吗?

4

4 回答 4

30

您正在检查评估为的对象相等性,false因为这些对象不相等。一个绑定到 a ZoneId,另一个绑定到 a ZoneOffset。如果要检查它们是否代表相同的时间,可以使用命名不是很直观的方法isEqual

例如:

ZoneId zid = ZoneId.of("America/New_York");
ZoneOffset offset = ZoneOffset.from(LocalDateTime.now().atZone(zid));
ZonedDateTime zdt0 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, offset);
ZonedDateTime zdt1 = ZonedDateTime.of(2014, 8, 24, 21, 10, 1, 777000002, zid);
System.out.println("isEqual:" + zdt0.isEqual(zdt1));
System.out.println("equals: " + zdt0.equals(zdt1));

印刷:

isEqual:true
equals: false

顺便说一句,请注意,您不需要使用Objects.equals(a,b)您已经知道是非的两个对象null。可以a.equals(b)直接调用。

于 2014-09-11T18:09:12.157 回答
8

当使用 Jackson 序列化/反序列化 ZonedDateTime 的实例,然后将它们相互比较以验证我的代码是否正常工作时,这对我也有好几个小时的影响。我不完全理解其中的含义,但我所学到的只是使用 isEqual 而不是 equals。但这会给测试计划带来很大的麻烦,因为大多数断言实用程序只会调用标准的 .equals()。

这是我在挣扎了一段时间后终于想到的:

@Test
public void zonedDateTimeCorrectlyRestoresItself() {

    // construct a new instance of ZonedDateTime
    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Z"));
    // offset = {ZoneOffset@3820} "Z"
    // zone   = {ZoneOffset@3820} "Z"

    String starting = now.toString();

    // restore an instance of ZonedDateTime from String
    ZonedDateTime restored = ZonedDateTime.parse(starting);
    // offset = {ZoneOffset@3820} "Z"
    // zone   = {ZoneOffset@3820} "Z"

    assertThat(now).isEqualTo(restored); // ALWAYS succeeds

    System.out.println("test");
}

@Test
public void jacksonIncorrectlyRestoresZonedDateTime() throws Exception {

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.findAndRegisterModules();

    // construct a new instance of ZonedDateTime
    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Z"));
    // offset = {ZoneOffset@3820} "Z"
    // zone   = {ZoneOffset@3820} "Z"


    String converted = objectMapper.writeValueAsString(now);

    // restore an instance of ZonedDateTime from String
    ZonedDateTime restored = objectMapper.readValue(converted, ZonedDateTime.class);
    // offset = {ZoneOffset@3820} "Z"
    // zone   = {ZoneOffset@3821} "UTC"

    assertThat(now).isEqualTo(restored); // NEVER succeeds

    System.out.println("break point me");
}
于 2017-05-18T22:21:25.333 回答
2

equals()on 方法要求对象的ZonedDateTime所有组成部分相等。由于 aZoneOffset不等于 a ZoneRegion(即使两者都是 的子类ZoneId),该方法返回 false。阅读有关VALJO的信息,以更多地了解为什么以这种方式比较值类型。

isEqual方法仅比较时间线上的瞬间,这可能是也可能不是您想要的。您也可以使用该timeLineOrder() 方法ZoneDateTime仅使用时间线来比较两者。

于 2014-09-11T18:59:14.377 回答
0

这也让我们痛苦了一阵子。这些ZonedDateTime值实际上代表相同的时间,但是它们的区域“类型”不同,这就是它们不相等的原因。

上面的答案是正确的,但是我想我会添加一个可能会进一步帮助的视觉效果:

在此处输入图像描述

ZonedDateTime代码中,我们发现,它显示了区域比较:

在此处输入图像描述

于 2021-03-03T15:39:25.083 回答