繁体   English   中英

java.time 比较不一致

[英]java.time Comparison Is Not Consistent

我在我的代码中使用LocalData对象,并且我注意到使用compareTo方法不一致。
我使用compareTo方法来获得两个日期之间的差异(以天为单位)。
但是,它似乎只适用于同一月份的日期。

我附上一个代码片段来演示这个问题:

import java.text.MessageFormat;
import java.time.LocalDate;

public class timeComparison {
    public static void main(String[] args) {
        LocalDate valentinesDay = LocalDate.of(2020, 2, 14);
        LocalDate darwinDay = LocalDate.of(2020, 2, 12);
        LocalDate leapDay = LocalDate.of(2020, 2, 29);
        LocalDate superTuesday = LocalDate.of(2020, 3, 3);

        String valentinesVsDarwin = MessageFormat.format(
                "Valentine day is {0} days after Darwin day",
                valentinesDay.compareTo(darwinDay)
        );
        System.out.println(valentinesVsDarwin);

        String darwinVsLeap = MessageFormat.format(
                "Leap day is {0} days after Darwin day",
                leapDay.compareTo(darwinDay)
        );
        System.out.println(darwinVsLeap);

        String superTuesdayVsLeap = MessageFormat.format(
                "Super Tuesday is {0} days after leap day",
                superTuesday.compareTo(leapDay)
        );
        System.out.println(superTuesdayVsLeap);
    }
}

我得到的输出是:

Valentine day is 2 days after Darwin day
Leap day is 17 days after Darwin day
Super Tuesday is 1 days after leap day

我期待Super Tuesday is 3 days after leap day获得Super Tuesday is 3 days after leap day

我想知道是什么导致了问题,以及如何获得两个不同日期之间的差异。

TL; 博士

compareTo方法不适用于此用途。
它旨在指示顺序,而不是显示时差。

解开谜团

为了更好地理解这一点,可以查看LocalDate的源代码。
在任何 IDE 上,您都可以选择“Go To => Implementation”来查看源代码。

public int compareTo(ChronoLocalDate other) {
    return other instanceof LocalDate ? this.compareTo0((LocalDate)other) : super.compareTo(other);
}

int compareTo0(LocalDate otherDate) {
    int cmp = this.year - otherDate.year;
    if (cmp == 0) {
        cmp = this.month - otherDate.month;
        if (cmp == 0) {
            cmp = this.day - otherDate.day;
        }
    }

    return cmp;
}

从上面的源代码中,您可以了解到compareTo方法返回以天为单位的日期之间的差异,而是返回第一个具有差异的参数中的日期之间的差异(可能是年,可能是月,也可能是是天)。

compareTo方法的目的是什么

为了理解上面的代码及其目的,
人们需要稍微了解一下 Java 中的比较是如何工作的。
Java 的LocalDate类实现ChronoLocalDate ,它是一个可比较的对象。
这意味着这个对象能够将自己与另一个对象进行比较。
为此,类本身必须实现java.lang.Comparable接口来比较其实例。
Java 的 API 文档中所述

该接口对实现它的每个类的对象强加了总排序。 这种排序称为类的自然排序,类的 compareTo 方法称为其自然比较方法。

此外, compareTo方法的方法细节如下:

将此对象与指定的对象进行比较以进行排序。
当此对象小于、等于或大于指定对象时,返回一个负整数、零或正整数。

意思是,为了使比较工作,我们需要返回一个与上述规则相对应的整数。 因此,数字本身没有任何意义,而不是表示对象的“顺序”。 这就是为什么如上所示的方法不计算天数的差异,而是寻求物体自然顺序的直接指示:首先以年为单位,然后以月为单位,最后以天为单位。
当您比较具有不同装载量的日期时 - 该方法显示了这一点。
如果您要在 2020 年的日期和 2018 年的日期之间进行比较,同样适用 - 结果是 2 / -2。

您应该如何衡量数据差异

我们发现compareTo并不打算用作日期差异的测量工具。
您可以根据需要使用PeriodChronoUnit
来自 Java 的Period and Duration 教程

计时单元

The Temporal Package 中讨论的ChronoUnit枚举定义了用于测量时间的单位。
当您只想以单个时间单位(例如天或秒)测量时间量时, ChronoUnit.between方法很有用。
between 方法适用于所有基于时间的对象,但它仅以单个单位返回数量。

时期

要使用基于日期的值(年、月、日)定义时间量,请使用Period类。
Period类提供了各种获取方法,例如getMonthsgetDaysgetYears ,以便您可以从周期中提取时间量。
总时间段由所有三个单位共同表示:月、日和年。
要显示以单个时间单位(例如天)测量的时间量,您可以使用ChronoUnit.between方法。

这是一个代码片段,演示了上述方法的用法:

jshell> import java.time.LocalDate;

jshell> LocalDate darwinDay = LocalDate.of(2020, 2, 12);
darwinDay ==> 2020-02-12

jshell> LocalDate leapDay = LocalDate.of(2020, 2, 29);
leapDay ==> 2020-02-29

jshell> leapDay.compareTo(darwinDay);
 ==> 17

jshell> import java.time.Period;

jshell> Period.between(darwinDay, leapDay).getDays();
 ==> 17

jshell> import java.time.temporal.ChronoUnit;

jshell> ChronoUnit.DAYS.between(darwinDay, leapDay);
 ==> 17

jshell> LocalDate now = LocalDate.of(2020, 3, 28);
now ==> 2020-03-28

jshell> LocalDate yearAgo = LocalDate.of(2019, 3, 28);
yearAgo ==> 2019-03-28

jshell> yearAgo.compareTo(now);
 ==> -1

jshell> Period.between(yearAgo, now).getDays();
 ==> 0

jshell> ChronoUnit.DAYS.between(yearAgo, now);
 ==> 366

我期待在闰日后 3 天获得超级星期二。

这是一个错误的期望。 看看 Java 如何比较LocalDate两个实例:

int compareTo0(LocalDate otherDate) {
    int cmp = (year - otherDate.year);
    if (cmp == 0) {
        cmp = (month - otherDate.month);
        if (cmp == 0) {
            cmp = (day - otherDate.day);
        }
    }
    return cmp;
}

由于年份相同(2020 年),它为您提供了月份之间的差异,即3 - 2 = 1

您应该使用java.time.temporal.ChronoUnit::between来查找LocalDate两个实例之间的天数。

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        System.out.println(ChronoUnit.DAYS.between(LocalDate.of(2020, 2, 29), LocalDate.of(2020, 4, 3)));
    }
}

输出:

34

暂无
暂无

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

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