web-dev-qa-db-ja.com

Javaを使用してPostgreSQLに時間を保存する最も推奨される方法は何ですか?

PostgreSQLデータベースに2つの日付を保存しています。 1つ目はWebページの訪問データであり、2つ目の日付はWebページの最終変更日です(これは長くなります)。

これらの値を保存するための最善の戦略は何か疑問があります。

私は日/月/年と時:秒のみが必要であり、これは統計的な提案のためだけです。

だから、いくつかの疑問:

  • 情報の回復時に変換するか、上記のデータ形式で保存する限り、最高のストアですか?
  • ソフトウェアへの訪問日またはデータベースへの挿入日を設定するのが最適ですか?
  • javaでは、日付を処理するのに最適なクラスはどのようになっていますか?
48
Renato Dinhani

日時データをPostgreSQLに保存するための戦略は、IMOが次の2つの点に依存する必要があります。

  • ソリューションはneverサーバーまたはクライアントのタイムゾーン設定に依存する必要があります。
  • 現在、PostgreSQL(ほとんどのデータベース)にはfull date-and-time with timezoneを格納するデータ型がありません。そのため、 Instant または LocalDateTime データタイプのいずれかを決定する必要があります。

私のレシピが続きます。


特定のイベントが発生したときに物理的なインスタントを記録したい場合は、(真の "timestamp"、通常は作成/変更/削除イベント)、次に使用:

(PostgreSQLに特有のデータ型_WITH TIMEZONE_/_WITHOUT TIMEZONE_を混同させないでください:それらのどれも実際にタイムゾーンを保存しません)

定型コード:psPreparedStatementrsResultSettzUTCは静的CalendarUTCタイムゾーンに対応するオブジェクト。

_public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));  
_

InstantをデータベースTIMESTAMPTZに書き込みます:

_Instant instant = ...;
Timestamp ts = instant != null ? new Timestamp(instant.toEpochMilli()) : null;
ps.setTimestamp(col, ts, tzUTC);   // column is TIMESTAMPTZ!
_

データベースInstantからTIMESTAMPTZを読み取ります。

_Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? Instant.ofEpochMilli(ts.getTime()) : null;
_

PGタイプがTIMESTAMPTZの場合、これは安全に機能します(その場合、calendarUTCはそのコードには影響しませんが、デフォルトのタイムゾーンに依存しないことを常にお勧めします)。 「安全」とは、結果がサーバーまたはデータベースのタイムゾーンまたはタイムゾーン情報に依存しないことを意味します。操作は完全に可逆であり、タイムゾーン設定に何が起こっても、常に取得できます。 Java側で最初に持っていたのと同じ「インスタント」。


タイムスタンプ(物理タイムライン上のインスタント)ではなく、 "civil" local date-time(つまり、フィールドのセット{year-month-day hour:min:sec(:msecs)})、使用します:

データベースLocalDateTimeからTIMESTAMPを読み取ります。

_Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null ) 
    localDt =  LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
_

LocalDateTimeをデータベースTIMESTAMPに書き込みます:

_  Timestamp ts = null;
  if( localDt != null)    
      ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
  ps.setTimestamp(colNum,ts, tzUTC); 
_

繰り返しますが、この戦略は安全で、安心して眠ることができます:_2011-10-30 23:59:30_を保存した場合は、たとえ明日であっても、常に正確なフィールド(時間= 23、分= 59など)を取得しますPostgresqlサーバー(またはクライアント)のタイムゾーンの変更、JVMまたはOSのタイムゾーン、または国がDSTルールなどを変更した場合.


追加:必要に応じて(当然のことと思われますが)完全な日時指定を保存します( ZonedDatetime :タイムゾーンとタイムゾーン、暗黙的に完全な市民日時情報も含みます) -プラスタイムゾーン)...それから私はあなたに悪いニュースがあります:PostgreSQLにはこれのデータ型がありません(私の知る限り、他のデータベースもありません)。おそらく一対のフィールドに独自のストレージを考案する必要があります:上記の2つのタイプ(非常に冗長で、検索と計算に効率的)、またはそれらの1つとタイムオフセット(タイムゾーン情報を失い、一部の計算は困難で、一部は不可能)、またはそれらの1つとタイムゾーン(文字列として、一部の計算は非常に高価になる可能性があります)。

75
leonbloy

Java.time

それはきれいではありませんが、これは新しい Java.time フレームワークを使用して ZonedDateTime インスタンスで私のために働いたものですin Java 8以降( Tutorial )):

ZonedDateTime receivedTimestamp = some_ts_value;
Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli());
ps.setTimestamp(
   1, 
   ts, 
   Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone()))
); 
3

つかいます Java.util.DateでJavaアプリケーションとtimestamp with time zoneデータベース内。

2
Jonas