web-dev-qa-db-ja.com

Java.util.Dateをどの「Java.time」型に変換しますか?

Java.util.Date オブジェクト、またはJava.util.Calendarオブジェクトがあります。 Java.time フレームワークでそれを正しい型に変換するにはどうすればよいですか?

Java.time型を使用してビジネスロジックの大部分を実行する必要があると聞いたことがあります。 Java.time用にまだ更新されていない古いコードを使用する場合、相互に変換できる必要があります。どのタイプがJava.util.DateまたはJava.util.Calendarにマップされますか?

32
Basil Bourque

はい、できれば必ず Java.time フレームワークを使用する必要があります。

古い日時クラスを避ける

Java.util.DateJava.util.Calendar 、およびJava.text.SimpleTextFormatなどを含む古い日時クラスは、 不十分な設計、混乱を招く、面倒です 。できる限り避けてください。ただし、これらの古いタイプと相互運用する必要がある場合は、古いものと新しいものを切り替えることができます。

多少単純化しすぎた基本的な紹介を読み、古い日時クラスと新しい日時クラスの間を行き来する方向に向けてください。

Java.time

Java.time フレームワークは JSR 31 によって定義され、非常に成功した Joda-Time ライブラリに触発され、 ThreeTen-Extra プロジェクト。機能の大部分は ThreeTen-Backport プロジェクトのJava 6および7にバックポートされ、 のAndroid ThreeTenABP プロジェクト。

どのJava.time型が Java.util.Date と一致しますか? Java.util.Dateオブジェクトは基本的に、UTCのタイムライン上の瞬間、日付と時刻の組み合わせを表します。これをJava.timeのいくつかのタイプのいずれかに変換できます。それぞれについて以下で説明します。変換を容易にするために、古い日時クラスにいくつかの新しいメソッドが追加されていることに注意してください。

diagram of converting from Java.util.Date/.Calendar through ZonedDateTime (or OffsetDateTime) to the three <code>Local…</code> types

Instant

Java.timeの構築ブロックは Instant であり、 [〜#〜] utc [〜#〜] のタイムライン上の瞬間であり、解像度は nanoseconds

通常、UTCでビジネスロジックの多くを実行する必要があります。このような作業では、Instantが頻繁に使用されます。 Instantオブジェクトを渡し、ユーザーへのプレゼンテーションにのみタイムゾーンを適用します。 do オフセットまたはタイムゾーンを適用する必要がある場合は、以下でさらに説明するタイプを使用します。

Java.util.DateからInstant

InstantJava.util.Dateの両方がUTCのタイムライン上にあることを考えると、 Java.util.Date からInstantに簡単に移動できます。古いクラスは新しいメソッド Java.util.Date::toInstant を取得しました。

Instant instant = myUtilDate.toInstant();

InstantからJava.util.Dateまで、他の方向に進むことができます。ただし、秒の小数部に関する情報が失われる可能性があります。 Instantは、2016-01-23T12:34:56.123456789Zなどの小数点以下9桁までの nanoseconds を追跡します。 Java.util.Dateと.Calendarは両方とも、2016-01-23T12:34:56.123Zなどの小数点以下3桁までの ミリ秒 に制限されています。この例でInstantからDateに移動すると、456789が切り捨てられます。

Java.util.Date myUtilDate = Java.util.Date.from(instant);

Java.util.CalendarからInstant

Java.util.Calendarの代わりに Java.util.Date はどうですか? Calendarオブジェクトの内部で、日付時刻は、UTC(1970-01-01T00:00:00.0Z)の1970年の最初の瞬間の エポック基準日時 からの ミリ秒 のカウントとして追跡されます。したがって、この値はInstantに簡単に変換できます。

Instant instant = myUtilCalendar.toInstant() ;

Java.util.GregorianCalendarからZonedDateTime

さらに良いのは、 Java.util.Calendar オブジェクトが実際に Java.util.GregorianCalendar である場合、 ZonedDateTime に簡単に直接アクセスできることです。このアプローチには、埋め込まれたタイムゾーン情報を保持するという利点があります。

Calendarinterface からGregorianCalendar具象クラス への Downcast 。次に、 toZonedDateTime および from メソッドを呼び出して、前後に移動します。

if (myUtilCalendar instanceof GregorianCalendar) {
    GregorianCalendar gregCal = (GregorianCalendar) myUtilCalendar; // Downcasting from the interface to the concrete class.
    ZonedDateTime zdt = gregCal.toZonedDateTime();  // Create `ZonedDateTime` with same time zone info found in the `GregorianCalendar`
}

別の方向に行く…

Java.util.Calendar myUtilCalendar = Java.util.GregorianCalendar.from(zdt); // Produces an instant of `GregorianCalendar` which implements `Calendar` interface.

上で説明したように、1秒の端数に関する情報を失うになる可能性があることに注意してください。 Java.time型(ZonedDateTime)の nanoseconds は、.Calendar/.GregorianCalendarミリ秒 に切り捨てられます。

OffsetDateTime

Instantから offset-from-UTC を適用して、ある地域の wall-clock time に移動できます。オフセットは、 [〜#〜] utc [〜#〜] (東向き)またはUTCの後ろ(西向き)よりも前の時間数、場合によっては分と秒です。 ZoneOffset クラスはこの考えを表しています。結果は OffsetDateTime オブジェクトです。

ZoneOffset offset = ZoneOffset.of("-04:00"); 
OffsetDateTime odt = OffsetDateTime.ofInstant(instant, zoneOffset);

OffsetDateTimeからJava.util.Dateまで、他の方向に進むことができます。 Instantを抽出し、上記のコードで見たように進めます。上記で説明したように、すべての ナノ秒ミリ秒データ損失)に切り捨てられます。

Java.util.Date myUtilDate = Java.util.Date.from(odt.toInstant());

ZonedDateTime

さらに良いことに、完全な タイムゾーン を適用します。タイムゾーンは、 夏時間(DST) などの異常を処理するためのオフセット plus ルールです。

ZoneId を適用すると、 ZonedDateTime オブジェクトが取得されます。 適切なタイムゾーン名 (大陸/地域)を使用します。 ESTISTなど、一般的に見られる3〜4文字の略語は、標準化も一意化もされていないため使用しないでください。

ZoneId zoneId = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);

