Java の DateTimeFormatter を使用したときの思わぬ例外

# まえがき

先日の [JJUG CCC 2017 Spring](http://www.java-users.jp/ccc2017spring/) で以下の発表を行いました。

<iframe src="//www.slideshare.net/slideshow/embed_code/key/238O6U11XoIp68" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/JSUXDesign/javajavasdk10" title="Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜" target="_blank">Javaチョットデキルへの道〜JavaコアSDKに見る真似したいコード10選〜</a> </strong> from <strong><a target="_blank" href="https://www.slideshare.net/JSUXDesign">JustSystems Corporation</a></strong> </div>

このセッションでの質問で「この10個はどうやって見つけたのですか？」の質問を頂きました。もともと知っていたのと、今回のセッションをやるにあたり見直してみたというのもあるのですが、やはり普段何かしらJavaコアAPIのソースを読んだり何か試してみたりということが地味に効いているのかもと、思いました。

# 今日の話題

さて、最近以下のことがふと気になったのでやってみました。

# Hを多く書いてみる
![Q1.PNG](https://qiita-image-store.s3.amazonaws.com/0/101293/a78a738f-f1f7-aac4-4302-9c4ba6bdd385.png)

3名の方からご回答頂きました。意見は割れました。私は2番目だと思っていました。
しかし現実は違いました。

![A1.PNG](https://qiita-image-store.s3.amazonaws.com/0/101293/80d7f0c6-004d-d4fd-f14e-7ef66590e5a6.png)

コアAPIの実装を見てみるとこうなっていました。（8u131のソースを抜粋）

```java:DateTimeFormatterBuilder.java(LL.1793-1807)
            case 'd':
            case 'h':
            case 'H':
            case 'k':
            case 'K':
            case 'm':
            case 's':
                if (count == 1) {
                    appendValue(field);
                } else if (count == 2) {
                    appendValue(field, count);
                } else {
                    throw new IllegalArgumentException("Too many pattern letters: " + cur);
                }
                break;

```
2文字しか受け付けないので3文字以上はアウトということですね。

# ではHが2個なら大丈夫か？→場合により例外が出る

そこで次の問題がこれです。

![Q2.PNG](https://qiita-image-store.s3.amazonaws.com/0/101293/da6759cc-2063-c6d6-3df0-ba1a07491e3b.png)

これも2番目だと思いました。
しかし現実は違いました。
![A2.PNG](https://qiita-image-store.s3.amazonaws.com/0/101293/3f4e3dee-384e-bb20-d363-f8c02764f338.png)

これも実装を見てました。

```java:Instant.java(LL.594-606)
    @Override
    public long getLong(TemporalField field) {
        if (field instanceof ChronoField) {
            switch ((ChronoField) field) {
                case NANO_OF_SECOND: return nanos;
                case MICRO_OF_SECOND: return nanos / 1000;
                case MILLI_OF_SECOND: return nanos / 1000_000;
                case INSTANT_SECONDS: return seconds;
            }
            throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
        }
        return field.getFrom(this);
    }
```

今回取得しようとしている`HOUR_OF_DAY`は`ChronoField`なので、上記の

```java
        if (field instanceof ChronoField) {
```
この条件を満たすためこの`if`文の中に入ってきます。そして、`switch`の中に該当するフィールドがないのであえなくアウト、ということになります。

つまり、時間を取りたい場合は`LocalDateTime`など`HOUR_OF_DAY`がサポートされているクラスのインスタンスでないとダメということですね。これは勉強になりました。

# 付録
テストに使用したコードを載せておきます。個人用に書いたのでコメントもなく、2つのテスト切り替えもコメントを外して実行、といったあまりキレイじゃないやり方でお恥ずかしい。

```java:FormatterTest.java
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 
 */
public class FormatterTest {
    /**
     * 
     * @param arguments 
     */
    public static void main(String[] arguments) {
        //System.out.println(DateTimeFormatter.ofPattern("HH").format(Instant.now()));
        System.out.println(DateTimeFormatter.ofPattern("HHHH").format(LocalDateTime.now()));
    }
}
```