検索
連載

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

Date-Timeの主なメソッド、ChronoUnit、DateTimeFormatterとフォーマット用のパターン文字などの使い方について解説します。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
前のページへ |       

解析や出力に使うフォーマット書式用の「パターン文字」

 DateTimeFormatterでは、解析や出力のための書式を指定するために、「パターン文字」を使って、どのような書式を解析するのかを表現した文字列を設定します。このパターン文字はアルファベットの大文字と小文字(「A」〜「Z」、「a」〜「z」)といくつかの記号の簡単な組み合わせで書式を表現します。

 書式に使えるパターン文字はアルファベットの大文字と小文字(「A」〜「Z」、「a」〜「z」)と「’」(シングルクォーテーション)、「[]」(角括弧)、「{}」(波括弧)、「#」が予約語として登録されており、それ以外の文字は単なる文字列として認識されます。例えば2014年8月9日のDate-Time APIのインスタンスを「uuuu/MM/dd」のフォーマットで文字列を生成すると「2014/08/09」が生成されます。

 また、このパターン文字の中で「{}」と「#」は将来使われる可能性があるものとして予約されています。

 それではフォーマットに使うパターン文字を見てみましょう。下記のものが主なパターン文字になります。

文字 意味 概要
u 西暦での年を表す
M 月を表す
d 日を表す
H 0〜23時を表す
h 1〜12時を表す。例えば時間的オブジェクトの時が13時の場合、1と出力される
m 分を表す
s 秒を表す
S ミリ秒 1秒に満たなかった端数を表す
G 年号 「西暦」、「平成」、「昭和」などの紀元の年号を表す。例えば、JapaneseDateのオブジェクトが平成の時間を持つ場合、ロケールが「Japan」の場合はG、GG、GGG、GGGGと4つまで続けても「平成」と表示され、GGGGGと5つ続けると「H」と表示される。また、LocalDateのようなISO 8601ベースの場合は、「西暦」と表示される
y 紀元での年 紀元での年を表す。例えば西暦2014年の場合、ISO 8601ベースのLocalDateの年は「2014」となり、和暦を表すJapaneseDateの年は「26」となる
a 午前/午後 午前なのか午後なのかを表す。1文字だけで「午前」、「午後」、「AM」、「PM」などを表し、「aa」のように2文字以上続けた場合は例外が発生する
E 曜日 「月」、「月曜日」、「Mon」、「Monday」などの曜日を表す。例えば時間的オブジェクトが土曜日の時間を持つ場合、ロケールが「Japan」の場合は「土」、「US」の場合は「Sat」と出力される。ロケールが「Japan」の場合でE、EE、EEEの場合「土」と出力され、EEEEの場合「土曜日」と出力される
X オフセット UTCからの時差を表す。ただし、UTC(時差が00:00)の場合は「Z」となります。文字数によって表すものが変わる。例えば、UTCからの時差が「+09:00」の場合、下記のようになる
・Xだと「+09」
・XXだと「+0900」
・XXXだと「+09:00」
x オフセット Xとほぼ同じ。ただしUTCの場合は、「Z」ではなく0時間として表現される
V ゾーンID 「Asia/Tokyo」などのタイムゾーンのIDを表す。続けて2文字(「VV」)以外は実行時にIllegalArgumentExceptionが発生する
z ゾーン名 タイムゾーンの名称を表す。例えば、ロケールが「Japan」の場合、z、zz、zzzの場合は「JST」となり、zzzzの場合は「日本標準時」と出力される
引用 「’」で囲まれた文字列がパターンを表さない文字列として認識されます。パターン文字になっている文字を文字列として使う場合に使う。例えば「'year:'yyyy 'month:'M 'day:'d」とした場合、時間的オブジェクトが2014年10月6日の場合、「year:2014 month:10 day:6」と出力される
‘’ シングルクォーテーション シングルクォーテーションを2つつなげたものは「’」の文字になる
[] オプション 「[]」(角括弧)で囲まれた文字列のパターンはオプション扱いとなり、項目がある場合は使われ、ない場合は使われない

 ここで注意するのが、年を表す記号に「u」が追加されたことです。旧日時APIのSimpleDateFormatterの場合、Localeを設定することにより、「y」が和暦の年なのか西暦の年なのかを判別していましたが、Date-Time APIではJapaneseDateという和暦を表すクラスが用意されており、設定したLocaleが何であれ「y」の場合は和暦での年を返します。

 また、従来のパターン文字と同様に大文字と小文字では違うものを表す点に注意してください。例えば大文字の「M」は月を表し、小文字の「m」は分を表します。また、「H」と「h」では同じ時を表しても、大文字の「H」は0時から23時を、小文字の「h」は1時から12時を表します。

 そして、同じパターン文字をいくつ続けるかによって解析される日時の文字や出力される書式が変わることにも注意してください。例えばフォーマット「MM」の場合、時間的オブジェクトが3月なら「03」という文字列になります。

