LocalDateで日にちの比較

目的

LocalDateを使った日にちの比較で引っかかりそうな点を発見したのでみんなに知ってもらいたいです。

TLDR

2つのLocalDateの日にちを比較するときはChronoUnitPeriodが役立ちます。ChronoUnitは日時の単位(年、月、日など)ごとに違いを計算するのに役立ちます。Periodは年、月、日に割り当てたあとの日にちの単位の違いを計算するのに役立ちます。説明だけだと分かりづらいので下記の例を見てください。

LocalDate startDate = LocalDate.of(2021, 1, 1);
LocalDate endDate = LocalDate.of(2022, 1, 1);
ChronoUnit.YEARS.between(startDate, endDate); // 1
ChronoUnit.MONTHS.between(startDate, endDate); // 12
ChronoUnit.DAYS.between(startDate, endDate); // 366

Period period = Period.between(startDate, endDate);
period.getYears(); // 1
period.getMonths(); // 0
period.getDays(); // 0

LocalDateを使った日にちの比較で起こったバグ

2つのLocalDateの比較でバグが起きてました。月の計算をPeriodを使って下記のように計算していました。

// Arguments
LocalDate startDate;
LocalDate endDate;

int months = Period.between(startDate, endDate).getMonths();

最初はこれでちゃんと計算されていましたが、一年経ってから動かなくなりました。

バグの原因

月の計算に使っていたPeriodgetMonths()メソッドが、2つの日にちの月の違いを取得するものではありませんでした。実際は年、月、日に割り当てたあとの月の違いが計算されていました。

LocalDate startDate = LocalDate.of(2021, 1, 1);
LocalDate endDate = LocalDate.of(2022, 3, 4);
Period period = Period.between(startDate, endDate);

period.getYears(); // 1
period.getMonths(); // 2
period.getDays(); // 3

解決方法

Periodを使った正しい月の計算は下記でした。

Period period = Period.between(startDate, endDate);
int months = period.getYears() * 12 + period.getMonths();

ChronoUnitっていうクラスを使えばもっとシンプルにできます。

ChronoUnit.MONTHS.between(startDate, endDate);

結論

2つのLocalDateの計算をするときはChronoUnitPeriodの違いを理解して使い分けたほうがいいです。ChronoUnitは日時の単位(年、月、日など)ごとに違いを計算するのに使い、Periodは年、月、日に割り当てたあとの日にちの単位の違いを計算するのに役立ちます。

LocalDate startDate = LocalDate.of(2021, 1, 1);
LocalDate endDate = LocalDate.of(2022, 1, 1);
ChronoUnit.YEARS.between(startDate, endDate); // 1
ChronoUnit.MONTHS.between(startDate, endDate); // 12
ChronoUnit.DAYS.between(startDate, endDate); // 366

Period period = Period.between(startDate, endDate);
period.getYears(); // 1
period.getMonths(); // 0
period.getDays(); // 0