2016-12-13 3 views
0

私はタイムゾーンの値をpostgresから取得しています。javaには結果をTimestamp変数に格納しています。しかし、日時は何とか変わります。私は元の価値を得ていない。Postgresの日付からタイムゾーンを含むタイムスタンプを取得した後に、1が増えます。なぜですか?

CREATE TABLE log_fail 
(
    user_name character varying(99) NOT NULL, 
    date_time timestamp with time zone, 
    CONSTRAINT pk_log_failed_login PRIMARY KEY (user_name) 
) 

テーブルデータ

名=超人

DATE_TIME = 2016年12月12日10:06:39.582から08

sql

文字列uname = "superman";

文字列sql1 = "log_failed_loginからdate_timeを選択します。user_name = '" + uname + "'";

rs1 = conn.createStatement().executeQuery(sql1); 

    if (rs1.next()) 
     { 
    Timestamp attempt_datetime = rs1.getTimestamp("date_time"); 
     } 

結果、私は= 2016年12月12日23

attempt_datetimeを取得しています:36:39.582

+0

「2016-12-12 23:36:39.582」は真夜中に近いです。>時間帯 –

+0

データベースに保存されているタイムゾーンと、タイムゾーンアプリケーションコードが実行されている時間を教えてください。データベースに保存されているタイムゾーン情報は、正しい形式の日時変換を行うのに役立ちます。私はそれがアプリケーションタイムゾーンに変換されると仮定しています。 – Nagaraddi

+1

データベースにタイムゾーンが保存されていないことは恐れています - 常にUTCです –

答えて

1

タイムゾーン付きタイムスタンプのためにhttps://www.postgresql.org/docs/current/static/datatype-datetime.html

、内部に格納された値は常に UTC(ユニバーサルコーディネートタイム、伝統的にはグリニッジ標準と呼ばれています。 時間、G MT)。明示的なタイムゾーンが指定された入力値は、そのタイムゾーンの適切なオフセットを使用して をUTCに変換したものです。入力文字列に のタイムゾーンが指定されていない場合は、システムのTimeZoneパラメータで指定されたタイムゾーンで とし、タイムゾーンゾーンのオフセットを使用して をUTCに変換します。

タイムゾーン値を使用してタイムスタンプが出力されると、それは常に は、現在のタイムゾーンのゾーンにUTCから変換、およびクライアントのタイムゾーンが何であるか、そのゾーン

チェックに 現地時間として表示されます:時間が異なるクライアント

ごとに異なる理由

select setting from pg_settings where name = 'TimeZone'; 

これは

1

問題がある、あなたのアイデアを与えるだろうその上のタイムゾーンJavaコードが実行されるマシンはAsia/Calcuttaであり、タイムスタンプは文字列に変換されるときに変換されます。

あなたはこのようにJavaで、あなたのタイムゾーンを変更することができます:あなたは私達にあなたのすべてのコードを示していないので、我々は確実に答えることができる

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("Europe/Vienna")); 
+0

JVMの現在のデフォルトタイムゾーンを設定することは、劇的な解決策です。そのようにすると、そのJVMで実行されているすべてのアプリケーションのすべてのスレッドのすべてのコードにすぐに影響します。次に、いつでも他のコードが背中の背後でゾーンを変更して、このアプローチを信頼できなくすることができます。より良い解決策は、デフォルトゾーンに暗黙的に依存するのではなく、Javaメソッドを呼び出すときに、明示的に望ましい/予想されるタイムゾーンを指定することです。 –

+0

この回答は問題の**診断で解決したようです**。上記の私のコメントは、ここに提示されている解決策とは単純には一致しません。 –

1

三のオフセット

。しかし、いくつかの控除をすると、理論、3つのオフセットの理論があります。 8時間のUTCの後ろの値を持つ

スタート→2016-12-12 10:06:39.582-08

UTCに→2016-12-12 18:06:39.582Z

を調整不器用サンセリフ→2016-12-12 23:36:39.582

をオフセット報告 +05:302016-12-12 23:36:39.582+05:30

のオフセットに調整します

コアの問題を解決するためにthe Answer of Laurenz Albeに感謝します。私はちょうどここで言葉のナレーションを追加しました。

用語のカップル:

  • offset-from-UTCは、たとえば+05:00のために、UTCから時間と分と秒数です。
  • タイムゾーンは、夏時間(DST)などの異常を処理するための規則のセットであるのオフセットです。 America/Montrealのような適切な名前のcontinent/region

あなたの入力2016-12-12 10:06:39.582-08は、北アメリカの西海岸の多くで使用されているUTCより8時間遅れた瞬間を表します。

あなたはその値をPostgresに保存しました。あなたは正確に言わないが、私はテーブルの列がタイプTIMESTAMP WITH TIME ZONEであると仮定します。

Postgresでは、このタイプは「付随するオフセット/ゾーン情報に関して、受信値をUTCに調整する」という意味です。 Postgresは付随するオフセット/ゾーン情報を破棄します。私は繰り返すが、Postgresはではない。は日付/時刻型ではなく、オフセット/ゾーンを記憶したり覚えている。 WITHタイプとTIMESTAMP WITHOUT TIME ZONEの違いは、WITHOUTタイプのオフセット/ゾーン情報がすべて無視され、調整が行われていないことです。

したがって、2016-12-12 10:06:39.582-08をPostgresに送信したとき、その値は自動的に2016-12-12 18:06:39.582Zに調整されました。 Zは、Zuluの略でUTCを意味します。また、Postgresはではなく、という文字列を格納する代わりに、の代わりに、独自の内部バイナリ記憶形式を使用します。これらの文字列は、この議論の中で人間が読むためのものです。

