Java 8複数の地域をサポートするSpring Webアプリ。顧客の場所のカレンダーイベントを作成する必要があります。だから、私のWebとPostgresサーバーはMSTタイムゾーンでホストされています。しかし、顧客はESTにいるため、読んだいくつかのベストプラクティスに従って、すべての日付時刻をUTC形式で保存すると考えました。データベースのすべての日付時刻フィールドはTIMESTAMPとして宣言されます。
LocalDateTimeを取得してUTCに変換する方法は次のとおりです。
ZonedDateTime startZonedDT = ZonedDateTime.ofLocal(dto.getStartDateTime(), ZoneOffset.UTC, null);
//appointment startDateTime is a LocalDateTime
appointment.setStartDateTime( startZonedDT.toLocalDateTime() );
さて、たとえば、日付の検索が入ったら、要求された日付時刻からUTCに変換し、結果を取得し、ユーザーのタイムゾーン(dbに保存されている)に変換する必要があります:
ZoneId userTimeZone = ZoneId.of(timeZone.getID());
ZonedDateTime startZonedDT = ZonedDateTime.ofLocal(appointment.getStartDateTime(), userTimeZone, null);
dto.setStartDateTime( startZonedDT.toLocalDateTime() );
今、これが正しいアプローチであるかどうかはわかりません。また、LocalDateTimeからZonedDateTimeへ、またはその逆に移動するため、タイムゾーン情報が失われる可能性があるのではないかと思います。
ここに私が見ているものがありますが、私には正しくないようです。 UIからLocalDateTimeを受け取ると、次のようになります。
2016-04-04T08:00
ZonedDateTime =
dateTime=2016-04-04T08:00
offset="Z"
zone="Z"
そして、その変換された値を保存する予定LocalDateTimeに割り当てると、
2016-04-04T08:00
LocalDateTimeに格納しているため、ZonedDateTimeに変換したタイムゾーンが失われているように感じます。
Postgresがその情報を失わないように、エンティティ(予定)にLocalDateTimeではなくZonedDateTimeを使用する必要がありますか?
----------------Edit--------------- -
バジルの優れた答えの後、私はユーザーのタイムゾーンを気にしないという贅沢があることに気付きました-すべての予定は特定の場所に対するものであるため、すべての日付時刻をUTCとして保存し、取得時に場所のタイムゾーンに変換できます。次のフォローアップを行いました question
Postgresには、TIMESTAMP
などのデータ型はありません。 Postgresには、日付と時刻を表す 2つのタイプ があります:TIMESTAMP WITH TIME ZONE
およびTIMESTAMP WITHOUT TIME ZONE
。これらのタイプは、タイムゾーン情報に関して非常に異なる動作をします。
WITH
typeは、任意のオフセットまたはタイムゾーン情報を使用して日時を [〜#〜] utc [〜#〜] に調整し、そのオフセットまたはタイムゾーンを破棄します。 Postgresはオフセット/ゾーン情報を保存しません。WITHOUT
typeは、存在する可能性のあるオフセットまたはゾーン情報を無視します。ここで説明します エキスパートのDavid E. Wheelerのように、実質的に常にWITH
型が必要です。 WITHOUT
は、タイムライン上の固定点ではなく、日時のあいまいな考えがある場合にのみ意味があります。たとえば、「今年のクリスマスは2016-12-25T00:00:00に開始されます」は、 any タイムゾーンに適用されるときにWITHOUT
に格納されますが、まだ適用されていませんタイムライン上の実際の瞬間を取得する単一のタイムゾーン。サンタのエルフがEugene Oregon USの開始時間を追跡している場合、彼らはWITH
タイプと、2016-12-25T00:00:00-08:00
(Z
はZuluまたはUTCを意味する)としてPostgresに保存される2016-12-25T08:00.00Z
などのオフセットまたはタイムゾーンを含む入力を使用します。
Java.timeのPostgresのTIMESTAMP WITHOUT TIME ZONE
に相当するものはJava.time.LocalDateTime
です。 UTCで作業すること(良いこと)を意図していたため、 not を使用してLocalDateTime
(悪いこと)を使用する必要があります。それが混乱とトラブルの主なポイントかもしれません。 LocalDateTime
またはZonedDateTime
の使用について考え続けていますが、どちらも使用しないでください。代わりにInstant
を使用する必要があります(以下で説明します)。
また、LocalDateTimeからZonedDateTimeへ、またはその逆に移動するため、タイムゾーン情報が失われる可能性があるのではないかと思います。
確かにあなたは。 LocalDateTime
の全体のポイントは lose タイムゾーン情報です。したがって、ほとんどのアプリでこのクラスを使用することはほとんどありません。繰り返しますが、クリスマスの例です。または別の例、「会社の方針:世界中のすべての工場が午後12時30分に昼食を取ります」。それはLocalTime
であり、特定の日付の場合はLocalDateTime
です。ただし、タイムゾーンを適用して ZonedDateTime
を取得するまで、タイムライン上の実際のポイントではなく、実際の意味はありません。その昼休みは、デリー工場のタイムライン上のデュッセルドルフ工場とは異なり、デトロイト工場では再び異なります。
LocalDateTime
の「Local」という単語は、 no specific localityを意味するため、直感に反する場合があります。クラス名の「ローカル」を読むときは、「一瞬ではなく、タイムライン上ではなく、ちょっとした日時に関する曖昧なアイデア」と考えてください。
サーバーは、ほとんど常にオペレーティングシステムのタイムゾーンでUTCに設定する必要があります。ただし、システム管理者がそれを変更したり、他のJavaアプリがJVM内の現在のデフォルトタイムゾーンを変更したりするのは非常に簡単なので、プログラミングはこの外部性に決して依存しないでください。したがって、常に希望の/予想されるタイムゾーンを指定してください。 (ちなみに、Locale
も同じです。)
結果:
オタクのヘルメットを着用した勤務時間中は、UTCで考えてください。普通の人に切り替えた日の終わりに、あなたは自分の町の現地時間について考えることに戻ります。
ビジネスロジックはUTCに焦点を合わせる必要があります。データベースストレージ、ビジネスロジック、データ交換、シリアル化、ロギング、および独自の思考はすべて、UTCタイムゾーン(およびちなみに24時間時計)で行う必要があります。データをユーザーに提示するときは、特定のタイムゾーンを適用するだけです。ゾーン化された日時は、アプリの内部の機能部分ではなく、外部のものと考えてください。
Java側では、多くのビジネスロジックで Java.time.Instant
(UTCのタイムライン上の瞬間)を使用します。
Instant now = Instant.now();
うまくいけば [〜#〜] jdbc [〜#〜] ドライバーは最終的に更新され、Instant
のようなJava.time型を直接処理できるようになります。それまでは、Java.sql型を使用する必要があります。古いJava.sqlクラスには、Java.timeとの間の変換のための新しいメソッドがあります。
Java.sql.TimeStamp ts = Java.sql.TimeStamp.valueOf( instant );
ここで、 Java.sql.TimeStamp
オブジェクトを setTimestamp
のPreparedStatement
経由で渡し、PostgresでTIMESTAMP WITH TIME ZONE
として定義された列に保存します。
別の方向に進むには:
Instant instant = ts.toInstant();
したがって、Instant
からJava.sql.Timestamp
、TIMESTAMP WITH TIME ZONE
、すべてUTCで、簡単に実行できます。 タイムゾーンは含まれません。サーバーOS、JVM、およびクライアントの現在のデフォルトタイムゾーンはすべて無関係です。
ユーザーに提示するには、タイムゾーンを適用します。 適切なタイムゾーン名 を使用します。EST
やIST
などの3〜4文字のコードは使用しないでください。
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
必要に応じて別のゾーンに調整できます。
ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( ZoneId.of( "Asia/Kolkata" ) );
UTCのタイムライン上の瞬間、Instant
に戻るには、ZonedDateTime
から抽出できます。
Instant instant = zdt.toInstant();
どこで LocalDateTime
を使用していません。
2016-04-04T08:00
など、UTCからのオフセットやタイムゾーンなしでデータを取得した場合、そのデータはまったく役に立ちません(上記で説明したクリスマスや会社ランチタイプのシナリオについては話していないと仮定します)。オフセット/ゾーン情報のない日時は、通貨を示すことのない金額のようなものです:142.70
または$142.70
-役に立たない。しかし、USD 142.70
、またはCAD 142.70
、またはMXN 142.70
…これらは便利です。
その2016-04-04T08:00
値を取得し、目的のオフセット/ゾーンコンテキストが絶対に確実である場合:
LocalDateTime
として解析します。OffsetDateTime
を取得するか、タイムゾーンを適用してZonedDateTime
を取得します。このコードのように。
LocalDateTime ldt = LocalDateTime.parse( "2016-04-04T08:00" );
ZoneId zoneId = ZoneId.of( "Asia/Kolkata" ); // Or "America/Montreal" etc.
ZonedDateTime zdt = ldt.atZone( zoneId ); // Or atOffset( myZoneOffset ) if only an offset is known rather than a full time zone.
あなたの質問は本当に他の多くの質問の複製です。これらの問題は、他の質問と回答で何度も議論されてきました。このトピックの詳細については、Stack Overflowを検索して調査することをお勧めします。
JDBC 4.2では、 Java.time オブジェクトをデータベースと直接交換できます。 Java.sql.Timestamp
や関連するクラスを再度使用する必要はありません。
JDBC仕様で定義されている OffsetDateTime
を使用した保存。
myPreparedStatement.setObject( … , instant.atOffset( ZoneOffset.UTC ) ) ; // The JDBC spec requires support for `OffsetDateTime`.
…またはJDBCドライバでサポートされている場合は、Instant
を直接使用することもできます。
myPreparedStatement.setObject( … , instant ) ; // Your JDBC driver may or may not support `Instant` directly, as it is not required by the JDBC spec.
取得しています。
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Java.time フレームワークは、Java 8以降に組み込まれています。これらのクラスは、 Java.util.Date
、 Calendar
、& SimpleDateFormat
などの厄介な古い legacy 日時クラスに取って代わります。
Joda-Time プロジェクトは、現在 メンテナンスモード であり、 Java.time クラスへの移行を推奨しています。
詳細については、 Oracle Tutorial を参照してください。また、Stack Overflowで多くの例と説明を検索してください。仕様は JSR 31 です。
Java.timeオブジェクトをデータベースと直接交換できます。 JDBC 4.2 以降に準拠する JDBCドライバー を使用します。文字列やJava.sql.*
クラスは必要ありません。
Java.timeクラスはどこで入手できますか?
ThreeTen-Extra プロジェクトは、追加のクラスでJava.timeを拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある証明の場です。ここで、 Interval
、 YearWeek
、 YearQuarter
、 more などの便利なクラスを見つけることができます。