web-dev-qa-db-ja.com

Java.sql.TimestampをJava.time.OffsetDateTimeに変換する方法は?

Scalaプロジェクトに取り組んでおり、OffsetDateTimeタイプをSQLTimestampタイプにマップする必要があります。DBでUTC時間を設定したいと思います。

OffsetDateTimeからTimestampへの変換は簡単で( この質問 からのヒント)、期待どおりに機能します。

import Java.time._
import Java.sql.Timestamp
val ofsdatetime = OffsetDateTime.now()
// ofsdatetime: Java.time.OffsetDateTime = 2017-04-04T21:46:33.567+02:00

val tstamp = Timestamp.valueOf(ofsdatetime.atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime())
// tstamp: Java.sql.Timestamp = 2017-04-04 19:46:33.567

ご覧のとおり、タイムゾーンが削除され、タイムスタンプが2時間前に戻っています(UTC)。

TimestampからOffsetDateTimeへの変換が期待どおりに機能しない

OffsetDateTime.ofInstant(Instant.ofEpochMilli(tstamp.getTime), ZoneId.systemDefault())

// Java.time.OffsetDateTime = 2017-04-04T19:46:33.567+02:00

新しく作成されたOffsetDateTimeにタイムゾーンが追加されましたが、時刻が正しくありません(UTCのままなので、実際のタイムゾーンに適合させる必要があります)。

どうして?私は何が間違っているのですか?

8
boh717

Java.sql.Timestampはエポックミリを格納しますが、.toStringメソッドはデフォルトのタイムゾーンを使用して文字列をレンダリングします。また、.valueOfはデフォルトのタイムゾーンを使用してLocalDateTimeを解釈します。

両方の組み合わせにより、最初の変換が正しく「見える」ようになりますが、実際には間違っています。値「2017-04-0419:46:33.567」は、UTCではなくデフォルトのTZに表示されています。

valueOfメソッドにLocalDateTime(UTC)を渡したが、それをLocalDateTime(デフォルトのTZ)として解釈したため。

最初の変換が間違っていることの証拠は次のとおりです。

scala> val now = OffsetDateTime.now
now: Java.time.OffsetDateTime = 2017-04-04T14:50:12.534-06:00

scala> Timestamp.valueOf(now.atZoneSameInstant(ZoneId.of("UTC")).toLocalDateTime).getTime == now.toInstant.toEpochMilli
res54: Boolean = false

.atZoneSameInstantが削除されました:

scala> Timestamp.valueOf(now.toLocalDateTime).getTime == now.toInstant.toEpochMilli
res53: Boolean = true

参照されたstackoverflowの質問に対する受け入れられた答えは間違っています。

最初の変換を修正したら(.atZoneSameInstantを削除して)、2番目の変換は問題なく機能するはずです。

12
Alvaro Carrasco

_Java.sql.Timestamp_は、エポック(_1970-01-01T00:00:00.000 UTC_)からのミリ秒を表すlong値の薄いラッパーです。したがって、UTCタイムゾーンは_Java.sql.Timestamp_に暗黙的に含まれます。タイムゾーン情報を保存することはできませんが、暗黙的にUTCであり、誰もがそれを知っている限り、すべて機能します。タイムゾーン情報を_Java.sql.Timestamp_に保存する方法はありません。入力データで受信したタイムゾーンを覚えておく必要がある場合は、DBの別の列として保存してください。 _Java.sql.Timestamp_に正しい時刻を保存することはできますが、入力データで受信したタイムゾーンを保存することはできません。そのためには、追加のフィールドが必要です。

DBの日付をUTCにしたいので、次のようにDBからデータを取得できます:OffsetDateTime.ofInstant(Instant.ofEpochMilli(tstamp.getTime), ZoneId.of("UTC"))。これは正しい時点ですが、UTCタイムゾーンです。 _+0200_はタイムゾーンコンポーネントを格納しないため、DBに保存する前にOffsetDateTimeが_Java.sql.Timestamp_タイムゾーンにあったという事実をDBから取得することはできません。その情報が必要な場合は、DBの別の列に保存する必要があります。

6
radumanolescu