How to compare two java.time.Period in java 8?

18.5k views Asked by At

How do I compare two Periods in java 8?

E.g.

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

here in this case one is greater than two.

6

There are 6 answers

0
Rohit Gulati On

Rightly said by JB Nizet. You cannot compare Periods, as per java doc in Period class there is similar class to Period (Duration) available in java, you can use that depends on your business requirement.

"Durations and periods differ in their treatment of daylight savings time when added to ZonedDateTime. A Duration will add an exact number of seconds, thus a duration of one day is always exactly 24 hours. By contrast, a Period will add a conceptual day, trying to maintain the local time."

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
Honza Zidek On

It is true that the comparison of two Period objects does not make sense in a general case, due to the undefined standard length of a month.

However, in many situations you can quite well live with an implementation similar to that which follows. The contract will be similar to the contract of 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();
}
1
gRohleder On

i like the answer of Martin Cassidy, but propose to use a hard coded LocalDate instead of LocalDate.now, for example LocalDate.MIN

public static int compareTo(final Period first, final Period second)
{
    return LocalDate.MIN.plus(first).compareTo(LocalDate.MIN.plus(second));
}
0
Anonymous On

This one should be bullet-proof. Whether it’s useful?

I claim that you get the smallest possible amount of days from a given number of years and months by counting forward from February 1, 2097 since you are first counting February (28 days) and it takes a long time until you get two months with 31 days in a row (July and August) and still longer until you hit a leap year (2104). Also 2200 and 2300 are non-leap years, should the counting reach that far. Conversely you get the greatest possible number of days when counting backward from the same date. You start by counting through 2 months @ 31 days (January and December). The first February you encounter is in 2096, a leap year, so 29 days. And 2000 is a leap year.

So my trick is to count the difference between the two periods both ways from the mentioned date. If counts agree that the difference is positive (or negative), then two (or one) will always be longer no matter which day we count from.

In some cases the counts will not agree. Then I refuse to call a longer period.

    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");
        }
    }

Output in this case (using the periods from the question):

One is greater (unambiguously)

The normalized method I am using in the first comparison converts between years and months ensuring that the months are in the interval from -11 through +11 and have the same sign as the years (except if years are 0). This can be done unambiguously since there are always 12 months in a year.

You may be able to refine it to report that one period is longer than or equal to the other. For example if one is a month and two is 31 days, we know that two is at least as long as one, even though we can’t decide whether it’s also strictly longer.

1
Radu Linu On

In case you have a period of months and period of years you can do as follows:

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()); 
3
Martin Cassidy On

This can be done with a proper compareTo() approach by borrowing from LocalDate.

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