Java - 比较两个 ZonedDateTime 时的结果与预期不符

Posted

技术标签:

【中文标题】Java - 比较两个 ZonedDateTime 时的结果与预期不符【英文标题】:Java - Result when compare two ZonedDateTime is not as expected 【发布时间】:2018-09-16 02:19:55 【问题描述】:

我有这个代码:

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T10:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
System.out.println(t1.compareTo(t2));

结果是-1。我猜它会将 t1 和 t2 都转换为相同的时区,然后进行比较。例如,在第一种情况下,它会(可能)比较

t1 = 2018-04-06T07:01:00.000+00:00 to t2 = 2018-04-06T13:01:00.000+00:00

这个case按我的预期返回1

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T17:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
System.out.println(t1.compareTo(t2));

但是这个 (*) 不返回 0,而是返回 1:

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
System.out.println(t1.compareTo(t2));

我什至尝试在解析字符串后使用truncatedTo(ChronoUnit.MINUTES) 来忽略秒部分,但没有任何改变。

最感兴趣的是如果我将 t1 / t2 减少/增加一秒,结果将是 -1

例如:

ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:01.000-03:00");
System.out.println(t1.compareTo(t2));

在我的用例中,所有用户输入时间都将以上述格式保存在许多 ZoneOffset 中,并且我有一些查询需要将其与服务器时间进行比较(忽略时间的秒数)。在上面的情况下(*),我怎样才能得到 t1 等于 t2?

【问题讨论】:

是的,我可能理解第一种情况。我不明白的情况是当我将2018-04-06T16:01:00+03:002018-04-06T10:01:00-03:00 进行比较时,它返回 1,在这种情况下它们是相等的。 【参考方案1】:

比较首先基于即时,然后基于本地 日期时间,然后是区域 ID,然后是年表。它是 “符合等于”,由Comparable 定义。

compareTo是在超类ChronoZonedDateTime中实现的,这个类我们通常不关心,但是我们需要在其中查找一些方法的文档。所以上面的引用来自那里。

所以在两个 ZonedDateTime 对象表示同一时刻的情况下,t1 的本地时间是 16:01,大于 t2 的 10:01。所以 1 是正确和预期的结果。另一方面,如果瞬间仅相隔 1 秒(或仅 1 纳秒),则优先,并且不比较本地时间。

我相信这可以解释您观察到的行为。

这句话还暗示了为什么会这样:假设比较两个具有相同时刻但不同时区的ZonedDateTime对象返回0。那么比较将不再与equals一致。这被认为是一个非常好的属性(尽管不是必需的)。

C 的自然顺序据说与一致 等于 当且仅当e1.compareTo(e2) == 0 具有相同的布尔值 对于 C 类的每个 e1e2,值为 e1.equals(e2)

(引自Comparable interface documentation)

因此,如果您只想比较瞬间,则有两种选择,具体取决于您喜欢哪种类型的结果:

    使用isBeforeisAfterisEqual

    ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
    ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
    System.out.println(t1.isBefore(t2));
    System.out.println(t1.isAfter(t2));
    System.out.println(t1.isEqual(t2));
    

    输出:

false
false
true
    使用the answer by Yoav Gur:明确比较瞬间。

【讨论】:

那么,在这种情况下我唯一的选择就是比较他们的瞬间,不是吗? 不是唯一的选择,而是一种选择。请参阅我的编辑。 @hai_uit 在我的用例中,比较两个瞬间更合适,因为我正在编写一个比较器进行排序,所以我需要比较整数的结果。【参考方案2】:

使用toInstant(),然后比较结果,如下所示:

    ZonedDateTime t1 = ZonedDateTime.parse("2018-04-06T16:01:00.000+03:00");
    ZonedDateTime t2 = ZonedDateTime.parse("2018-04-06T10:01:00.000-03:00");
    System.out.println(t1.toInstant().compareTo(t2.toInstant())); 

【讨论】:

我很抱歉,我已经用赞成票代替了我的反对票。我的评论对于 hai_uit 问题的第一部分是正确的 - 但我没有意识到 hai_uit 对问题的其他部分更感兴趣。

以上是关于Java - 比较两个 ZonedDateTime 时的结果与预期不符的主要内容,如果未能解决你的问题,请参考以下文章

《日期与时间》第7节:ZonedDateTime与OffsetDateTime

无法使用Java 8中的DateTimeFormatter和ZonedDateTime从TemporalAccessor获取ZonedDateTime

Java 8:如何从 Epoch 值创建 ZonedDateTime?

Java 8 Time API - ZonedDateTime - 解析时指定默认 ZoneId

Java ZonedDateTime类怎么转化成时间戳

将 java.sql.Timestamp 转换为 Java 8 ZonedDateTime?