連載
» 2015年01月29日 18時00分 公開

Java 8日時APIの主なメソッドとフォーマット用のパターン文字の使い方ここが大変だよJava 8 Date-Time API(2)(5/6 ページ)

[長谷川智之,株式会社ビーブレイクシステムズ]

文字列からの変換や書式を指定した文字列の生成を行う

 Date-Time APIでは、文字列からDate-Time APIのインスタンスを生成する「parse」メソッドとDate-Time APIのクラスから指定した書式で文字列を生成する「format」メソッドが用意されています。また、文字列の生成については書式を指定しない場合は「toString」メソッドで文字列を生成することも可能です。

 ここでは、文字列を解析してDate-Time APIのインスタンスを生成する方法やDate-Time APIのインスタンスから文字列を生成する方法を見ていきましょう。

parseメソッドとformatメソッドの概要

 Date-Time APIでは文字列を解析するparseメソッドと書式に合わせた文字列を生成するformatメソッドが用意されています。書式を指定しない場合は、そのDate-Time APIのクラスが持つデフォルトの書式を使うことになりますが、後述するDateTimeFormatterに書式を指定することにより、これらのメソッドを指定した書式で使うことが可能になります。

 また、これらのメソッドを使用する際にDate-Time APIのLocalDateのような日時を表すクラスからparseメソッドやformatメソッドを呼び出す方法と、指定の書式を設定された後述するDateTimeFormatterインスタンスからparseメソッドやformatメソッドを呼び出す方法の2パターンがあります。

デフォルトの場合(DateTimeFormatterを使わない)

 Date-Time APIでは、基本的には日時を表すための国際規格である「ISO 8601」を基にしているため、ISO 8601を基にしたクラスではデフォルトでISO 8601の拡張表記を文字列の解析や生成に使っています。ISO 8601の拡張表記では年月日を「-」(ハイフン)で区切り、時分秒を「:」(コロン)で区切ります。そして日時と時刻を同時に表す場合は年月日と時分秒を「T」で連結します。

2014-01-02T03:04:05

 また、UTCからの時差(Offset)を表す際は「+」もしくは「-」を付けて「hh:mm」の書式でOffsetを表現します。さらにOffsetの後に「[]」(角括弧)にタイムゾーンを指定することでOffsetではなくタイムゾーンの指定も可能です。

 ただし、タイムゾーンを指定する際はその前にOffsetがないとparseメソッドの実行時に例外が発生しますが、生成されたDate-Time APIのオブジェクトは指定したOffsetを無視することに注意してください。

LocalDateTime localDateTime = LocalDateTime.parse("2014-10-01T23:59:59");
System.out.println("localDateTime=" + localDateTime);
 
OffsetDateTime offsetDateTime = OffsetDateTime.parse("2014-10-01T23:59:59-06:00");
System.out.println("offsetDateTime=" + offsetDateTime);
 
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2014-10-01T23:59:59+08:00[America/Los_Angeles]");
System.out.println("zonedDateTime=" + zonedDateTime);
サンプル
localDateTime=2014-10-01T23:59:59
offsetDateTime=2014-10-01T23:59:59-06:00
zonedDateTime=2014-10-01T23:59:59-07:00[America/Los_Angeles]
実行結果

※上記のzonedDateTimeは「+08:00」のOffsetを指定していますが、生成されたオブジェクトは「-07:00」となっています。

DateTimeFormatterクラスの概要

 文字列を解析や書式を指定する際に、デフォルトのISO 8601の拡張表記とは違う独自の書式を使う場合、java.time.formatパッケージにあるDateTimeFormatterを使うことになります。

 例えば、デフォルトの場合だと「2014-01-02」のように年月日を「-」(ハイフン)で区切っているものを、書式を指定して「2014/1/2」のように年月日を「/」(スラッシュ)で区切ったり、月や日を2桁にしたりしなくても解析や出力できるようにする場合に、その書式を設定したDateTimeFormatterを使います。

 このDateTimeFormatterを使うには、「ofPattern」メソッドに指定した書式を渡すことにより、その書式が設定されたDateTimeFormatterのインスタンスが生成されます。そして、この生成したDateTimeFormatterのインスタンスを通して、日時の文字列を解析してDate-Time APIのインスタンスを生成したり、Date-Time APIのインスタンスから指定した書式の文字列の生成したりします。

 また、DateTimeFormatterのインスタンスを生成する際に、ofPatternメソッドの引数に書式の文字列のみ渡した場合は実行環境のデフォルトのロケールが使われますが、引数にロケールを追加することにより、特定のロケールでの解析や書式を設定できます。

 そして、このDate-Time APIのDateTimeFormatterクラスは旧日時APIのjava.text.DateFormatと違いスレッドセーフになっています。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:m a", Locale.JAPANESE);
System.out.println(LocalTime.of(13,34).format(formatter));
 
formatter = DateTimeFormatter.ofPattern("h:m a", Locale.ENGLISH);
System.out.println(LocalTime.of(13,34).format(formatter));
サンプル
1:34 午後
1:34 PM
出力結果

 また、DateTimeFormatterを使ってparseメソッドを使う場合、Date-Time APIのLocalDateなどの日時を表すインスタンスからparseメソッドを呼び出す方法とDateTimeFormatterのインスタンスからparseメソッドを呼び出す方法の2パターンがあります。

 ここで注意すべき点は、日時を表すクラスからparseメソッドを呼び出した場合は、呼び出したクラスのインスタンスが生成されますが、DateTimeFormatterのインスタンスから呼び出した場合は、引数に文字列だけ渡すと戻り値はjava.time.format.Parsedのインスタンスが返ってきます。

 そのため、意図したDate-Time APIの日時を表すクラスに変換するためには、対象となるクラスのfromメソッドなど使って、返ってきたインスタンスを変換する方法か、DateTimeFormatterのparseメソッドの第2引数にTemporalQueryを設定して変換した戻り値を受け取る方法があります。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/M/d");
 
System.out.println("***** LocalDate.pareseメソッド: *****");
TemporalAccessor value = LocalDate.parse("2014/02/28", formatter);
System.out.println("class=" + value.getClass());
System.out.println("value=" + value);
 
System.out.println("***** formatter.parese(文字列): *****");
value = formatter.parse("2014/2/28");
System.out.println("class=" + value.getClass());
System.out.println("value=" + value);
 
System.out.println("***** formatter.parese(文字列, TemporalQuery<T>): *****");
value = formatter.parse("2014/2/28", LocalDate::from);
System.out.println("class=" + value.getClass());
System.out.println("value=" + value);
サンプル
***** LocalDate.pareseメソッド: *****
class=class java.time.LocalDate
value=2014-02-28
***** formatter.parese(文字列): *****
class=class java.time.format.Parsed
value={},ISO resolved to 2014-02-28
***** formatter.parese(文字列, TemporalQuery<T>): *****
class=class java.time.LocalDate
value=2014-02-28
実行結果

コラム「メソッド参照を使ったTemporalQueryの実装」

先ほどのサンプルでは、TemporalQueryの引数に対し、Java 8から導入された「メソッド参照」という記述方法を使って、関数型インターフェースであるTemporalQueryを実装しています。この関数型インターフェースの実装方法を段階に分けて記述していくと、次のようになります。

LocalDate value = formatter.parse("2014/2/28", new TemporalQuery<LocalDate>() {
    @Override
    public LocalDate queryFrom(TemporalAccessor parsedObject) {
        return LocalDate.from(parsedObject);
    }
});
匿名クラスを使って実装した場合
LocalDate value = formatter.parse("2014/2/28", (parsedObject) -> LocalDate.from(parsedObject));
ラムダ式を使った場合
LocalDate value = formatter.parse("2014/2/28", LocalDate::from);
メソッド参照を使った場合

 Java 8よりラムダ式という新たな記述方法も導入されています。この記述方法は今後Javaの実装で頻繁に使われると思います。ラムダ式の詳細について知りたい場合は、『Java 8はラムダ式でここまで変わる』の連載をご覧ください。


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。