LocalDateTime localDateTime = LocalDateTime.of(2014, 1, 2, 13, 2, 3, 4);
ZonedDateTime zoneDateTime1 = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
ZonedDateTime zoneDateTime2 = zoneDateTime1.withZoneSameInstant(ZoneId.of("UTC"));
 
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("uuuu/M/d H:m:s X '['VV']'");
System.out.println("「uuuu/M/d H:m:s X '['VV']'」: " + formatter1.format(zoneDateTime1));
System.out.println("「uuuu/M/d H:m:s X '['VV']'」: " + formatter1.format(zoneDateTime2));
 
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("uuuu/MM/dd hh:mm:ss a xxx z");
System.out.println("「uuuu/MM/dd hh:mm:ss a xxx z」: " + formatter2.format(zoneDateTime1));
System.out.println("「uuuu/MM/dd hh:mm:ss a xxx z」: " + formatter2.format(zoneDateTime2));
 
JapaneseDate japaneseDate = JapaneseDate.of(2014, 1, 2);
 
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("G yy/M/d E");
System.out.println("「G yy/M/d E」: " + formatter3.format(zoneDateTime1));
System.out.println("「G yy/M/d E」: " + formatter3.format(japaneseDate));
 
DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("GGGG yyyy/MM/dd EEEE");
System.out.println("「GGGG yyyy/MM/dd EEEE」: " + formatter4.format(zoneDateTime1));
System.out.println("「GGGG yyyy/MM/dd EEEE」: " + formatter4.format(japaneseDate));
 
DateTimeFormatter formatter5 = DateTimeFormatter.ofPattern("uuuu/MM/dd", Locale.JAPANESE);
System.out.println("「uuuu/MM/dd」: " + formatter5.format(japaneseDate));
サンプル1
「uuuu/M/d H:m:s X '['VV']'」: 2014/1/2 13:2:3 +09 [Asia/Tokyo]
「uuuu/M/d H:m:s X '['VV']'」: 2014/1/2 4:2:3 Z [UTC]
「uuuu/MM/dd hh:mm:ss a xxx z」: 2014/01/02 01:02:03 午後 +09:00 JST
「uuuu/MM/dd hh:mm:ss a xxx z」: 2014/01/02 04:02:03 午前 +00:00 UTC
「G yy/M/d E」: 西暦 14/1/2 木
「G yy/M/d E」: 平成 26/1/2 木
「GGGG yyyy/MM/dd EEEE」: 西暦 2014/01/02 木曜日
「GGGG yyyy/MM/dd EEEE」: 平成 0026/01/02 木曜日
「uuuu/MM/dd」: 2014/01/02
実行結果1
LocalTime localTime = LocalTime.of(13, 1, 2, 3);
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("hh:mm:ss a", Locale.ENGLISH);
System.out.println("「hh:mm:ss a」: " + formatter1.format(localTime));
 
LocalDate localDate = LocalDate.of(2014, 1, 2);
JapaneseDate japaneseDate = JapaneseDate.of(2014, 1, 2);
 
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("G yy/M/d E", Locale.ENGLISH);
System.out.println("「G yy/M/d E」: " + formatter2.format(localDate));
System.out.println("「G yy/M/d E」: " + formatter2.format(japaneseDate));
 
DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("GGGG yyyy/MM/dd EEEE", Locale.ENGLISH);
System.out.println("「GGGG yyyy/MM/dd EEEE」: " + formatter4.format(localDate));
System.out.println("「GGGG yyyy/MM/dd EEEE」: " + formatter4.format(japaneseDate));
サンプル2
「hh:mm:ss a」: 01:01:02 PM
「G yy/M/d E」: AD 14/1/2 Thu
「G yy/M/d E」: Heisei 26/1/2 Thu
「GGGG yyyy/MM/dd EEEE」: Anno Domini 2014/01/02 Thursday
「GGGG yyyy/MM/dd EEEE」: Heisei 002601/02 Thursday
実行結果2

 この他にもいろいろなパターン文字があります。他にどのようなパターン文字があるか興味ある方はDateTimeFormatterのJavaDocをご参照ください。

解析時の日付の正しさの指定

 parseメソッドを使って解析する際に、引数として受け取った文字列の日付を厳密に解析するのか、それとも存在しない日付でも調整して解析するのかを指定可能です。

 もし、厳密なモードの場合に存在しない日付が指定されたら例外が発生します。このモードを指定する際にはDateTimeFormatterのwithResolverStyleメソッドに対象のモードを指定して、新たなモードになったDateTimeFormatterのインスタンスを生成します。

 この指定するモードは列挙型であるResolverStyleに定義されています。次のものが用意されています。

ResolverStyle 概要
STRICT 厳密に解析するモードです。解析の対象の日時が存在しない場合、例外が発生する
SMART ある程度の誤差を考慮して解析するモード。例えば、月が13月のようなあからさまにおかしい場合は例外が発生しますが、月の日が31日未満の月に31日の設定がされた日付の文字列の場合、その月の最終日として解析する
LENIENT 曖昧に解析するモード。解析の対象の日時の各項目が存在する日時を超過している場合、次の項目に加算して解析する。例えば、「2014/2/31」を解析させた場合、2月の最終日である28日に3日加算した日付として「2014/3/3」の日付を持つインスタンスを返す
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/M/d");
LocalDate value = null;
 
formatter = formatter.withResolverStyle(ResolverStyle.STRICT);
// 例外が発生します
// value = formatter.parse("2014/2/31", LocalDate::from);
// System.out.println("STRICT: 2014/2/31 → " + value);
// value = formatter.parse("2014/13/29", LocalDate::from);
// System.out.println("STRICT: 2014/13/29 → " + value);
 
formatter = formatter.withResolverStyle(ResolverStyle.SMART);
value = formatter.parse("2014/2/31", LocalDate::from);
System.out.println("SMART: 2014/2/31 → " + value);
// 例外が発生します
// value = formatter.parse("2014/13/29", LocalDate::from);
// System.out.println("SMART: 2014/13/29 → " + value);
 
formatter = formatter.withResolverStyle(ResolverStyle.LENIENT);
value = formatter.parse("2014/2/31", LocalDate::from);
System.out.println("LENIENT: 2014/2/31 → " + value);
value = formatter.parse("2014/13/29", LocalDate::from);
System.out.println("LENIENT: 2014/13/29 → " + value);
サンプル
SMART: 2014/2/31 → 2014-02-28
LENIENT: 2014/2/31 → 2014-03-03
LENIENT: 2014/13/29 → 2015-01-29
実行結果

DateTimeFormatterで用意されている定数

 DateTimeFormatterでは、パターン文字の書式を毎回作らなくてもいいように、すでにいくつかの書式の定数が用意されています。主なものは下記で、厳密モード(ResolverStyle.STRICT)で解析されます。

定数 概要
BASIC_ISO_DATE 20140930
もしくは
20140930+0900
区切り文字なしで年月日を表した日付の書式。UTCからの時差がある場合は、それも表示する
ISO_LOCAL_DATE 2014-09-30 年月日を「-」で区切った日付の書式
ISO_OFFSET_DATE 2014-09-30+09:00 ISO_LOCAL_DATEにUTCからの時差を追加した日付の書式
ISO_DATE 2014-09-30
もしくは
2014-09-30+09:00
ISO_LOCAL_DATEの書式でもISO_OFFSET_DATEの書式でも解析できる書式
ISO_LOCAL_TIME 06:30:15.123 時分秒を「:」で区切り、秒未満も表示する時刻を表す書式。秒および秒未満が0の場合、それらは省略できる
ISO_OFFSET_TIME 06:30:15.123+09:00 ISO_LOCAL_TIMEにUTCからの時差を追加した時刻を表す書式。秒および秒未満が0の場合、それらは省略できる
ISO_TIME 06:30:15.123
もしくは
06:30:15.123+09:00
ISO_LOCAL_TIMEの書式でもISO_OFFSET_TIMEの書式でも解析できる書式。秒および秒未満が0の場合、それらは省略できる
ISO_LOCAL_DATE_TIME 2014-09-30T06:30:15.123 年月日を「-」で区切った日付と時分秒を「:」で区切り、秒未満を表示した時刻を「T」で連結した日時を表す書式。秒および秒未満が0の場合、それらは省略できる
ISO_OFFSET_DATE_TIME 2014-09-30T06:30.123:15+09:00 ISO_LOCAL_DATE_TIMEにUTCからの時差を追加した書式。秒および秒未満が0の場合、それらは省略できる
ISO_ZONED_DATE_TIME 2014-09-30T06:30:15.123+09:00[Asia/Tokyo] ISO_OFFSET_DATE_TIMEにタイムゾーンのIDを追加した書式。秒および秒未満、それらは省略できる
ISO_DATE_TIME 2014-09-30T06:30:15.123
もしくは
2014-09-30T06:30.123:15+09:00
もしくは
2014-09-30T06:30:15.123+09:00[Asia/Tokyo]
ISO_LOCAL_DATE_TIMEの書式でもISO_OFFSET_DATE_TIMEでもISO_ZONED_DATE_TIMEの書式でも解析できる書式。秒および秒未満が0の場合、それらは省略できる
ISO_INSTANT 2014-09-30T06:30:15Z 対象のDate-Time APIのインスタンスが持つInstantを表す書式。秒および秒未満が0の場合、それらは省略できる
LocalDateTime localDateTime = LocalDateTime.of(2014, 1, 2, 13, 4, 5, 123456789);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
 
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
System.out.println("BASIC_ISO_DATE: " + formatter.format(localDateTime));
System.out.println("BASIC_ISO_DATE: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_LOCAL_DATE;
System.out.println("ISO_LOCAL_DATE: " + formatter.format(localDateTime));
 
formatter = DateTimeFormatter.ISO_OFFSET_DATE;
System.out.println("ISO_OFFSET_DATE: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_DATE;
System.out.println("ISO_DATE: " + formatter.format(localDateTime));
System.out.println("ISO_DATE: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_LOCAL_TIME;
System.out.println("ISO_LOCAL_TIME: " + formatter.format(localDateTime));
 
formatter = DateTimeFormatter.ISO_OFFSET_TIME;
System.out.println("ISO_OFFSET_TIME: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_TIME;
System.out.println("ISO_TIME: " + formatter.format(localDateTime));
System.out.println("ISO_TIME: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
System.out.println("ISO_LOCAL_DATE_TIME: " + formatter.format(localDateTime));
 
formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println("ISO_OFFSET_DATE_TIME: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
System.out.println("ISO_ZONED_DATE_TIME: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_DATE_TIME;
System.out.println("ISO_DATE_TIME: " + formatter.format(localDateTime));
System.out.println("ISO_DATE_TIME: " + formatter.format(zonedDateTime));
 
formatter = DateTimeFormatter.ISO_INSTANT;
System.out.println("ISO_INSTANT: " + formatter.format(zonedDateTime));
サンプル
BASIC_ISO_DATE: 20140102
BASIC_ISO_DATE: 20140102+0900
ISO_LOCAL_DATE: 2014-01-02
ISO_OFFSET_DATE: 2014-01-02+09:00
ISO_DATE: 2014-01-02
ISO_DATE: 2014-01-02+09:00
ISO_LOCAL_TIME: 13:04:05.123456789
ISO_OFFSET_TIME: 13:04:05.123456789+09:00
ISO_TIME: 13:04:05.123456789
ISO_TIME: 13:04:05.123456789+09:00
ISO_LOCAL_DATE_TIME: 2014-01-02T13:04:05.123456789
ISO_OFFSET_DATE_TIME: 2014-01-02T13:04:05.123456789+09:00
ISO_ZONED_DATE_TIME: 2014-01-02T13:04:05.123456789+09:00[Asia/Tokyo]
ISO_DATE_TIME: 2014-01-02T13:04:05.123456789
ISO_DATE_TIME: 2014-01-02T13:04:05.123456789+09:00[Asia/Tokyo]
ISO_INSTANT: 2014-01-02T04:04:05.123456789Z
実行結果

 この他にもいろいろな書式の定数が用意されています。興味ある方はDateTimeFormatterのJavaDocを参照ください。

次回は、旧日時APIから新Date-Time APIへの変換

 今回はJava 8から導入されたDate-Time APIで何ができるのかについて用意されているメソッドを見てきました。

 次回は、このJava 8から導入されたDate-Time APIとJava 8より前の旧日時APIとの変換について見ていきます。

著者プロフィール

長谷川 智之(はせがわ ともゆき)

株式会社ビーブレイクシステムズ開発部所属。

社内サークル執筆チーム在籍。

主な執筆


Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る