簡體   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