简体   繁体   English

java 8 - ZonedDateTime 不等于另一个 ZonedDateTime

[英]java 8 - ZonedDateTime not equal another ZonedDateTime

I created two ZonedDateTime objects and I think they are should be equal:我创建了两个 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);
}

In debugger I see that class of member of ZonedDateTime zone in first case is java.time.ZoneOffset and in second java.time.ZoneRegion and this is makes ZonedDateTime objects not equal.在调试器中,我看到 ZonedDateTime zone的成员类在第一种情况下是java.time.ZoneOffset ,在第二个 java.time.ZoneRegion 中,这使得 ZonedDateTime 对象不相等。 This is confusing... Any ideas?这令人困惑......有任何想法吗?

You are checking for object equality which evaluates to false as these objects are not equivalent.您正在检查评估为false对象相等性,因为这些对象不相等。 One is bound to a ZoneId , the other to a ZoneOffset .一个绑定到ZoneId ,另一个绑定到ZoneOffset If you want to check whether they represent the same time, you can use the not very intuitively named method isEqual .如果你想检查它们是否代表同一时间,你可以使用不太直观的命名方法isEqual

Eg:例如:

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));

prints:印刷:

isEqual:true
equals: false

Btw, note that you don't need to use Objects.equals(a,b) for two objects you already know to be non- null .顺便说一句,请注意,对于已经知道为非null两个对象,您不需要使用Objects.equals(a,b) You can invoke a.equals(b) directly.您可以直接调用a.equals(b)

This played hell on me for hours too when using Jackson to serialize / deserialize instances of ZonedDateTime and then compare them against each other for equality to verify that my code was working correctly.当使用 Jackson 序列化/反序列化 ZonedDateTime 的实例,然后将它们相互比较以验证我的代码是否正常工作时,这也让我困扰了几个小时。 I don't fully understand the implications but all I've learned is to use isEqual instead of equals.我不完全理解其中的含义,但我所学到的只是使用 isEqual 而不是 equals。 But this throws a big wrench in testing plans as most assertion utilities will just call the standard .equals().但这会给测试计划带来很大的麻烦,因为大多数断言实用程序只会调用标准的 .equals()。

Here's what I finally came up with after struggling for quite some time:这是我在挣扎了一段时间后终于想出的:

@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");
}

The equals() method on ZonedDateTime requires that all component parts of the object are equal. ZonedDateTime上的equals()方法要求对象的所有组成部分都相等。 Since a ZoneOffset is not equal to a ZoneRegion (even though both are subclasses of ZoneId ), the method returns false.由于ZoneOffset不等于ZoneRegion (即使两者都是ZoneId子类),该方法返回 false。 Read about VALJOs to understand more as to why value types are compared in this way.阅读VALJO以了解更多关于为什么以这种方式比较值类型的信息。

The isEqual method only compares the instant on the time-line, which may or may not be what you want. isEqual方法只比较时间线上的瞬间,这可能是您想要的,也可能不是。 You can also use the timeLineOrder() method to compare two ZoneDateTime only using the time-line.您还可以使用timeLineOrder() 方法仅使用时间线来比较两个ZoneDateTime

This caused us pain for a while too.这也让我们痛苦了一阵子。 The ZonedDateTime values actually represent identical times, however their zone "types" are different, which is why they are not equal. ZonedDateTime值实际上表示相同的时间,但是它们的区域“类型”不同,这就是它们不相等的原因。

The above answers are correct, however I thought I'd add a visual that might further help:上面的答案是正确的,但是我想我会添加一个可能会进一步帮助的视觉效果:

在此处输入图片说明

In the ZonedDateTime code, we find, which shows the zone comparison:ZonedDateTime代码中,我们发现,其中显示了区域比较:

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM