Java8日時APIのちょっと特殊なクラスたち

[前回](http://qiita.com/tag1216/items/91a471b33f383981bfaa)は日時APIの基本的な使い方を説明したので、今回は少し特殊なクラスの使い方を紹介する。


# [Clock]

現在時刻を提供するクラス。
テスト時に現在時刻を固定したりできる。

```java:
Clock clock = Clock.fixed(
        ZonedDateTime.of(2015, 12, 15, 23, 30, 59, 999999999, ZoneId.systemDefault()).toInstant(), 
        ZoneId.systemDefault());
System.out.println(LocalDateTime.now(clock)); //2015-12-15T23:30:59.999999999
```


# [Duration]/[Period]/[ChronoUnit]

時間量を表すクラス。

[Duration]は時間ベースの時間量(3時間40分など)を表す。

```java:
LocalDateTime from = LocalDateTime.of(2015, 12, 15,  0,  0);
LocalDateTime to   = LocalDateTime.of(2015, 12, 16, 12, 30, 59);
Duration duration = Duration.between(from, to);
System.out.println(duration); //PT36H30M59S -> 36時間30分59秒という意味
System.out.println(duration.toDays()); //1
System.out.println(duration.toHours()); //36
System.out.println(duration.toMinutes()); //2190
System.out.println(duration.getSeconds()); //131459
```

[Period]は日付ベースの時間量(3年4ヶ月など)を表す。

```java:
LocalDate from = LocalDate.of(2014, 1, 1);
LocalDate to = LocalDate.of(2015, 3, 10);
Period period = Period.between(from, to);
System.out.println(period); // P1Y2M9D -> 1年2ヶ月9日の意味
System.out.println(period.getYears());  // 1
System.out.println(period.getMonths()); // 2
System.out.println(period.getDays());   // 9
System.out.println(period.toTotalMonths()); // 14
```

Periodにはトータル日数を取得するメソッドはないので、この場合は[ChronoUnit]のbetween()を使用する。

```java:
System.out.println(ChronoUnit.DAYS.between(from, to)); // 433
```

# [JapaneseDate]

和暦の日付。

西暦だけでなく和暦で元号と年を指定してインスタンスを作成できる。

```java:
JanapeseDate d = JapaneseDate.now(); //現在時刻
JapaneseDate d = JapaneseDate.of(2015, 12, 15); //西暦で指定
JapaneseDate d = JapaneseDate.of(JapaneseEra.HEISEI, 27, 12, 15); //和暦で指定
```

toString()の結果。

```java:
System.out.println(JapaneseDate.of(2015, 12, 15));
//Japanese Heisei 27-12-15
```

`元号yy年MM月dd日`という形式はフォーマッタを使う。

```java:
DateTimeFormatter f = DateTimeFormatter.ofPattern("Gy年M月d日")
        .withChronology(JapaneseChronology.INSTANCE);
JapaneseDate d = JapaneseDate.of(2015, 12, 15);

//書式化
System.out.println(f.format(d)); //平成27年12月15日
System.out.println(d.format(f)); //平成27年12月15日

//パース
JapaneseDate d2 = JapaneseDate.from(f.parse("平成27年5月16日"));
System.out.println(LocalDate.from(d2)); //2015-05-16
```

# [DateTimeFormatterBuilder]

出力する要素を一つずつ指定したりフォーマッタを組み合わせたりして、フォーマッタを構築できる。

四半期の値から季節を出力する例。

```java:
Map<Long, String> map = new LinkedHashMap<>();
map.put(1L, "冬");
map.put(2L, "春");
map.put(3L, "夏");
map.put(4L, "秋");
DateTimeFormatter season = new DateTimeFormatterBuilder()
        .appendText(IsoFields.QUARTER_OF_YEAR, map)
        .toFormatter();
DateTimeFormatter f = new DateTimeFormatterBuilder()
        .appendValue(ChronoField.YEAR)
        .appendLiteral("年")
        .append(season)
        .toFormatter();        
Stream.iterate(YearMonth.of(2015, 1), ym -> ym.plusMonths(1))
        .limit(15)
        .map(ym -> ym.toString() + " -> " + ym.format(f))
        .forEach(System.out::println);

//結果
// 2015-01 -> 2015年冬
// 2015-02 -> 2015年冬
// 2015-03 -> 2015年冬
// 2015-04 -> 2015年春
// :
// 2016-03 -> 2016年冬
```

# [WeekFields]

「月の週番号」と「年の週番号」を計算する際のルールを定義するクラス。
週単位で集計をする様な場合に、「水曜から火曜まで」で集計したいとかのワガママに対応できる。

WeekFieldsのインスタンス作成時には以下の情報を指定する。

- 週の最初の曜日
- 最初の週の最小日数

以下は月の週番号を、月曜日から始まり最小日数が4日で計算した場合の例。

```java:
WeekFields wf = WeekFields.of(DayOfWeek.MONDAY, 4);
DateTimeFormatter f = new DateTimeFormatterBuilder()
        .appendPattern("yyyy/MM/dd(E) ")
        .appendLiteral("第")
        .appendValue(wf.weekOfMonth())
        .appendLiteral("週")
        .toFormatter();
Stream.iterate(LocalDate.of(2015, 5, 1), d -> d.plusDays(1))
        .limit(10)
        .map(d -> d.format(f))
        .forEach(System.out::println);

//2015/05/01(金) 第0週
//2015/05/02(土) 第0週
//2015/05/03(日) 第0週
//2015/05/04(月) 第1週
//2015/05/05(火) 第1週
//2015/05/06(水) 第1週
//2015/05/07(木) 第1週
//2015/05/08(金) 第1週
//2015/05/09(土) 第1週
//2015/05/10(日) 第1週
```

5/1(金)から5/3(日)までは4日に満たないので、第1週は5/4(月)からになる。

これを`WeekFields.of(DayOfWeek.MONDAY, 1)`として最小日数を1日にすると、以下の様に5/1(金)から5/3(日)が第１週になる。

```java:
//2015/05/01(金) 第1週
//2015/05/02(土) 第1週
//2015/05/03(日) 第1週
//2015/05/04(月) 第2週
//2015/05/05(火) 第2週
//2015/05/06(水) 第2週
//2015/05/07(木) 第2週
//2015/05/08(金) 第2週
//2015/05/09(土) 第2週
//2015/05/10(日) 第2週
```



[Clock]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/Clock.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

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

[ChronoUnit]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/ChronoUnit.html
[TemporalQuery]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/TemporalQuery.html
[TemporalQueries]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/TemporalQueries.html
[WeekFields]:http://docs.oracle.com/javase/jp/8/docs/api/java/time/temporal/WeekFields.html