java 8 - ZonedDateTime不等于另一个ZonedDateTime

时间:2014-09-11 17:28:12

标签: java java-8 java-time

我创建了两个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 个答案:

答案 0 :(得分:19)

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

E.g:

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)

答案 1 :(得分:2)

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

isEqual方法仅比较时间线上的时刻,可能是您想要的也可能不是。您还可以使用timeLineOrder() method仅使用时间线比较两个ZoneDateTime

答案 2 :(得分:1)

当使用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");
}

答案 3 :(得分:0)

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

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

enter image description here

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

enter image description here