ZonedDateTimeからJava.util.Dateまで、他の方向に進むことができます。 Instantを抽出し、上記のコードで見たように進めます。上記で説明したように、すべての ナノ秒ミリ秒データ損失)に切り捨てられます。

Java.util.Date myUtilDate = Java.util.Date.from( zdt.toInstant() );

さらに、上記でZonedDateTimeGregorianCalendarに変換できることがわかりました。

LocalDate

時刻やタイムゾーンのない日付のみの値が必要な場合があります。そのためには、 Java.time.LocalDate オブジェクトを使用します。

詳細については、この質問を参照してください。 Java.util.DateをJava.time.LocalDateに変換 、特に this Answer は、Joda-TimeとJavaの両方の発明の背後にある主人によって書かれました。時間。

重要なのは、ZonedDateTimeを経由することです(上記のコードで生成されます)。日付を決定するにはタイムゾーンが必要です。日付は世界中で異なり、東部では新しい日が早く始まります。たとえば、パリの真夜中は新しい日ですが、モントリオールでは「昨日」です。したがって、LocalDateには contain タイムゾーンは含まれませんが、 determine LocalDateにはタイムゾーンが必要です。

LocalDate localDate = zdt.toLocalDate();

LocalDateから日時への逆方向への変換は、時刻を作成することを意味します。ビジネスシナリオで意味のある任意の時刻を選択できます。ほとんどの人にとって、その日の最初の瞬間は理にかなっています。その最初の瞬間を00:00:00.0としてハードコーディングしたくなるかもしれません。一部のタイムゾーンでは、 夏時間(DST) またはその他の異常により、その時間が最初の瞬間として有効でない場合があります。そのため、Java.timeに atStartOfDay を呼び出して正しい時間を決定させます。

ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

LocalTime

まれに、日付とタイムゾーンのない時刻のみが必要な場合があります。この概念は、 LocalTime クラスによって表されます。上記でLocalDateで説明したように、LocalTimeオブジェクトにそのタイムゾーンが含まれていない(「覚えていない」)場合でも、LocalTimeを決定するにはタイムゾーンが必要です。したがって、再び、上記のようにZonedDateTimeから取得したInstantオブジェクトを調べます。

LocalTime localTime = zdt.toLocalTime();

LocalDateTime

他の2つのLocal…タイプと同様に、 LocalDateTime にはタイムゾーンもオフセットも割り当てられていません。そのため、めったに使用しないこれ。日時の大まかなアイデアを提供しますが、タイムライン上の not ポイントです。これは、タイムゾーンに適用される一般的な日付と時刻を意味する場合に使用します。

たとえば、「今年はクリスマスが始まります」は2016-12-25T00:00:00.0になります。 LocalDateTimeのテキスト表現にオフセットまたはタイムゾーンがないことに注意してください。クリスマスはパリのフランスよりも早くインドのデリーで始まり、その後はカナダのモントリオールで始まります。これらの各地域のタイムゾーンを適用すると、タイムライン上で異なる瞬間が得られます。

LocalDateTime ldt = zdt.toLocalDateTime();

enter image description here

96
Basil Bourque