我正在比较两个似乎相等的日期,但它们包含的区域名称不同:一个是Etc/UTC,另一个是UTC

根据这个问题:Is there a difference between the UTC and Etc/UTC time zones?-这两个区域是相同的。但是我的测试失败了:

import org.junit.Test;
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import static org.junit.Assert.assertEquals;

public class TestZoneDateTime {

    @Test
    public void compareEtcUtcWithUtc() {
        ZonedDateTime now = ZonedDateTime.now();
        ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(ZoneId.of("Etc/UTC"));
        ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(ZoneId.of("UTC"));

        // This is okay
        assertEquals(Timestamp.from(zoneDateTimeEtcUtc.toInstant()), Timestamp.from(zoneDateTimeUtc.toInstant()));
        // This one fails
        assertEquals(zoneDateTimeEtcUtc,zoneDateTimeUtc);

        // This fails as well (of course previous line should be commented!)
        assertEquals(0, zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc));
    }
}

结果:
java.lang.AssertionError:
Expected :2018-01-26T13:55:57.087Z[Etc/UTC]
Actual   :2018-01-26T13:55:57.087Z[UTC]

更具体地说,我希望ZoneId.of("UTC")等于ZoneId.of("Etc/UTC"),但它们不是!

作为@NicolasHenneaux suggested,我可能应该使用compareTo(...)方法。那是个好主意,但是zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc)返回-16值,因为ZoneDateTime内有此实现:
cmp = getZone().getId().compareTo(other.getZone().getId());

断言结果:
java.lang.AssertionError:
Expected :0
Actual   :-16

因此,问题出在ZoneId实现中。但是我仍然希望,如果两个区域ID均有效且都指定相同的区域,则它们应该相等。

我的问题是:这是库错误,还是我做错了什么?

更新

几个人试图说服我这是正常的行为,比较方法的实现使用StringZoneId id表示形式是正常的。在这种情况下,我应该问,为什么以下测试可以正常进行?
    @Test
    public void compareUtc0WithUtc() {
        ZonedDateTime now = ZonedDateTime.now();
        ZoneId utcZone = ZoneId.of("UTC");
        ZonedDateTime zonedDateTimeUtc = now.withZoneSameInstant(utcZone);
        ZoneId utc0Zone = ZoneId.of("UTC+0");
        ZonedDateTime zonedDateTimeUtc0 = now.withZoneSameInstant(utc0Zone);

        // This is okay
        assertEquals(Timestamp.from(zonedDateTimeUtc.toInstant()), Timestamp.from(zonedDateTimeUtc0.toInstant()));
        assertEquals(0, zonedDateTimeUtc.compareTo(zonedDateTimeUtc0));
        assertEquals(zonedDateTimeUtc,zonedDateTimeUtc0);
    }

如果Etc/UTC UTC相同,那么我看到两个选项:
  • compareTo/equals方法不应使用ZoneId id,而应比较其规则
  • Zone.of(...)损坏,应将Etc/UTCUTC视为相同的时区。

  • 否则我看不出为什么UTC+0UTC可以正常工作。

    UPDATE-2 我已经报告了一个ID为9052414的错误。将看到Oracle团队将决定什么。

    UPDATE-3 已接受此错误报告(不知道他们是否会因为“无法修复”而将其关闭):https://bugs.openjdk.java.net/browse/JDK-8196398

    最佳答案

    您可以将ZonedDateTime对象转换为Instant,如其他答案/评论所述。
    ZonedDateTime::isEqual
    或者,您可以使用 isEqual method,它比较两个ZonedDateTime实例是否对应于相同的Instant:

    ZonedDateTime now = ZonedDateTime.now();
    ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(ZoneId.of("Etc/UTC"));
    ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(ZoneId.of("UTC"));
    
    Assert.assertTrue(zoneDateTimeEtcUtc.isEqual(zoneDateTimeUtc));
    

    关于java - ZonedDateTime比较: expected: [Etc/UTC] but was: [UTC],我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48462758/

    10-14 12:26
    查看更多