こんにちわ、みけです。
前回、前々回に引き続き、Java8のDate and Time APIをさわります。
今日は、文字列から日付を取得する方法です。
DateTimeFormatterクラス
文字列とTemporalAccessorとを変換するクラスです。
DateTimeFormatter#format(TemporalAccessor)メソッドによって、
日付から文字列に変換することができます。
DateTimeFormatter#parse(String)メソッドによって、
文字列から日付に変換することができます。
また、このクラスは、必要に応じて何度でも利用できスレッドセーフです。
では、サンプルコードです。
@RunWith(Enclosed.class) public class DateTimeTest { private static final Object LOCK = new Object(); private static final DateTimeFormatter withoutZone = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); private static final DateTimeFormatter withZone = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss Z"); private static final ZoneId ASIA_TOKYO = ZoneId.of("Asia/Tokyo"); private static final ZoneId GMT = ZoneId.of("GMT"); private static final String DATE_TIME = "2014/10/24 14:09:20 +0900"; private synchronized static void log(Queue<Output> outputs) { while (outputs.size() > 0) { synchronized (LOCK) { System.out.println(outputs.poll()); } } } private static class Output { final String message; final String className; final String methodName; private Output(String message, String className, TestName testName) { this.message = message; this.className = className; this.methodName = testName.getMethodName(); } @Override public String toString() { return className + " - " + message + " <- " + methodName; } } public static class 文字列からDateTimeを取得する { private static final String CLASS = 文字列からDateTimeを取得する.class.getSimpleName(); private static final Queue<Output> QUEUE = new ConcurrentLinkedQueue<>(); @Rule public TestName testName = new TestName(); @Test public void 文字列からLocalDateTimeを取得する() { LocalDateTime parsed = LocalDateTime.parse(DATE_TIME, withZone); log(withoutZone.format(parsed)); } @Test public void 文字列からZonedDateTimeを取得する() { ZonedDateTime parsed = ZonedDateTime.parse(DATE_TIME, withZone); log(withZone.format(parsed)); } @Test(expected = DateTimeParseException.class) public void ゾーンなしの文字列からZonedDateTimeを取得しようとするとDateTimeParseException() { ZonedDateTime parsed = ZonedDateTime.parse("2014/10/24 14:09:20", withoutZone); toQueue(withZone.format(parsed)); } @Test public void 文字列からTemporalAccessorを取得する() { TemporalAccessor parsed = withZone.parse(DATE_TIME); log(LocalDateTime.from(parsed).toString()); } @Test(expected = DateTimeParseException.class) public void 起源なしのフォーマットをSTRICTモードで解釈させるとDateTimeParseException() { DateTimeFormatter strict = withZone.withResolverStyle(ResolverStyle.STRICT); ZonedDateTime.parse(DATE_TIME, strict); } @Test public void STRICTモードを使う場合はyearはuを用いる() { DateTimeFormatter strict = DateTimeFormatter.ofPattern("uuuu/MM/dd HH:mm:ss Z") .withResolverStyle(ResolverStyle.STRICT); ZonedDateTime parsed = ZonedDateTime.parse(DATE_TIME, strict); toQueue(withZone.format(parsed)); } @Test public void 起源ありのフォーマットをSTRICTモードで解釈させる() { String dateTime = "2014/10/24 AD 14:09:20 +0900"; String pattern = "yyyy/MM/dd G HH:mm:ss Z"; DateTimeFormatter strict = DateTimeFormatter.ofPattern(pattern) .withLocale(Locale.US) .withResolverStyle(ResolverStyle.STRICT); ZonedDateTime parsed = ZonedDateTime.parse(dateTime, strict); log(withZone.format(parsed)); } private void log(String message) { QUEUE.offer(new Output(message, CLASS, testName)); } @AfterClass public static void outputMessages() { log(QUEUE); } } }
出力結果
文字列からDateTimeを取得する - 2014/10/24 14:09:20 <- 文字列からLocalDateTimeを取得する
文字列からDateTimeを取得する - 2014/10/24 14:09:20 +0900 <- 起源ありのフォーマットをSTRICTモードで解釈させる
文字列からDateTimeを取得する - java.time.format.Parsed <- 文字列からTemporalAccessorを取得する
文字列からDateTimeを取得する - {InstantSeconds=1414127360, OffsetSeconds=32400},ISO resolved to 2014-10-24T14:09:20 <- 文字列からTemporalAccessorを取得する
文字列からDateTimeを取得する - 2014/10/24 14:09:20 +0900 <- STRICTモードを使う場合はyearはuを用いる
文字列からDateTimeを取得する - 2014/10/24 14:09:20 +0900 <- 文字列からZonedDateTimeを取得する
最初のテスト文字列からLocalDateTimeを取得するでは、
書式"yyyy/MM/dd HH:mm:ss Z"の形式の文字列からLocalDateTimeを取得しています。
LocalDateTimeはゾーン情報を持たないので、パースした際に時差の情報はなくなります。
2つ目のテスト文字列からZonedDateTimeを取得するでは、
書式"yyyy/MM/dd HH:mm:ss Z"の形式の文字列からZonedDateTimeを取得しています。
ZonedDateTimeはゾーン情報をもつので、ゾーンありの形式で出力できます。
3つ目のテストゾーンなしの文字列からZonedDateTimeを取得しようとするとDateTimeParseExceptionでは、
ゾーンなしの"yyyy/MM/dd HH:mm:ss"の形式の文字列からZonedDateTimeを取得しようと
しますが、ゾーン情報がないためにDateTimeParseExceptionが発生します。
4つ目のテスト文字列からTemporalAccessorを取得するでは、最初に紹介した
DateTimeFormatter#parse(String)を使っています。
DateTimeFormatter#parse(String)で返される実装クラスはjava.time.format.Parsedというクラスです。
このクラスのjavadocを読むと、パースしている間にデータを保持するクラスであることがわかります。
まあ、たぶん、このクラスをそのまま使うことはほとんどないと思います。
5つ目のテスト起源なしのフォーマットをSTRICTモードで解釈させるとDateTimeParseExceptionでは、
これまで特に適当に使っていたDateTimeFormatterの書式を厳密に用いています。
このときに、書式"yyyy"は実際はyear-of-eraなので、
era(起源)の情報がないとDateTimeParseExceptionが発生します。
参考 : Java8 の Date & Time API ではまった。 - ゆっちのBlog
では、厳密にパースする場合どうするかというと、
一つ目は上記のブログで書いてあった解決方法で書式を"uuuu/MM/dd HH:mm:ss Z"を用いるという方法です。
これが6つ目のテストSTRICTモードを使う場合はyearはuを用いるです。
もう一つの方法は時刻を表す書式を"yyyy/MM/dd G HH:mm:ss Z"にするという方法です。
これが7つ目のテスト起源ありのフォーマットをSTRICTモードで解釈させるです。
なお、起源ありのフォーマットをパースする場合、
Localを設定していないとDateTimeParseExceptionが発生します。
参考 Java日付時刻APIメモ(Hishidama's Java8 Date and Time API Memo)
また、"SHOWA"とか試してみましたが、ダメでした(´・ω・`)
以上
次回はLocalDateTimeとZonedDateTimeの相互変換について書きます。