Java8の日時APIはとりあえずこれだけ覚えとけ

Javaで日付/時間を扱うには従来はDate/Calendar/DateFormat等のクラスを使っていたが(以下、旧API)、Java8からはjava.timeパッケージに新しくAPIが追加された(以下、新API)。

しかし新APIはパッケージ数が5、クラス数は69もあり最初はどれをどう使うのか戸惑ってしまう。

そこで最低限これだけ覚えておけば旧APIと同じ事ができるという程度の情報をまとめてみた。

# 新APIの特徴

- 旧APIとは全く別のAPI。
- データを格納するクラスは、日時/日付のみ/時間のみなど保持する要素やタイムゾーンの有無などで、複数のクラスから選べるようになった。
- データ保持と日付操作(年/月/日フィールドの取得/変更など)が1クラスで出来るようになった。  
  (旧APIではデータ保持はDateクラス、日付操作はCalendarクラスと分かれていた)  
- 日時クラスはImmutableなオブジェクトになった。既存インスタンスの値を変更する際は新しくインスタンスを作成する事になる。
- 旧APIで月の値は0-11だったが、新APIでは1-12になった。

# このクラスだけ覚えとけ

最低限知っておくべきクラス。

## 日時クラス

- [java.time.LocalDateTime][LocalDateTime]  
    - タイムゾーンのない日時。
    - 内部に[LocalDate][LocalDate]と[LocalTime][LocalTime]のインスタンスを持っている。  
    - 例：2015-12-15T23:30:59.999
- [java.time.ZonedDateTime][ZonedDateTime]
    - タイムゾーン付きの日時。
    - 例：2015-12-15T23:30:59.999+09:00[Asia/Tokyo]
- [java.time.OffsetDateTime][OffsetDateTime]
    - オフセット付きの日時。
    - 例：2015-12-15T23:30:59.999+09:00

特徴

- 旧APIのDateとCalendarを合わせたようなクラス。
- 時間はナノ秒まで格納できる。
- 日時を格納するクラスは他にもたくさんあるけど使い方は似ているので、初めはLocalDateTimeの使い方を覚えれば他も使えるようになる。

## フォーマッタ

- [java.time.fomat.DateTimeFormatter][DateTimeFormatter]
  - 日付・時刻の文字列出力、解析を行う。
  - 旧APIのSimpleDateFormatに相当。
  - ISO標準形式等のフォーマッタも定数として定義してある。

## Dateとの相互変換

- [java.time.Instant][Instant]
  - エポック秒を表すlong値と1秒未満のナノ秒を表すint値(0-999,999,999)からなる日時。
  - 新APIの日時を格納するクラスの中で唯一Dateとの相互変換可能なクラス。
  - 他の日時クラスもInstantを経由してDateとの相互変換が可能。

## その他

- [java.time.ZoneId][ZoneId]
  - タイムゾーンID。(Asia/Tokyoなど)  
  - ZonedDateTimeのインスタンス作成時などに使用する。
- [java.time.ZoneOffset][ZoneOffset]
  - グリニッジ/UTCからのタイムゾーン・オフセット(+09:00など)。
  - OffsetDateTimeのインスタンス作成時などに使用する。
- [java.time.temporal.ChronoField][ChronoField]
  - 「年」「月」などの日時のフィールドを表す列挙型。
  - [TemporalField][TemporalField]インターフェースの実装クラス。
  - 日時クラスから値を取得する場合など、[TemporalField型のパラメータを持つメソッド][class-use/TemporalField]で使用する。
- [java.time.temporal.ChronoUnit][ChronoUnit]
  - 「1年」「1ヶ月」など、日時の単位を表す列挙型。
  - [TemporalUnit][TemporalUnit]インターフェースの実装クラス。
  - [TemporalUnit型のパラメータを持つメソッド][class-use/TemporalUnit]で使用する。

# 使い方

- 主にLocalDateTimeの使い方の例。
- メソッド名のパターンは他の日時クラスもほぼ一緒。
- ここに書いたのは一部のメソッドだけなので、詳細は各クラスのAPIドキュメントを参照して下さい。

## 現在日時の取得 - now

```java:
LocalDateTime d = LocalDateTime.now();
```

## インスタンスの作成 - of

```java:
//年月日時分秒を指定
LocalDateTime d1 = LocalDateTime.of(2015, 12, 15, 23, 30, 59);
//年月日時分秒と1秒未満のナノ秒を指定
LocalDateTime d2 = LocalDateTime.of(2015, 12, 15, 23, 30, 59, 999999999);
```

## フィールドの取得 - get

- 日時オブジェクトから特定のフィールド値を取得する。
- Calendar.get()に相当。

```java:
LocalDateTime d = LocalDateTime.now();
System.out.println(d.getYear());
System.out.println(d.getMonth());
System.out.println(d.getDayOfMonth());
System.out.println(d.getHour());
System.out.println(d.getMinute());
System.out.println(d.getSecond());
System.out.println(d.getNano());
System.out.println(d.get(ChronoField.YEAR));
```

## 値を変更してインスタンスの作成 - plus/minus/with

- 日時オブジェクトの値を変更して新しい日時オブジェクトを作成する。
- Calendar.set()やCalendar.add()に相当。

```java:
LocalDateTime d = LocalDateTime.of(2015, 12, 15, 23, 30, 59);
System.out.println(d.plusDays(20));       //2016-01-04T23:30:59
System.out.println(d.minusDays(20));      //2015-11-25T23:30:59
System.out.println(d.withDayOfMonth(20)); //2015-12-20T23:30:59
```

## 時間を切り捨てる - truncatedTo

```java:
LocalDateTime.of(2015, 12, 15, 23, 30, 59).truncatedTo(ChronoUnit.HOURS);
// 2015-12-15T23:00
```

## 来月1日の12時

```java:
LocalDateTime d = 
        LocalDateTime.now()
        .plusMonths(1)
        .withDayOfMonth(1)
        .withHour(12)
        .truncatedTo(ChronoUnit.HOURS);
```

## ZoneId/ZoneOffsetのインスタンス作成

```java:
ZoneId zoneId = ZoneId.systemDefault();
ZoneId zoneId = ZoneId.of("Asia/Tokyo");
ZoneOffset offset = ZoneOffset.UTC;
ZoneOffset offset = ZoneOffset.ofHours(9);
ZoneOffset offset = ZoneId.systemDefault().getRules().getOffset(Instant.now());
```

## 他の日時クラスに変換 - to/at

```java:
LocalDateTime ldt = LocalDateTime.now();
//LocalDateTime -> ZoneDateTime
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
//LocalDateTime -> OffsetDateTime
OffsetDateTime odt = ldt.atOffset(ZoneOffset.ofHours(9));
//LocalDateTime -> Instant
Instant instant = ldt.toInstant(ZoneOffset.UTC);
```

## ISO日時フォーマッタを使ったインスタンスの生成

- 日時クラスには予めISO日時フォーマッタを使った文字列から日時を作成するコンストラクタが用意されている。

```java:
LocalDateTime.parse("2015-12-15T23:30");
LocalDateTime.parse("2015-12-15T23:30:59");
LocalDateTime.parse("2015-12-15T23:30:59.999");
```

## フォーマッタ

```java:
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime d = LocalDateTime.parse("2015/12/15 23:30:59", f);
System.out.println(d.format(f));
```

注意点

- *DateTimeクラスのparse()を使う時は時刻も含めないといけない。


## Dateとの相互変換

- 日時オブジェクトとDate間の変換はInstantクラスを経由して行う。

```java:
//Date -> *DateTime
Date date = new Date();
ZonedDateTime zdt = date.toInstant().atZone(ZoneId.systemDefault());
LocalDateTime ldt = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
OffsetDateTime odt = OffsetDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

//ZonedDateTime -> Date
ZonedDateTime zdt = ZonedDateTime.now();
Date date = Date.from(zdt.toInstant());

//OffsetDateTime -> Date
OffsetDateTime odt = OffsetDateTime.now();
Date date = Date.from(odt.toInstant());

//LocalDateTime -> Date
LocalDateTime ldt = LocalDateTime.now();
Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
```

# 日時クラスの一覧

最後に、使えそうな日時クラスの一覧。

日時クラスは[Temporal][Temporal]又は[TemporalAccessor][TemporalAccessor]の実装クラス、時間量を表すクラスは[TemporalAmount][TemporalAmount]の実装クラスとなっている。

#### 代表的な日時クラス

||日付と時間|日付のみ|時間のみ|
|:---:|:---:|:---:|:---:|
|タイムゾーンなし|[LocalDateTime][LocalDateTime]|[LocalDate][LocalDate]|[LocalTime][LocalTime]|
|タイムゾーンあり|[ZonedDateTime][ZonedDateTime]|||
|オフセットあり|[OffsetDateTime][OffsetDateTime]||[OffsetTime][OffsetTime]|

#### 日付の一部分だけを持つクラス

- [Year][Year]
- [Month][Month]
- [YearMonth][YearMonth]
- [MonthDay][MonthDay]
- [DayOfWeek][DayOfWeek]

#### その他の日時

- [JapaneseDate][JapaneseDate]
- [Instant][Instant]

#### 時間量

- [Duration][Duration]
    - 時間ベースの時間
- [Period][Period]
    - 日付ベースの時間量

[LocalDateTime]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDateTime.html
[ZonedDateTime]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/ZonedDateTime.html
[OffsetDateTime]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/OffsetDateTime.html
[DateTimeFormatter]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/format/DateTimeFormatter.html
[Instant]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/Instant.html
[ZoneId]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/ZoneId.html
[ZoneOffset]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/ZoneOffset.html
[ChronoField]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/ChronoField.html
[ChronoUnit]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/ChronoUnit.html

[LocalDate]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html
[LocalTime]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalTime.html
[OffsetTime]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/OffsetTime.html
[Year]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/Year.html
[Month]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/Month.html
[YearMonth]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/YearMonth.html
[MonthDay]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/MonthDay.html
[DayOfWeek]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/DayOfWeek.html

[JapaneseDate]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/chrono/JapaneseDate.html

[Duration]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/Duration.html
[Period]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/Period.html

[TemporalField]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/TemporalField.html
[TemporalUnit]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/TemporalUnit.html

[Temporal]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/Temporal.html
[TemporalAccessor]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/TemporalAccessor.html
[TemporalAmount]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/TemporalAmount.html

[class-use/TemporalField]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/class-use/TemporalField.html#java.time
[class-use/TemporalUnit]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/class-use/TemporalUnit.html#java.time