S-JIS[2014-07-21/2014-07-28] 変更履歴

日付時刻フォーマッター

Java日付時刻APIのDateTimeFormatterについて。


概要

日付時刻オブジェクトを文字列に整形(format)、あるいは逆に文字列から日付時刻オブジェクトを生成(parse)するにはjava.time.format.DateTimeFormatterを使う。
(従来はjava.text.SimpleDateFormatを使っていた)

DateTimeFormatterは不変オブジェクトであり、スレッドセーフである。

import java.time.format.DateTimeFormatter;

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オブジェクトを作ってもいい。


DateTimeFormatterインスタンスの生成時間

従来 日付の整形や解析に使っていたSimpleDateFormatは、スレッドセーフではなかった。
マルチスレッドで同期化せずに使うのに一番簡単な方法は「使う度に毎回インスタンスを生成する」ことだが、インスタンス生成も重い(実行時間が遅い)部類だった。

DateTimeFormatterはスレッドセーフなので、staticフィールドに定義しておいてマルチスレッドで使うことが出来る。
ついでに、インスタンス生成もSimpleDateFormatより軽い。

整形(format)に関してはSimpleDateFormatより速いような遅いような微妙な感じだが、
解析(parse)もDateTimrFormatterの方が速い。

Windowsのjava(1.8.0-b132)で100万回の実行にかかった時間
  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
  at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:282)
  at java.time.format.DateTimeFormatterBuilder$ZoneIdPrinterParser.format(DateTimeFormatterBuilder.java:3756)
  at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182)
  at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1745)
  at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1719)
  at java.time.OffsetDateTime.format(OffsetDateTime.java:1667)
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
2014/10/11 12:43:54.000
「M」や「H」等を1文字にすると、0埋めされずに出力される。
yyyy/ppM/ppd ppH:ppm:pps.SSS 2014/ 7/ 1  2: 3: 4.000
2014/10/11 12:43:54.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との違い

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」「u」

パターンの「y」は年を表す。[2014-07-23]
「y」は、厳密には暦(ERA。和暦の場合は昭和とか平成とか)に対する年を表す。
暦と関係ない年は「u」で表す。

ResolverStyleがSTRICTの場合に違いが出る


時「h」「H」「k」「K」

時(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」

パターンの「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メソッドを使うと有効範囲チェックが行われるので、範囲外の値だと例外が発生する。


日付時刻APIに戻る / Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま