web-dev-qa-db-ja.com

JavaでRFC 3339の日時を解析するにはどうすればよいですか?

HTML5 datetime入力フィールドから値として返された日付を解析しようとしています。 Operaで例を確認してください。返される日付は次のようになります:2011-05-03T11:58:01Z

これをJava日付またはカレンダーオブジェクトに解析します。

理想的には、ソリューションには次のものが必要です。

  • 外部ライブラリ(jar)なし
  • 受け入れ可能なすべてのRFC 3339形式を処理します
  • 文字列は、有効なRFC 3339日付であるかどうかを確認するために簡単に検証できる必要があります
27
Adam

GoogleがGoogle HTTPクライアントライブラリにRfc3339パーサーを実装していることがわかりました

https://github.com/google/google-http-Java-client/blob/dev/google-http-client/src/main/Java/com/google/api/client/util/DateTime。 Java

テスト済み。これは、さまざまなサブ秒の時間フラグメントを解析するのに適しています。

import Java.time.ZoneId;
import Java.time.format.DateTimeFormatter;
import Java.util.Date;

import com.google.api.client.util.DateTime;

DateTimeFormatter formatter = DateTimeFormatter
            .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            .withZone(ZoneId.of("UTC"));

@Test
public void test1e9Parse() {
    String timeStr = "2018-04-03T11:32:26.553955473Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void test1e3Parse() {
    String timeStr = "2018-04-03T11:32:26.553Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void testEpochSecondsParse() {

    String timeStr = "2018-04-03T11:32:26Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.000Z");
}
6
vicknite

tl; dr

Instant.parse( "2011-05-03T11:58:01Z" )

ISO 8601

実際、 RFC 3339 は、実際の標準、 ISO 8601 の自己宣言した「プロファイル」にすぎません。

RFCは、意図的にISO 8601に違反してゼロ時間(-00:00)の負のオフセットを許可し、意味が「オフセット不明」であることを意味するという点で異なります。そのセマンティクスは、私には非常に悪い考えのようです。私は、より賢明なISO 8601ルールを守ることをお勧めします。 ISO 8601では、オフセットがまったくないということは、オフセットが不明であることを意味します。これは明らかな意味ですが、RFCルールは厄介です。

最新のJava.timeクラスは、文字列の解析/生成時にデフォルトでISO 8601形式を使用します。

入力文字列は、UTCでの瞬間を表します。末尾のZZuluの略で、UTCを意味します。

InstantDateではない)

最新のクラスInstantは、UTCにおける瞬間を表します。このクラスは Java.util.Date を置き換え、ミリ秒ではなくナノ秒のより細かい分解能を使用します。

Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;

ZonedDateTimeCalendarではない)

特定の地域(タイムゾーン)の人々が使用する実時間を通じて同じ瞬間を確認するには、ZoneIdを適用してZonedDateTimeを取得します。このクラスZonedDateTimeは、 Java.util.Calendar クラスを置き換えます。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, same point on the timeline, different wall-clock time.

変換中

可能な場合は、従来の日時クラスを使用しないことを強くお勧めします。ただし、Java.timeにまだ更新されていない古いコードと相互運用する必要がある場合は、前後に変換できます。 oldクラスに追加された新しいメソッドを呼び出します。

InstantJava.util.Dateを置き換えます。

Java.util.Date myJUDate = Java.util.Date.from( instant ) ;  // From modern to legacy.
Instant instant = myJUDate.toInstant() ;                    // From legacy to modern.

ZonedDateTimeGregorianCalendarを置き換えます。

Java.util.GregorianCalendar myGregCal = Java.util.GregorianCalendar.from( zdt ) ;  // From modern to legacy.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

Java.util.Calendarが実際にGregorianCalendarである場合は、キャストします。

Java.util.GregorianCalendar myGregCal = ( Java.util.GregorianCalendar ) myCal ;  // Cast to the concrete class.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

箇条書きの懸念

質問の特定の問題について…

  • 外部ライブラリ(jar)なし

Java.timeクラスはJava 8、9、10、およびそれ以降に組み込まれています。実装は後のAndroidにも含まれています。 Java以前のAndroid、この回答の次のセクションを参照してください。

  • すべての受け入れ可能なRFC 3339形式を処理します

さまざまなJava.timeクラスは、私が知っているすべてのISO 8601形式を処理します。標準の後の版から不可解に姿を消したいくつかのフォーマットも処理します。

