Javaの日付時刻APIのDateTimeFormatterについて。
|
日付時刻オブジェクトを文字列に整形(format)、あるいは逆に文字列から日付時刻オブジェクトを生成(parse)するにはjava.time.format.DateTimeFormatterを使う。
(従来はjava.text.SimpleDateFormatを使っていた)
DateTimeFormatterは不変オブジェクトであり、スレッドセーフである。
ただし、シリアライズ可能ではない。(SimpleDateFormatはシリアライズ可能だったが)[2017-07-26]
import java.time.format.DateTimeFormatter;
DateTimeFormatterインスタンスは、基本的にofPatternメソッドで生成する。
import java.time.format.DateTimeFormatter;
内容 | 例 | 出力例 |
---|---|---|
パターン指定 | DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS") |
2014/07/21 22:16:46.348 |
固定パターン | DateTimeFormatter.ISO_ZONED_DATE_TIME |
2014-07-21T22:16:46.348+09:00[Asia/Tokyo] |
DateTimeFormatter.ISO_OFFSET_DATE_TIME |
2014-07-21T22:16:46.348+09:00 |
|
DateTimeFormatter.ISO_OFFSET_DATE |
2014-07-21+09:00 |
|
DateTimeFormatter.ISO_OFFSET_TIME |
22:16:46.348+09:00 |
|
DateTimeFormatter.ISO_LOCAL_DATE_TIME |
2014-07-21T22:16:46.348 |
|
DateTimeFormatter.ISO_LOCAL_DATE |
2014-07-21 |
|
DateTimeFormatter.ISO_LOCAL_TIME |
22:16:46.348 |
他に、with系メソッドを使って属性を変更することが出来る。
→リゾルバースタイル
内部ではDateTimeFormatterBuilderを使っており、これを直接使ってDateTimeFormatterオブジェクトを作ってもいい。
従来 日付の整形や解析に使っていたSimpleDateFormatは、スレッドセーフではなかった。
マルチスレッドで同期化せずに使うのに一番簡単な方法は「使う度に毎回インスタンスを生成する」ことだが、インスタンス生成も重い(実行時間が遅い)部類だった。
DateTimeFormatterはスレッドセーフなので、staticフィールドに定義しておいてマルチスレッドで使うことが出来る。
ついでに、インスタンス生成もSimpleDateFormatより軽い。
整形(format)に関してはSimpleDateFormatより速いような遅いような微妙な感じだが、
解析(parse)もDateTimrFormatterの方が速い。
DateTimeFormatter | SimpleDateFromat | |
---|---|---|
インスタンス生成 | DateTimeFormatter.ofPattern("yyyy/MM/dd
HH:mm:ss.SSS"); |
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); |
約 471(406〜500)ms | 約 1060(998〜1123)ms | |
整形 (format) |
dtf.format(zonedDateTime); |
sdf.format(date); |
約 669(593〜811)ms | 100万回を10セットのうち、最初の2セットは約 1411(1388〜1435)ms それ以降は約 573(499〜702)ms |
|
解析 (parse) |
dtf.parse("2014/07/21 14:05:31.789"); |
sdf.parse("2014/07/21 14:05:31.789"); |
約 844(765〜1061)ms | 約 1812(1623〜1981)ms | |
LocalDateTime.parse("2014/07/21 14:05:31.789",
dtf); |
||
ほぼ同じ |
static void loop() throws InterruptedException, ParseException { // SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); // Date date = new Date(); // DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"); // ZonedDateTime date = ZonedDateTime.now(); long total = 0; for (int n = 0; n < 10; n++) { long s = System.currentTimeMillis(); for (int i = 0; i < 100_0000; i++) { new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); // DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"); // sdf.format(date); // dtf.format(date); // sdf.parse("2014/07/21 14:05:31.789"); // dtf.parse("2014/07/21 14:05:31.789"); } long e = System.currentTimeMillis(); long t = e - s; System.out.println(t); total += t; System.gc(); Thread.sleep(1000); } System.out.println("----"); System.out.println(total / 10); }
日付時刻オブジェクトから文字列へ整形するには、日付時刻クラスのformatメソッドを使う方法と、DateTimeFormatterのformatメソッドを使う方法がある。
日付時刻クラスのformatメソッドも内部ではDateTimeFormatterのformatメソッドを呼び出しているが、分かりやすさから言うと前者の方法を使う方が良さそう。
(解析(parse)するときも日付時刻クラスのparseメソッドを使う方が良いので、日付時刻クラスのメソッドを呼ぶ方が統一がとれる)
import java.time.*; import java.time.format.DateTimeFormatter;
// 日付時刻クラスのformatメソッドを使う方法 ZonedDateTime zonedDateTime = ZonedDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSxxxxx VV"); String s = zonedDateTime.format(formatter);
// DateTimeFormatterのformatメソッドを使う方法 ZonedDateTime zonedDateTime = ZonedDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSxxxxx VV"); String s = formatter.format(zonedDateTime);
対象クラス | 例 | 備考 | |
---|---|---|---|
書式 | 出力結果 | ||
ZonedDateTime | yyyy/MM/dd HH:mm:ss.SSSxxxxx VV |
2014/07/21 09:28:05.510+09:00 Asia/Tokyo |
「x」5つは、オフセットを「: (コロン)」付きで出力する。「VV」は、タイムゾーンのIDを出力する。 |
yyyy/MM/dd HH:mm:ss.SSSxxxx zzz |
2014/07/21 09:28:05.510+0900 JST |
「x」4つは、オフセットを数値がくっついた状態で出力する。 「zzz」は、タイムゾーンのSHORT_IDを出力する。 |
|
OffsetDateTime | yyyy/MM/dd HH:mm:ss.SSSxxxxx VV |
Exception in thread "main"
java.time.DateTimeException: Unable to extract value:
class java.time.OffsetDateTime |
OffsetDateTimeにはZoneIdが無いので、「VV」や「zzz」を使うと例外が発生する。 (エラーメッセージが分かりにくい…) |
yyyy/MM/dd HH:mm:ss.SSSxxxxx |
2014/07/21 09:40:28.453+09:00 |
||
yyyy/MM/dd HH:mm:ss.SSSxxxx |
2014/07/21 09:40:28.453+0900 |
||
LocalDateTime | yyyy/MM/dd HH:mm:ss.SSSxxxxx |
Exception in thread "main"
java.time.temporal.UnsupportedTemporalTypeException:
Unsupported field: OffsetSeconds |
LocalDateTimeにはオフセットが無いので、「x」を使うと例外が発生する。 |
yyyy/MM/dd HH:mm:ss.SSS |
2014/07/21 09:43:54.644 |
||
yyyy/M/d H:m:s.SSS |
2014/7/1 2:3:4.000 |
「M」や「H」等を1文字にすると、0埋めされずに出力される。 | |
yyyy/ppM/ppd ppH:ppm:pps.SSS |
2014/ 7/ 1 2: 3: 4.000 |
「pp」を付けるとスペース埋め(パディング)される。 | |
LocalDate | yyyy/MM/dd HH:mm:ss.SSS |
Exception in thread "main"
java.time.temporal.UnsupportedTemporalTypeException:
Unsupported field: HourOfDay |
LocaDateには時刻が無いので、「HH」等を使うと例外が発生する。 |
yyyy/MM/dd |
2014/07/21 |
||
LocalTime | yyyy/MM/dd HH:mm:ss.SSS |
Exception in thread "main"
java.time.temporal.UnsupportedTemporalTypeException:
Unsupported field: YearOfEra |
LocalTimeには日付が無いので、「y」等を使うと例外が発生する。 |
HH:mm:ss.SSS |
09:48:00.914 |
文字列を解析して日付時刻オブジェクトを生成するには、日付時刻クラスのparseメソッドを使う方法と、DateTimeFormatterのparseメソッドを使う方法がある。
日付時刻クラスのparseメソッドも内部ではDateTimeFormatterのparseメソッドを呼び出しているが、前者の方が戻り値のクラスがはっきりするので、基本的には前者の方法を使うのが良さそう。
ただし、
エラーチェック等を行いたい場合はDateTimeFormatterのparseメソッドを使った方が良さそう。
(→DateTimeFormatterのparseUnresolvedメソッド)
なお、解析に失敗するとjava.time.DateTimeExceptionが発生するが、この例外は非チェック例外(RuntimeException)である。
(SimpleDateFormat#parse()はParseExceptionを発生させるが、この例外はチェック例外(catch又はthrowsする必要がある)である)
import java.time.*; import java.time.format.DateTimeFormatter;
// 日付時刻クラスのparseメソッドを使う方法 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); String text = "2014/07/19 12:34:56"; LocalDateTime localDateTime = LocalDateTime.parse(text, formatter);
// DateTimeFormatterのparseメソッドを使う方法 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); String text = "2014/07/19 12:34:56"; TemporalAccessor parsed = formatter.parse(text); LocalDateTime localDateTime = LocalDateTime.from(parsed); // LocalDateTime localDateTime = parsed.query(LocalDateTime::from); // LocalDateTime localDateTime = formatter.parse(text, LocalDateTime::from);
対象クラス | 例 | 備考 | ||
---|---|---|---|---|
書式 | 入力値 | 出力結果 | ||
ZonedDateTime | yyyy/MM/dd HH:mm:ss |
2014/07/21 12:34:56 |
java.time.format.DateTimeParseException: Text
'2014/07/21 12:34:56' could not be parsed: Unable to obtain
ZonedDateTime from TemporalAccessor: {},ISO resolved to
2014-07-21T12:34:56 of type java.time.format.Parsed |
ZonedDateTimeではタイムゾーンの書式が必要。 |
yyyy/MM/dd HH:mm:ss VV |
2014/07/21 12:34:56 Asia/Tokyo |
2014-07-21T12:34:56+09:00[Asia/Tokyo] |
「VV」でタイムゾーンを指定する。 ただし略称は受理されない。 |
|
2014/07/21 12:34:56 JST |
java.time.format.DateTimeParseException: Text
'2014/07/21 12:34:56 JST' could not be parsed at index 20 |
|||
2014/07/21 12:34:56 |
java.time.format.DateTimeParseException: Text
'2014/07/21 12:34:56' could not be parsed at index 19 |
|||
yyyy/MM/dd HH:mm:ss zzz |
2014/07/21 12:34:56 Asia/Tokyo |
2014-07-21T12:34:56+09:00[Asia/Tokyo] |
「zzz」は正式なIDも略称も受理される。 | |
2014/07/21 12:34:56 JST |
2014-07-21T12:34:56+09:00[Asia/Tokyo] |
|||
2014/07/21 12:34:56 |
java.time.format.DateTimeParseException: Text
'2014/07/21 12:34:56' could not be parsed at index 19 |
|||
OffsetDateTime | yyyy/MM/dd HH:mm:ss |
2014/07/21 12:34:56 |
java.time.format.DateTimeParseException: Text
'2014/07/21 12:34:56' could not be parsed: Unable to obtain
OffsetDateTime from TemporalAccessor: {},ISO resolved to
2014-07-21T12:34:56 of type java.time.format.Parsed |
OffsetDateTimeではオフセットの書式が必要。 |
yyyy/MM/dd HH:mm:ssxxxxx |
2014/07/21 12:34:56+09:00 |
2014-07-21T12:34:56+09:00 |
「x」でオフセットを指定する。 | |
2014/07/21 12:34:56 |
java.time.format.DateTimeParseException: Text
'2014/07/21 12:34:56' could not be parsed at index 19 |
|||
LocalDateTime | yyyy/MM/dd HH:mm:ss |
2014/07/21 12:34:56 |
2014-07-21T12:34:56 |
「MM」や「HH」の場合、1桁の値でも0埋めしてある必要がある。 |
2014/7/21 1:2:3 |
java.time.format.DateTimeParseException: Text
'2014/7/21 1:2:3' could not be parsed at index 5 |
|||
2014/ 7/21 1: 2: 3 |
java.time.format.DateTimeParseException: Text
'2014/ 7/21 1: 2: 3' could not be parsed at index 5 |
|||
yyyy/M/d H:m:s |
2014/07/21 12:34:56 |
2014-07-21T12:34:56 |
「M」や「H」の場合、1桁の値でも受理される。 が、空白が入っていると受理されない。 |
|
2014/7/21 1:2:3 |
2014-07-21T01:02:03 |
|||
2014/ 7/21 1: 2: 3 |
java.time.format.DateTimeParseException: Text
'2014/ 7/21 1: 2: 3' could not be parsed at index 5 |
|||
yyyy/ppM/ppd ppH:ppm:pps |
2014/07/21 12:34:56 |
2014-07-21T12:34:56 |
「pp(パディング)」を指定すると、1桁の値の場合に空白でも受理される。 が、詰めた文字列は受理されない。 |
|
2014/7/21 1:2:3 |
java.time.format.DateTimeParseException: Text
'2014/7/21 1:2:3' could not be parsed at index 10 |
|||
2014/ 7/21 1: 2: 3 |
2014-07-21T01:02:03 |
|||
yyyy/[ ]M/[ ]d [ ]H:[ ]m:[ ]s |
2014/07/21 12:34:56 |
2014-07-21T12:34:56 |
角括弧でオプション(省略可能)の書式を指定できる。[2014-07-23] 空白文字をオプションにすれば、空白が有っても無くても受理される。 |
|
2014/7/21 1:2:3 |
2014-07-21T01:02:03 |
|||
2014/ 7/21 1: 2: 3 |
2014-07-21T01:02:03 |
|||
LocalDate | yyyy/MM/dd |
2014/07/21 |
2014-07-21 |
|
LocalTime | HH:mm:ss |
12:34:05 |
12:34:05 |
SimpleDateFormatで解析(parse)する際は、対象文字列内の空白は無視される。[2014-07-23]
// いずれも受理される SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); System.out.println(sdf.parse("2014/07/23")); System.out.println(sdf.parse("2014/7/23")); System.out.println(sdf.parse("2014/ 7/23")); System.out.println(sdf.parse("2014/ 7/ 23"));
しかしDateTimeFormatterの場合、対象文字列内の空白は、書式文字列内に空白が指定されている場合だけ受理される。
DateTimeFormatter tdf = DateTimeFormatter.ofPattern("yyyy/MM/dd"); System.out.println(tdf.parse("2014/07/23")); //× System.out.println(tdf.parse("2014/7/23")); //× System.out.println(tdf.parse("2014/ 7/23")); //× System.out.println(tdf.parse("2014/ 7/ 23"));
DateTimeFormatter tdf = DateTimeFormatter.ofPattern("yyyy/M/d"); System.out.println(tdf.parse("2014/07/23")); System.out.println(tdf.parse("2014/7/23")); //× System.out.println(tdf.parse("2014/ 7/23")); //× System.out.println(tdf.parse("2014/ 7/ 23"));
DateTimeFormatter tdf = DateTimeFormatter.ofPattern("yyyy/ M/d"); //× System.out.println(tdf.parse("2014/07/23")); //× System.out.println(tdf.parse("2014/7/23")); System.out.println(tdf.parse("2014/ 7/23")); //× System.out.println(tdf.parse("2014/ 7/ 23"));
空白をオプション書式にすれば、空白が有っても無くても受理されるようになる。
が、複数の空白には対応できない。(オプションを複数指定できるが、任意の個数には対応しきれない)
DateTimeFormatter tdf = DateTimeFormatter.ofPattern("yyyy/[ ]M/[ ]d"); System.out.println(tdf.parse("2014/07/23")); System.out.println(tdf.parse("2014/7/23")); System.out.println(tdf.parse("2014/ 7/23")); //× System.out.println(tdf.parse("2014/ 7/ 23"));
DateTimeFormatter tdf = DateTimeFormatter.ofPattern("yyyy/[ ][ ]M/[ ][ ]d"); System.out.println(tdf.parse("2014/07/23")); System.out.println(tdf.parse("2014/7/23")); System.out.println(tdf.parse("2014/ 7/23")); System.out.println(tdf.parse("2014/ 7/ 23"));
パターンの「y」は年を表す。[2014-07-23]
「y」は、厳密には暦(ERA。和暦の場合は昭和とか平成とか)に対する年を表す。
暦と関係ない年は「u」で表す。
→ResolverStyleがSTRICTの場合に違いが出る
時(hour)を表すパターンはいくつかある。[2014-07-23]
基本的には「H」だけ使えばいいと思うが。
パターン文字 | 値の範囲 | 備考 |
---|---|---|
h | 1〜12 | AM・PMの時刻。12時が0時相当。 |
H | 0〜23 | |
k | 1〜24 | 24時が0時相当。 |
K | 0〜11 | AM・PMの時刻。 |
大文字・小文字で24時間系か12時間系かが異なるが、hでは小文字がAM/PMで、Kでは大文字がAM/PM。
パターンの「p」はパディング(空白埋め)を意味する。[2014-07-23]
「p」の個数分の桁を確保し、直後に指定したパターンがその桁数になるよう空白埋めされる。
(空白埋めという仕様上、「p」は2個以上必要(「p」が1個だけだとエラーになる))
例えば「ppM」は月が2桁分、「pppM」なら月が3桁分になる。
角括弧で囲むと、その部分はオプション(省略可能)になる。[2014-07-23]
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/[ ]M/[ ]d [ ]H:[ ]m:[ ]s.SSS");
空白文字をオプションにすると、解析(parse)する際、そこに空白が有っても無くても受理されるようになる。
DateTimeFormatterはリゾルバースタイルという属性を持っている。
これは、日時文字列を解析した後の値が範囲外の場合に、それを解決する方法を決めるもの。
デフォルトはSMART(賢い)で、他にLENIENT(寛大)、STRICT(厳密)がある。
import java.time.*; import java.time.format.DateTimeFormatter;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd").withResolverStyle(ResolverStyle.SMART);
対象クラス | 書式 | 入力文字列 | 解析(parse)結果 | ||
---|---|---|---|---|---|
SMART | LENIENT | STRICT | |||
LocalDate | yyyy/MM/dd |
2014/02/28 |
2014-02-28 |
2014-02-28 |
java.time.format.DateTimeParseException: Text
'2014/02/28' could not be parsed: Unable to obtain LocalDate from
TemporalAccessor: {YearOfEra=2014, MonthOfYear=2, DayOfMonth=28},ISO of
type java.time.format.Parsed |
2014/02/30 |
2014-02-28 |
2014-03-02 |
java.time.format.DateTimeParseException: Text
'2014/02/30' could not be parsed: Unable to obtain LocalDate from
TemporalAccessor: {YearOfEra=2014, MonthOfYear=2, DayOfMonth=30},ISO of
type java.time.format.Parsed |
||
2014/06/31 |
2014-06-30 |
2014-07-01 |
java.time.format.DateTimeParseException: Text
'2014/06/31' could not be parsed: Unable to obtain LocalDate from
TemporalAccessor: {YearOfEra=2014, MonthOfYear=6, DayOfMonth=31},ISO of
type java.time.format.Parsed |
||
2014/07/32 |
java.time.format.DateTimeParseException: Text
'2014/07/32' could not be parsed: Invalid value for DayOfMonth (valid
values 1 - 28/31): 32 |
2014-08-01 |
java.time.format.DateTimeParseException: Text
'2014/07/32' could not be parsed: Unable to obtain LocalDate from
TemporalAccessor: {YearOfEra=2014, MonthOfYear=7, DayOfMonth=32},ISO of
type java.time.format.Parsed |
||
2014/13/01 |
java.time.format.DateTimeParseException: Text
'2014/13/01' could not be parsed: Invalid value for MonthOfYear (valid
values 1 - 12): 13 |
2015-01-01 |
java.time.format.DateTimeParseException: Text
'2014/13/01' could not be parsed: Unable to obtain LocalDate from
TemporalAccessor: {YearOfEra=2014, MonthOfYear=13, DayOfMonth=1},ISO of
type java.time.format.Parsed |
||
uuuu/MM/dd |
2014/02/28 |
2014-02-28 |
2014-02-28 |
2014-02-28 |
|
2014/02/30 |
2014-02-28 |
2014-03-02 |
java.time.format.DateTimeParseException: Text
'2014/02/30' could not be parsed: Invalid date 'FEBRUARY 30' |
||
2014/06/31 |
2014-06-30 |
2014-07-01 |
java.time.format.DateTimeParseException: Text
'2014/06/31' could not be parsed: Invalid date 'JUNE 31' |
||
2014/07/32 |
java.time.format.DateTimeParseException: Text
'2014/07/32' could not be parsed: Invalid value for DayOfMonth (valid
values 1 - 28/31): 32 |
2014-08-01 |
java.time.format.DateTimeParseException: Text
'2014/07/32' could not be parsed: Invalid value for DayOfMonth (valid
values 1 - 28/31): 32 |
||
2014/13/01 |
java.time.format.DateTimeParseException: Text
'2014/13/01' could not be parsed: Invalid value for MonthOfYear (valid
values 1 - 12): 13 |
2015-01-01 |
java.time.format.DateTimeParseException: Text
'2014/13/01' could not be parsed: Invalid value for MonthOfYear (valid
values 1 - 12): 13 |
||
LocalTime | HH:mm:ss |
23:59:59 |
23:59:59 |
23:59:59 |
23:59:59 |
24:01:02 |
java.time.format.DateTimeParseException: Text
'24:01:02' could not be parsed: Invalid value for HourOfDay (valid
values 0 - 23): 24 |
00:01:02 |
java.time.format.DateTimeParseException: Text
'24:01:02' could not be parsed: Invalid value for HourOfDay (valid
values 0 - 23): 24 |
範囲外の数値を指定した場合、
SMART(賢い)では、明らかに許容範囲を超えている場合はエラーになる。例えば13月や32日は絶対にありえないのでエラー。
小の月(31日より少ない月)では、31以下で本来ありえない日を指定すると、その月の末日になる。
LENIENT(寛大)では、許容範囲を超えている場合は、本来の月末日を超えて、超過分を加算した日付になる。
STRICT(厳密)では、その日時に許されないものは全てエラーになる。
書式「yyyy」は、厳密には暦(ERA。和暦の場合は昭和とか平成とか)に対する年なので、ERAが指定されていない場合はエラーになるようだ。
書式「uuuu」はERAと無関係な年を表す。
どうしてもSTRICTで「yyyy」を使いたかったら、以下の様にすると使えなくもない。[2014-07-23]
import java.time.format.DateTimeFormatter; import java.time.format.ResolverStyle; import java.util.Locale;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd G") .withLocale(Locale.ENGLISH) .withResolverStyle(ResolverStyle.STRICT); LocalDate ldate = LocalDate.parse("2014/07/23 AD", formatter);
書式「G」で暦を指定する。
ここで、ロケールにENGLISH(等の英語圏)を指定しておかないと、「AD」という値はエラーになってしまう。
(日本の場合は、デフォルトのロケールが日本なので、「西暦」という値ならOK。だが、プログラム内の値に漢字とか使いたくない^^;)
import java.time.chrono.IsoEra; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.ResolverStyle; import java.time.temporal.ChronoField;
DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendPattern("yyyy/MM/dd") .parseDefaulting(ChronoField.ERA, IsoEra.CE.getValue()) .toFormatter() .withResolverStyle(ResolverStyle.STRICT); LocalDate ldate = LocalDate.parse("2014/07/23", formatter);
LENIENT(寛大)の場合、時刻に関しては、超過した日数を取得する方法がある。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss").withResolverStyle(ResolverStyle.LENIENT); TemporalAccessor parsed = formatter.parse("24:01:02"); LocalTime ltime = parsed.query(LocalTime::from); //→ 00:01:02 Period period = parsed.query(DateTimeFormatter.parsedExcessDays()); //→ P1D
同様の方法をLocalDateでも試してみたが、こちらでは超過日数を取得することは出来なかった。
通常のparseメソッドでは、解析フェーズと解決フェーズを実行する。[2014-07-28]
解析フェーズで文字列の解析を行い、それぞれの要素の値を決定する。
解決フェーズでそれぞれの値が正しい範囲に収まっているかどうかを判定する。
parseUnresolvedメソッドを使うと、解析フェーズのみを実行し、解決フェーズを実行しない。
つまり、通常のparseメソッドではエラーとなるような値でも取得することが出来る。
import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.text.ParsePosition; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/[ ]M/[ ]d"); ParsePosition position = new ParsePosition(0); TemporalAccessor temporal = formatter.parseUnresolved("9999/13/99", position); assertThat(temporal, is(not(nullValue()))); assertThat(position.getErrorIndex(), is(-1)); assertThat(temporal.getLong(ChronoField.YEAR_OF_ERA), is(9999L)); assertThat(temporal.getLong(ChronoField.MONTH_OF_YEAR), is(13L)); assertThat(temporal.getLong(ChronoField.DAY_OF_MONTH), is(99L));
parseUnresolvedメソッドでは、解析エラーがあると戻り値はnullとなり、引数のParsePositionオブジェクトにエラー位置がセットされる。
(ParsePositionは、旧来のDateFormatで使用していたParsePositionと全く同じクラス)
解析に成功した場合は、戻り値のTemporalAccessorに対してgetLongメソッドを呼び出すことにより、それぞれの値を取得できる。
ちなみにgetメソッドを使うと有効範囲チェックが行われるので、範囲外の値だと例外が発生する。