如何在Java 8中比较两个java.time.Period?

11

我该如何在Java 8中比较两个时间段(Period)?

例如:

Period one = Period.of(10,0,0);
Period two = Period.of(8,0,0);

在这种情况下,一个比两个大。


12
月份不能进行比较,这是有原因的。例如:一个月的周期和30天的周期哪个更长?答案是取决于情况。你可以将这两个周期加到给定的“LocalDate”上,看哪一个“LocalDate”更大。但结果可能取决于从哪个日期开始算起。 - JB Nizet
1
@JBNizet的评论完全正确,Period不可比较,也不应该比较。然而,JSR-310的设计者们允许/定义了一个方法isNegative(),并使用疯狂的定义和实现来规避“P1M-30D”的问题。非常糟糕。 - Meno Hochschild
6个回答

6

确实,在一般情况下,由于月份的标准长度未定义,比较两个Period对象是没有意义的。

然而,在许多情况下,您可以使用类似以下实现的实现。合同将类似于compareTo()的合同:

public int comparePeriodsApproximately(Period p1, Period p2) {
    return period2Days(p1) - period2Days(p2);
}

private int period2Days(Period p) {
    if (p == null) {
        return 0;
    }
    return (p.getYears() * 12 + p.getMonths()) * 30 + p.getDays();
}

Kotlin-Extension 版本:private fun Period.toDayEstimate():Int = years * 12 + months * 30 + days - m.reiter

3

JB Nizet说得很对。根据Java文档中Period类的说明,有一个类与Period(Duration)类似,可以根据您的业务需求使用它,因此您不能比较Periods。

"在添加到ZonedDateTime时,持续时间和周期在处理夏令时方面有所不同。持续时间将添加精确的秒数,因此一天的持续时间始终精确为24小时。相比之下,期间将添加一个概念上的日,试图保持本地时间。"

Period period = Period.of(10, 0, 0);
Period period2 = Period.of(10, 0, 0);

// No compareTo method in period
System.out.println(period.compareTo(period2));

Duration duration = Duration.ofDays(3);
Duration duration2 = Duration.ofDays(3);

// Can compare durations as it gives you the exact time
System.out.println(duration.compareTo(duration2));

1
如果您有一个 和一个 的时间段,可以按照以下方式操作:
Period periodOfMonths = Period.ofMonths(16);
Period periodOfYears = Period.ofYears(1);

// Check if period of months is ghreather that the period of years
System.out.println(periodOfMonths.toTotalMonths() > periodOfYears.toTotalMonths()); 

toTotalMonths 不考虑“天数”。 - Siarhei

0
我喜欢Martin Cassidy的答案,但建议使用硬编码的LocalDate而不是LocalDate.now,例如LocalDate.MIN。
public static int compareTo(final Period first, final Period second)
{
    return LocalDate.MIN.plus(first).compareTo(LocalDate.MIN.plus(second));
}

如果其中任何一个时间段可能为负数,这将导致下溢错误。例如,您可以尝试使用LocalDate.EPOCH。除此之外,该方法将生成结果,这些结果可能很难理解和预测,但是这些结果是可重现的。 - Ole V.V.

0

这个应该是无懈可击的。是否有用呢?

我声称,从2097年2月1日开始向前计算,你可以得到给定年份和月份中最少的天数,因为首先要计算二月(28天),直到连续两个月都有31天(七月和八月)需要很长时间,而且要更长时间才能遇到闰年(2104年)。另外,2200年和2300年都不是闰年,如果计算到那时,也不会出现闰年。相反,当从同一日期向后计算时,你将得到最大可能的天数。你首先要计算通过31天的2个月(1月和12月)。你遇到的第一个二月是在2096年,是一个闰年,所以有29天。而2000年是一个闰年。

所以我的诀窍是从提到的日期两个时期的差异进行双向计数。如果计数一致,差异为正(或负),那么无论我们从哪一天开始计数,两个(或一个)总是更长的。

在某些情况下,计数可能不一致。那么我就拒绝称之为更长的时期。

    Period one = Period.of(10, 0, 0);
    Period two = Period.of(8, 0, 0);
    
    if (one.normalized().equals(two.normalized())) {
        System.out.println("They are the same.");
    } else {
        Period diff = two.minus(one);
        
        LocalDate anchor = LocalDate.of(2097, Month.FEBRUARY, 1);
        
        LocalDate plusDiff = anchor.plus(diff);
        LocalDate minusDiff = anchor.minus(diff);
        if (plusDiff.isAfter(anchor) && minusDiff.isBefore(anchor)) {
            System.out.println("Two is greater (unambiguously)");
        } else if (plusDiff.isBefore(anchor) && minusDiff.isAfter(anchor)) {
            System.out.println("One is greater (unambiguously)");
        } else {
            System.out.println("Cannot decide");
        }
    }

在这种情况下的输出(使用问题中的句点):

一个更大(明确无误)

我在第一次比较中使用的normalized方法将年份和月份之间进行转换,确保月份在-11到+11的区间内,并且与年份具有相同的符号(除非年份为0)。由于一年总是有12个月,因此可以明确无误地完成此操作。

您可以将其细化为报告一个期间长于或等于另一个期间。例如,如果one是一个月,而two是31天,则我们知道two至少与one一样长,尽管我们无法确定它是否也严格更长。


-1

可以通过借鉴LocalDate的方法,使用适当的compareTo()方法来实现该功能。

public static int compareTo(final Period first, final Period second)
{
    final LocalDate now = LocalDate.now();
    return now.plus(first).compareTo(now.plus(second));
}

1
请注意,对于某些时间段,此方法在不同的日期会给出不同的结果。我认为这是一种令人不愉快和意外的行为。有些人会说这是有缺陷的。 - Ole V.V.
1
一种基于当前日期给出不同结果的方法应该被点踩。 - Honza Zidek
如果“first”周期为30天,而“second”周期为一个月,那么代码将根据“now”日期给出不同的结果。 - Ilya Serbis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接