web-dev-qa-db-ja.com

JodaLocalTimeをJava.sql.Dateに変換する

JDBCクエリを作成するには、日付を渡す必要があります。日付は Date field タイプのPostgreSqlデータベースに保持されます。これは、時間のない特定の日を表します。

日付だけが必要なので、時間のない日付のみを表す特定のオブジェクトを使用することにしました。これは Joda-TimeパッケージのLocalDate です。 DateTimeオブジェクトを使用すると、冗長な時間データが含まれるだけでなく、時計が1時間遅れると、夏時間の終わりにバグが発生する可能性があるため、重要だと思いました(状況は前例のないほどまれですが、そうではありません)無理だよ)。

しかし、preparedStatement.setDateメソッドの受け入れられた引数を使用してLocalDateオブジェクトを二乗しようとしたとき、それを行う適切な方法が見つかりませんでした。

setDateは、Java.sql.Dateをパラメーターとして受け入れます。そして、Java.sql.Dateオブジェクトを作成する唯一のオプションは、ミリ秒単位で時間を渡すことです。

しかし、これはJoda-TimeパッケージのLocalDateを使用するすべての目的を無効にします。この変換ではミリ秒に戻り、これらの変換が行われている間、時計が1時間戻され、日付が前に変更される可能性があります。日付。

これで、コードに次の行が含まれます。

preparedStatement.setDate(1, new Java.sql.Date(localDate.toDate().getTime()));

しかし、これはLocalDatesetDate形式で受け入れられるように変換するための最良の方法ですか?

夏時間とそれに対応するクロックシフトに関連する私の懸念は正当化されますか?

JDBCのpreparedStatementに日付(および時間のない日付のみ)を渡すためのより良い方法はありますか?

23
ovgolovin

LocalDate#toDateはすべてのタイムゾーンの問題を考慮に入れるため、この手法を使用しても安全です。結果として得られるミリ秒の瞬間はコンテキストに依存しません。これは、変換に使用しているロケール内のその時点で有効なタイムゾーンに一意に関連しています。つまり、1年を通してまったく同じミリ秒値の変換を繰り返すと、一貫してまったく同じ答えが得られますその間にタイムゾーンの規制が変更された場合でも JDKが参照しているため、世界中のすべてのタイムゾーン変更の完全な履歴を文書化するデータベースに。

これらの問題について推論するときは、現在のタイムゾーンが変換に影響を与えないことを覚えておくことが重要です。これは、localeによってパラメーター化され、変換される瞬間のコンテキスト内でのみタイムゾーンを解決します。

私はあなたがこれらすべてについて落ちた不安に心から同情します:それは単純で戦略的な操作を複雑な計算の迷路に変えており、それはトラブルを招くだけです。うまくいけば、JodaTimeにしっかりと基づいたJava 8とその新しい(はい、もう一度!)Date/TimeAPIで物事が前向きになるでしょう。

12
Marko Topolnik

今日も同じ問題が発生しました。私はJDK8を使用しています。最終的に検索に数時間を費やした後、Java SE 8ドキュメントで答えを見つけました。これが解決策です:

statement.setDate(5, Java.sql.Date.valueOf(personToUpdate.getBirthday()));

ステートメントはPreparedStatementインスタンスです。 「personToUpdate.getBirthday()」はLocalDateのタイプです。

4
Ajang R

Org.joda.time.toDateMidnight()とorg.joda.time.toDateMidnight(DateTimeZone zone)は非推奨になっているため、これは私にとって完璧に機能するソリューションです。

私の典型的なクラス、永続化する:

    ...
    import org.joda.time.LocalDate;
    ...

    public class MyObject implements Serializable {

      ...
      private LocalDate startDate;

      ...
      private EndDate startDate;


      // Getters and Setters
      ...

      ...
    }

StartDateを永続化する他のクラスの場合、次のようになります。

    myObject.setStartDate(new LocalDate(myObject.getStartDate().toDateTimeAtStartOfDay(DateTimeZone.getDefault())));
1
Oshkosh1017

tl; dr

_myPreparedStatement.setObject(   // Pass Java.time objects directly to database with JDBC 4.2 or later.
    … , 
    LocalDate.now()              // Get current date today. Better to pass optional `ZoneId` time zone object explicitly than rely implicitly on JVM’s current default.
)
_

Java.time

Java 8以降では、新しいJava.timeフレームワークが組み込まれています。Joda-Timeの後継は JSR 31 で定義され、-で拡張されます。 ThreeTen-Extra プロジェクト。

最終的には、新しいJava.timeタイプを直接処理するようにJDBCドライバーが更新されることを期待しています。しかしそれまでは、Java.sql。*タイプが引き続き必要です。幸い、タイプ間で簡単に変換できる新しいメソッドが追加されました。

日付のみの場合、時刻もタイムゾーンもありません。Javaタイプは LocalDate (Joda-と非常によく似ています)時間)。

LocalDateに関連するタイムゾーンについての懸念については、日付のみを日時、タイムライン上の瞬間に変換する場合に重要です。日付のみは、タイムライン上の瞬間のタイムスパン(あるタイムゾーンの深夜から深夜)に変換するまでは、実際の意味を持たない漠然としたアイデアです。たとえば、「今日」を決定するにはタイムゾーンが必要です。 Java.timeでは、 ZoneId クラスを使用します。

_LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
_

省略した場合、JVMの現在のデフォルトのタイムゾーンが日付の決定に使用されます。つまり、次の2行は同等です。

_LocalDate today = LocalDate.now();
LocalDate today = LocalDate.now( ZoneId.systemDefault() );
_

最初のバージョンであるnow()は、API設計の選択としては不十分だと思います。 JVMの現在のデフォルトのタイムゾーンのこの暗黙のアプリケーションは、ナイーブな開発者の間で混乱、バグ、および悲惨さの終わりを引き起こしません。 1つには、JVMの現在のデフォルトは、マシン、ホストOS設定、およびシステム管理者によって異なります。さらに悪いことに、JVMの現在のデフォルトは、実行時に、そのJVM内の任意のアプリの任意のスレッド内の任意のコードによっていつでもを変更できます。したがって、ベストプラクティスは、常に希望/予想されるタイムゾーンを指定することです。

「今日」のJava.timeオブジェクトができたので、それをデータベースに取り込む方法は?

JDBC 4.2以降では、_Java.time_オブジェクトをデータベースと直接交換します。

_myPreparedStatement.setObject( … , today ) ;
_

取得するには:

_LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
_

JDBC 4.2以降にまだアップグレードできない場合: _Java.sql.Date_ オブジェクトを使用します。 Java 8では、その古いクラスは新しいメソッドを取得しました。 toLocalDate および valueOf 。後者Java.time型からJava.sql型へのブリッジです。

_Java.sql.Date sqlToday = Java.sql.Date.valueOf( today );
_

そこから通常のPreparedStatement処理を行います。

_myPreparedStatement.setDate( 1 , sqlToday );
_

日付のみと日時

おそらくあなたはそのような日付について懸念を持っています-あなたのビジネスニーズに合うだけです。

たとえば、法的な理由で1日の終わりまでに契約が締結されたかどうかを知る必要がある場合、モントリオールの真夜中のストロークなどの特定の瞬間を意味する場合、日付のみは間違ったデータ型です。パリではモントリオールよりも早く新しい日が始まるので、パリの「今日」はモントリオールの「昨日」です。契約期限がモントリオールでの1日の終わりとして法的に定義されている場合は、タイムゾーンを適用する必要があります。タイムゾーンを適用するには、日付のみではなく日時が必要です。 LocalDateから ZonedDateTime にジャンプすることはできますが、それは非常に複雑だと思います。データベースは最初から日時型を使用している必要があります。

Postgresでは、日時タイプは _TIMESTAMP WITH TIME ZONE_ タイプを意味します。タイムゾーンは実際には保存されないため、この名前は誤った名前です。 「タイムゾーンのrespectのタイムスタンプ」と考えてください。 Postgresは、オフセットから- [〜#〜] utc [〜#〜] または受信データに付随するタイムゾーン情報を使用してUTCに調整し、そのオフセット/ゾーン情報は破棄されます。もう1つのタイプ_TIMESTAMP WITHOUT TIME ZONE_は、オフセット/ゾーン情報を完全に無視します。これは、ほとんどのビジネスアプリにとって間違った動作です。

多くの開発者やDBAは、日付のみには明らかな意味があると直感的に考えるという素朴な間違いを犯しているのではないかと思います。ただし、実際には、「契約書に署名」、「請求書を受け取った」、「会社の幹部を雇った」などのイベントに関する合法性など、特定のまたは厳密な瞬間指向のニーズがある場合は、日付ではなく日時の値を使用する必要があります-のみ。

言い換えれば、質問の作者のコメントに関して:

そして、時間インスタンスに頼らずに日付を処理する方法があるはずだと思いました。

いいえ、それは問題を引き起こしていると私は主張します。瞬間が重要な場合は、日付のみではなく日時を使用してください。


Java.timeについて

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

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

詳細については、 Oracle Tutorial を参照してください。そして、StackOverflowで多くの例と説明を検索してください。仕様は JSR 31 です。

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

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

1
Basil Bourque

時間を0に設定するLocalDate#toDateMidnight()を使用してから、DateMidnight#toDate()を使用してみてください。

_Date date = localDate.toDateMidnight().toDate();
_

または、JodaTime 1.5以降を使用している場合は、LocalDate#toDateTimeAtStartOfDay()を使用してからDateTime#toDate()を使用します。

お役に立てば幸いです

0
Khalil