Javaの日付の差分処理はDurationを使おう！！

実装を進めていく中で日付の差分を取得する処理に遭遇することがあるかと思います。
Java 8以前ではCalendarクラスやDateクラスの基準日時からのミリ秒を取得し、差分を見るというやり方が主流でした（今でも検索するとそのようなやり方がたくさん出てきます）。
Java 8からは時間（日付ではなく）を取り扱うことに特化したDurationクラスというものが使えるようになっていますので、そちらのクラスがいかに便利かを紹介したいと思います。

# java.time.Duration
Java 8で追加された`Date-time API`のうちの一つです。
時間を扱うことに特化したクラスで、「何秒か？」のような情報をいい感じに保持することができます。
https://docs.oracle.com/javase/jp/16/docs/api/java.base/java/time/Duration.html

# 差分時間を取得する
例として夏休みが何時間か、みたいな処理を書いてみようかと思います。

```java
import java.time.Duration;
import java.time.LocalDateTime;
// クラス定義、メソッド定義の省略
LocalDateTime beginSummerVacation = LocalDateTime.of(2021, 7, 20, 11, 00);// 夏休みは終業式が終わったタイミングから
LocalDateTime endSummerVacation = LocalDateTime.of(2021, 9, 1, 8, 00);// 始業式が始まるタイミングまで
Duration summerVacationDuration = Duration.between(beginSummerVacation, endSummerVacation);// 期間分の時間を取得する
System.out.println("2021年の夏休みは" + summerVacationDuration.toHours() + "時間です");// 2021年の夏休みは1029時間です
```

ちなみにDateクラスを使うとこんな感じの実装になります。

```java
import java.util.Date;
import java.text.SimpleDateFormat;
// クラス定義、メソッド定義の省略
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
Date beginSummerVacation = df.parse("2021/07/20 11:00:00.000");// 夏休みは終業式が終わったタイミングから
Date endSummerVacation = df.parse("2021/09/01 08:00:00.000");// 始業式が始まるタイミングまで
long summerVacationMillis = endSummerVacation.getTime() - beginSummerVacation.getTime();
System.out.println("2021年の夏休みは" + summerVacationMillis / 1000 / 60 / 60 + "時間です");
```

一部ではこれでいいじゃんって思われる方もいるかと思いますので、どこが良いのかを列挙します。

* Durationのbetweenメソッドで差分の時間を計算していることがコードから明白になる
* `/ 1000 / 60 / 60`みたいなよくわからない計算を書かなくて良い
* 日数が知りたい場合、`summerVacationDuration.toDays();`とするだけで良い（計算書かなくて良い）
* イミュータブルで安全
* 日付の加算処理でもそのまま使える

5点目だけコードで示します。

例えば、夏休みが2倍になったらなーと誰もが考えると思います。その場合の計算処理は以下のようになります。

```java
import java.time.Duration;
import java.time.LocalDateTime;
// クラス定義、メソッド定義の省略
LocalDateTime beginSummerVacation = LocalDateTime.of(2021, 7, 20, 11, 00);// 夏休みは終業式が終わったタイミングから
LocalDateTime endSummerVacation = LocalDateTime.of(2021, 9, 1, 8, 00);// 始業式が始まるタイミングまで
Duration summerVacationDuration = Duration.between(beginSummerVacation, endSummerVacation);// 期間分の時間を取得する
LocalDateTime extendedSummerVacation = endSummerVacation.plus(summerVacationDuration);// endSummerVacationに期間分の時間を足すことで2倍にする
System.out.println("2021年の夏休みは" + extendedSummerVacation + "までです");// 2021年の夏休みは2021-10-14T05:00までです
```

こんなに簡単に夏休みが延長できました！！！

この書き方のメリットは以下のようなことが挙げられます。

* LocalDateTimeのplusメソッドが加算してるってことがわかり直感的
* この処理もイミュータブルで安全
* ミリ秒を使ってDateインスタンス、Calendarインスタンスを生成するより、実装意図がコードから伝わりやすい

ちなみにもう1日夏休みを延長したいという場合も簡単に対応できます。

```java
import java.time.Duration;
import java.time.LocalDateTime;
// クラス定義、メソッド定義の省略
LocalDateTime beginSummerVacation = LocalDateTime.of(2021, 7, 20, 11, 00);// 夏休みは終業式が終わったタイミングから
LocalDateTime endSummerVacation = LocalDateTime.of(2021, 9, 1, 8, 00);// 始業式が始まるタイミングまで
Duration summerVacationDuration = Duration.between(beginSummerVacation, endSummerVacation);// 期間分の時間を取得する
Duration plusSummerVacationDuration = summerVacationDuration.plusDays(1);// 1日延長
LocalDateTime extendedSummerVacation = endSummerVacation.plus(plusSummerVacationDuration);// endSummerVacationに期間分の時間＋1日を足すことで2倍+1日にする
System.out.println("2021年の夏休みは" + extendedSummerVacation + "までです");// 2021年の夏休みは2021-10-15T05:00までです
```



# まとめ
Java 8以降なら日付の差分取得はDurationクラスを使いましょう。
詳細な紹介は省きますが、`toHours()`, `toDays()`以外にも、秒数を取得する`toSeconds()`や期間に何日含まれるかを取得できる`toDaysPart()`メソッドがあります。
Durationを含む、`Date-time API`は日付処理を非常に便利に、わかりやすく書けるよいAPI群なので使える環境であれば、おすすめします！