LoginSignup
2
1

More than 3 years have passed since last update.

【Java】ChronoUnitのbetween関数は比較内容に合わせて入力を変換しなければ求めたい値にならない場合がある

Last updated at Posted at 2019-07-15

TL;DR

LocalDateTimeに対して月の差を取得するためにChronoUnit.MONTHS.between関数を用いた場合、0.1秒でも期間に足らなければ差が1減ってしまうため、求めたい値になりません。
正確に計算しようと思えば型を変換するなどして、余計な情報を無くした上で比較する必要があります。

起きた問題

以下のようなコードを書いたところ、plusMonths(3)しているにも関わらず2が表示されました。

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

public class Main {
  public static void main(String[] args) throws InterruptedException {
    LocalDateTime endDateTime = LocalDateTime.now().plusMonths(3);
    TimeUnit.MILLISECONDS.sleep(1); // 時間差の再現
    LocalDateTime startDateTime = LocalDateTime.now();

    // 2が出力される
    System.out.println(ChronoUnit.MONTHS.between(startDateTime, endDateTime));
  }
}

原因

公式ドキュメントでは以下のように説明されています。

計算では、2つの時間的オブジェクト間の完全な単位の数を表す整数を返します。たとえば、時間11:30と13:29の間の量は、2時間には1分足りないため、時間数では1時間だけになります。
ChronoUnit (Java Platform SE 8 )

つまり、たとえ1msであっても月の差に足りなければ切り捨てられてしまうということです。

対処

YearMonthにすることで日時情報を切り捨てることで確実に欲しい値が出ます。

import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

public class Main {
  public static void main(String[] args) throws InterruptedException {
    LocalDateTime endDateTime = LocalDateTime.now().plusMonths(3);
    TimeUnit.MILLISECONDS.sleep(1); // 時間差の再現
    LocalDateTime startDateTime = LocalDateTime.now();

    // 3が出力される
    System.out.println(
        ChronoUnit.MONTHS.between(
            YearMonth.from(startDateTime), YearMonth.from(endDateTime)
        )
    );
  }
}

補足

公式ドキュメントには以下のように

実装では、2番目の型を最初の型のインスタンスに変換してから、量を計算します。
ChronoUnit (Java Platform SE 8 )

2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1