web-dev-qa-db-ja.com

Java 8 Date and Time:オフセットにコロンのないISO 8601文字列を解析

タイムゾーンオフセットを使用して、次のISO 8601 DateTime文字列を解析します。

_final String input = "2022-03-17T23:00:00.000+0000";

OffsetDateTime.parse(input);
LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
_

両方のアプローチは失敗します(OffsetDateTimeも_DateTimeFormatter.ISO_OFFSET_DATE_TIME_を使用するので理にかなっています)タイムゾーンオフセットのコロンのため。

Java.time.format.DateTimeParseException:インデックス23でテキスト「2022-03-17T23:00:00.000 + 0000」を解析できませんでした

しかし Wikipedia によると、タイムゾーンオフセットには4つの有効な形式があります。

_<time>Z 
<time>±hh:mm 
<time>±hhmm 
<time>±hh
_

他のフレームワーク/言語は、問題なくこの文字列を解析できます。 Javascript Date()またはJacksons _ISO8601Utils_(この問題について議論します here

複雑なRegExを使用して独自のDateTimeFormatterを記述できるようになりましたが、私の意見では、_Java.time_ライブラリは有効なISO 8601文字列をデフォルトで解析できるはずです。

ここでは、Jacksons _ISO8601DateFormat_を使用しますが、公式の_date.time_ライブラリを使用することをお勧めします。この問題に取り組むためのあなたのアプローチは何ですか?

18
u_b

オフセットのすべての有効な形式(Z±hh:mm±hhmmおよび±hh)をすべて解析する場合、1つの選択肢はJava.time.format.DateTimeFormatterBuilderをオプションで使用することですパターン(残念ながら、すべてに一致する単一のパターン文字はないようです):

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // date/time
    .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    // offset (hh:mm - "+00:00" when it's zero)
    .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
    // offset (hhmm - "+0000" when it's zero)
    .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
    // offset (hh - "Z" when it's zero)
    .optionalStart().appendOffset("+HH", "Z").optionalEnd()
    // create formatter
    .toFormatter();
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+0000", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00:00", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000Z", formatter));

上記の4つのケースはすべて、2022-03-17T23:00Zに解析されます。


必要に応じて、[]を使用してオプションのセクションを区切って、単一の文字列パターンを定義することもできます。

// formatter with all possible offset patterns
DateTimeFormatter formatter = DateTimeFormatter
    .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS[xxx][xx][X]");

このフォーマッタは、上記の以前のフォーマッタと同様に、すべての場合に機能します。 javadoc をチェックして、各パターンの詳細を取得します。


注:

  • 上記のようなオプションのセクションを持つフォーマッタは、解析には適していますが、フォーマットには適していません。フォーマットするとき、オプションのセクションをall印刷します。つまり、オフセットを何度も印刷します。したがって、日付をフォーマットするには、別のフォーマッターを使用します。
  • 2番目のフォーマッタは、小数点の後に正確に3桁を受け入れます(.SSSのため)。一方、ISO_LOCAL_DATE_TIMEはより柔軟です。秒とナノ秒はオプションであり、小数点以下0〜9桁も受け入れます。入力データに最適なものを選択してください。
24
user7605325

複雑な正規表現を書く必要はありません-その形式で簡単に動作するDateTimeFormatterを構築できます:

DateTimeFormatter formatter =
    DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ROOT);

OffsetDateTime odt = OffsetDateTime.parse(input, formatter);

また、「0000」ではなく「Z」も受け入れます。 not accept "+00:00"(コロンなどを使用)。ドキュメントを考えると驚くべきことですが、コロンなしの値に常にUTCオフセットがある場合は問題ありません。

4
Jon Skeet