こんにちわ、みけです。
前回、前々回に引き続き、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
の相互変換について書きます。