とにかく、その調整をUTCに戻してください。 10時間から18時間に時間がどのように変更されたかに注意してください。これは、UTCからUTCまでの8時間から8時間の追加が8時間の追加を意味するためです。

だから、この値を保存する作業はうまくいきます。まったく同じ瞬間を記録しました。2016-12-12 10:06:39.582-082016-12-12 18:06:39.582Zの両方が非常に同じ瞬間でしたが、2つの異なるwall-clock time値のレンズを通して見ました。これまでのことはすべて適切かつ賢明です。

後でPostgresデータベースから2016-12-12 18:06:39.582Zの値を取得しました。 JDBCを使用してデータをjava.sql.Timestampオブジェクトに抽出したようです。そこに問題があります。このクラスは、Javaの初期バージョンにバンドルされている古い日時クラスの1つです。業界標準の日時処理の試みとして賞賛される一方で、これらのクラスは混乱し、面倒で、欠陥があることが証明されています。可能な限り避けてください。それらは今やlegacyであり、java.timeクラスに置き換えられています。

これらのレガシー日時クラスの多くの問題の中には、toStringメソッドの実装があります。 Thee toStringのメソッドは、JVMの現在のタイムゾーンを暗黙的に適用することに熱心です。非常に多くのJavaプログラマーに混乱が生じることはありません。この混乱の別の例として、この質問を追加してください。

まず、この暗黙的なタイムゾーンの適用について説明します。まず、現在の瞬間をUTCで、Instantとして取得します。

Instant instant = Instant.now(); 

JVMの現在のデフォルトタイムゾーンとそのオフセットを確認するには、チェックしてください。

ZoneId z = ZoneId.systemDefault(); // Get current default time zone of this JVM. 
String offset = z.getRules().getOffset (instant).toString(); // Extract the offset-from-UTC from our default time zone. 

最後には、そのtoStringメソッドの動作を確認するためにjava.sql.Timestampを作ります。

java.sql.Timestamp ts = new java.sql.Timestamp (instant.toEpochMilli()); 

コンソールにダンプします。

System.out.println ("instant.toString(): " + instant); 
System.out.println ("z.toString(): " + z); 
System.out.println ("offset.toString(): " + offset); 
System.out.println ("ts.toString(): " + ts); 

instant.toString():2016-12-13T23:06:22.635Z
z.toString():アメリカ/ Los_Angeles
offset.toString():-08:00
ts.toString():2016年12月13日15:06:22.635

この出力から、我々はTimestampが実際にUTCで内部値にオフセット-08:00の私自身の現在のデフォルトを適用していることがわかります。さらに悪いことに、このメソッドの出力ではオフセットの表示が省略され、実際にはそうでない場合はUTCとみなされます。さらにフラストレーション:this ‘feature’ is entirely undocumented

このアンチフィーチャの動作が実際に動作していることを確認したので、質問のサンプルデータに戻ることができます。明らかに、得られたデータは2016-12-12 23:36:39.582であり、上記のようにオフセットの兆候がないことは明らかです。しかし、今日の時刻である23:36:39.582と、UTCとして期待されていたかもしれないもの、18:06:39.582を見てください。予期しない値は、予想されるUTC値よりも5時間半、すなわち+05:30です。 +05:30のオフセットはAsia/Kolkataのようなゾーン内でused in Indiaとスリランカになります。そこで、この質問の著者は、Asia/ColomboまたはAsia/Kolkataのような現在のデフォルトタイムゾーンが+05:30のJVM上に自分のコードを実行していると推測できます。

ソリューション

レガシーの日時クラスは使用しないでください。

代わりにjava.timeクラスを使用してください。 java.timeクラスはすべて、標準のISO 8601のテキスト形式を使用して予期しないボーナス動作を起こさずに簡単にtoStringメソッドを持っています。

JDBC 4.2準拠driverは、PreparedStatement::setObjectResultSet::getObjectを呼び出すことによって、java.time型に直接的に対処することができます。

myPreparedStatement.setObject(… , instant) ; 

...と...

Instant instant = myResultSet.getObject(…) ; 

ない場合は、できるだけ簡単にjava.sqlのタイプを使用するようにフォールバックし、しかし。古いクラスに追加された新しい変換メソッドを使用します。

myPreparedStatement.setTimestamp(… , java.sql.Timestamp.from(instant)) ; 

...と...

Instant instant = myResultSet.getTimestamp(…).toInstant() ; 

java.timeについて

java.timeフレームワークは、Java 8に組み込まれており、後にされています。これらのクラスは、java.util.DateCalendar、& SimpleDateFormatなど、面倒な古いlegacy日時クラスに取って代わります。

maintenance modeにあるJoda-Timeプロジェクトは、java.timeクラスへの移行を推奨しています。

Oracle Tutorialを参照して、詳細をご覧ください。そして、多くの例と説明のためにStack Overflowを検索してください。仕様はJSR 310です。

ここで、java.timeクラスを取得するには?

  • Java SE 8SE 9以降
    • 内蔵しています。
    • バンドルされた実装の標準Java APIの一部です。
    • Java 9には、いくつかのマイナーな機能と修正が加えられています。
  • Java SE 6SE 7
    • java.time機能の多くはThreeTen-BackportのJava 6 & 7に戻って、移植されます。
  • Android
    • ThreeTenABPプロジェクトはThreeTen-バックポート具体的にAndroidのため(上記)を適応させます。
    • How to use…を参照してください。

ThreeTen-Extraプロジェクトでは、追加のクラスでjava.timeを拡張します。このプロジェクトは、将来のjava.timeへの追加の可能性を証明する土台です。ここでは、IntervalYearWeekYearQuartermoreなどの便利なクラスがあります。

関連する問題