その他の形式については、parsetoStringなどのさまざまなクラスのLocalDateおよびOffsetDateTimeメソッドを参照してください。また、このトピックに関するmanyの例とディスカッションがあるため、スタックオーバーフローを検索してください。

  • 文字列は、有効なRFC 3339の日付であるかどうかを確認するために簡単に検証できる必要があります

入力文字列を検証するには、 DateTimeParseException をトラップします。

try {
    Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;
} catch ( DateTimeParseException e ) {
    … handle invalid input
}

Java.timeについて

Java.time フレームワークはJava 8以降に組み込まれています。これらのクラスは厄介な古い レガシーに取って代わりますJava.util.DateCalendar 、& SimpleDateFormat などの日時クラス.

Joda-Time プロジェクトは現在 メンテナンスモード であり、 Java.time クラスへの移行を推奨しています。

詳細については、 Oracleチュートリアル を参照してください。スタックオーバーフローで多くの例と説明を検索してください。仕様は JSR 310 です。

Java.timeオブジェクトをデータベースと直接交換できます。 JDBC 4.2 以降に準拠した JDBCドライバー を使用します。文字列も、Java.sql.*クラスも必要ありません。

Java.timeクラスはどこで入手できますか?

  • Java SE 8Java SE 9 以降
    • 内蔵。
    • 標準の一部Java実装がバンドルされたAPI。
    • Java 9では、いくつかのマイナーな機能と修正が追加されています。
  • Java SE 6 および Java SE 7
  • Android
    • 以降のバージョンのAndroidには、Java.timeクラスの実装がバンドルされています。
    • 以前のAndroid(<26)、 ThreeTenABP プロジェクトが適応しますThreeTen-Backport(上記) ThreeTenABPの使用方法… を参照してください。

ThreeTen-Extra プロジェクトは、追加のクラスでJava.timeを拡張します。このプロジェクトは、Java.timeに将来追加される可能性があることを証明する場です。 IntervalYearWeekYearQuartermore などの便利なクラスがここにあります。

15
Basil Bourque

したがって、原則として、これはさまざまな SimpleDateFormat パターンを使用して行われます。

RFC 3339の 個別宣言のパターンのリスト

  • date-fullyear:yyyy
  • 日付-月:MM
  • date-mday:dd
  • 時間-時間:HH
  • 時間-分:mm
  • 時間:ss
  • time-secfrac:_.SSS_(ただし、Sはミリ秒を意味します。これらの数字が3桁より多い場合や少ない場合に何が起こるかは明確ではありません。)
  • time-numoffset:(_+02:00_はサポートされていないようです-代わりに、_+0200_、_GMT+02:00_の形式と、zおよびZ。)
  • time-offset:_'Z'_(他のタイムゾーンはサポートされていません)-これを使用する前にformat.setTimezone(TimeZone.getTimeZone("UTC"))を使用する必要があります。)
  • 部分時間:_HH:mm:ss_または_HH:mm:ss.SSS_。
  • フルタイム:_HH:mm:ss'Z'_または_HH:mm:ss.SSS'Z'_。
  • 完全な日付:_yyyy-MM-dd_
  • 日時:_yyyy-MM-dd'T'HH:mm:ss'Z'_または_yyyy-MM-dd'T'HH:mm:ss.SSS'Z'_

ご覧のとおり、これではすべてを解析できないようです。 _RFC3339DateFormat_を最初から実装することをお勧めします(簡単にするために正規表現を使用するか、効率を上げるために手動で解析する)。

13
Paŭlo Ebermann

ここ は、そのための簡単な方法です。それはあなたのニーズに合うかもしれません。

3
karmakaze

おそらく最もエレガントな方法ではないかもしれませんが、確かに私が最近作った方法で動作しています:

Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
cal.setTime(sdf.parse(dateInString.replace("Z", "").replace("T", "-")));
3
PecaWolf

あなたが持っているフォーマットで2011-05-03T11:58:01Z、以下のコードで実行できます。しかし、最近ChromeおよびOperaでhtml5の日時を試してみたところ、2011-05-03T11:58Z->以下のコードでは処理できないss部分がありません。

new Timestamp(javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar(date).toGregorianCalendar().getTimeInMillis());
2
Coolliam
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(datetimeInFRC3339format)
0
Artsv