ZonedDateTime比较:预期:[Etc/UTC]但是:[UTC]

And*_*niy 13 java datetime date datetime-comparison zoneddatetime

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

根据这个问题:UTC和Etc/UTC时区之间有区别吗? - 这两个区域是相同的.但我的测试失败了:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

java.lang.AssertionError: 
Expected :2018-01-26T13:55:57.087Z[Etc/UTC]
Actual   :2018-01-26T13:55:57.087Z[UTC]
Run Code Online (Sandbox Code Playgroud)

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

正如@NicolasHenneaux 所说,我应该使用compareTo(...)方法.这是个好主意,但zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc)返回-16值,因为这里面的实现ZoneDateTime:

cmp = getZone().getId().compareTo(other.getZone().getId());
Run Code Online (Sandbox Code Playgroud)

断言结果:

java.lang.AssertionError: 
Expected :0
Actual   :-16
Run Code Online (Sandbox Code Playgroud)

所以问题出在ZoneId实施的某个地方.但我仍然希望如果两个区域ID都有效并且两者都指定相同的区域,那么它们应该是相同的.

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

UPDATE

有几个人试图说服我这是一种正常行为,比较方法的实现使用id表示是正常StringZoneId.在这种情况下,我应该问,为什么以下测试运行正常?

    @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);
    }
Run Code Online (Sandbox Code Playgroud)

如果Etc/UTC 相同的UTC,那么我看到两个选项:

  • compareTo/equals方法不应该使用ZoneId id,而应该比较它们的规则
  • Zone.of(...)是破碎的,应该对待Etc/UTCUTC作为相同的时区.

否则我不明白为什么UTC+0并且UTC工作正常.

UPDATE-2我报告了一个错误,ID:9052414.将看看Oracle团队将决定什么.

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

小智 9

您可以将ZonedDateTime对象转换Instant为已经告知的其他答案/评论.

ZonedDateTime::isEqual

或者您可以使用该isEqual方法,该方法比较两个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));
Run Code Online (Sandbox Code Playgroud)