org.joda.time.Months.monthsBetween() は ChronoUnit.MONTHS.between() に置き換えられない


Months.monthsBetween()

Joda-Time では、結果が終了日を超えるまで開始日に月を足していくことで計算を行う。 そのため、「長い」月の終わりから「短い」月の終わりまでの期間は、1か月としてカウントされる。

import org.joda.time.LocalDate;
import org.joda.time.Months;

LocalDate start = new LocalDate(2026, 1, 31);
LocalDate end = new LocalDate(2026, 2, 28);
int months = Months.monthsBetween(start, end).getMonths(); // 1


ChronoUnit.MONTHS.between()

2つの temporal オブジェクトの間の時間量を計算する。

temporal オブジェクトが「時間」の場合、たとえば、時間11:30と13:29の間の量は、2時間には1分足りないため、時間数では1時間となる。 temporal オブジェクトが「日」の場合、開始日の「日」より終了日の「日」が小さい場合、最後の1ヶ月は切り捨てられる。

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

LocalDate start = LocalDate.of(2026, 1, 31);
LocalDate end = LocalDate.of(2026, 2, 28);
long months = ChronoUnit.MONTHS.between(start, end); // 0

Joda-Time では、1 となるが、ChronoUnit.MONTHS.between() では、開始日の「日」より終了日の「日」が小さいため、最後の1ヶ月は切り捨てられる。


Joda-Time 互換の between

Joda-Time 互換とするには、以下のような補正を加える必要がある。

public static long monthsBetween(LocalDate start, LocalDate end) {
    if (start.isAfter(end)) {
        return -monthsBetweenJodaStyle(end, start);
    }
    
    long months = ChronoUnit.MONTHS.between(start, end);
    if (!start.plusMonths(months + 1).isAfter(end)) {
        months++;
    }
    return months;
}