java.sql.Timestampタイムゾーンは特定ですか?


85

UTCdateTimeをDBに保存する必要があります。
特定のタイムゾーンで指定されたdateTimeをUTCに変換しました。そのために私は以下のコードに従いました。
入力dateTimeは「2012122510:00:00Z」です。タイムゾーンは「Asia / Calcutta」です。
サーバー/ DB(oracle)は同じタイムゾーン(IST)「Asia / Calcutta」で実行されています。

この特定のタイムゾーンでDateオブジェクトを取得します

        String date = "20121225 10:00:00 Z";
        String timeZoneId = "Asia/Calcutta";
        TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);

        DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
                    //This date object is given time and given timezone
        java.util.Date parsedDate = dateFormatLocal.parse(date + " "  
                         + timeZone.getDisplayName(false, TimeZone.SHORT));

        if (timeZone.inDaylightTime(parsedDate)) {
            // We need to re-parse because we don't know if the date
            // is DST until it is parsed...
            parsedDate = dateFormatLocal.parse(date + " "
                    + timeZone.getDisplayName(true, TimeZone.SHORT));
        }

       //assigning to the java.sql.TimeStamp instace variable
        obj.setTsSchedStartTime(new java.sql.Timestamp(parsedDate.getTime()));

DBに保存

        if (tsSchedStartTime != null) {
            stmt.setTimestamp(11, tsSchedStartTime);
        } else {
            stmt.setNull(11, java.sql.Types.DATE);
        }

出力

DB(oracle)はdateTime: "20121225 10:00:00、UTCではなく同じものを保存しています。

以下のSQLから確認しました。

     select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable

私のDBサーバーも同じタイムゾーン「アジア/カルカッタ」で実行されています

それは私に以下の外観を与えます

  1. Date.getTime() UTCではありません
  2. または、タイムスタンプはDBに保存するときにタイムゾーンに影響を与えます。ここで何が間違っていますか?

もう1つの質問:

timeStamp.toString()ようにローカルタイムゾーンで印刷java.util.dateしますか?UTCではありませんか?


4
タイトルの質問に答えるために、いいえ、java.sql.TimestampはUTCに基づいています。タイムスタンプの表示はタイムゾーン固有です。
ギルバートルブラン

@Gilbert Le Blancありがとう!「newjava.sql.Timestamp(parsedDate.getTime())」が入力のGMTタイムスタンプオブジェクトを返すと答えていただけますか?
Kanagavelu Sugumar 2012

1
@GilbertLeBlancjava.sql.TimestampはUTCですが、タイムゾーン情報なしでTIMESTAMPフィールドに格納する場合、仮想マシンの現在のタイムゾーンで同等の日付/時刻を使用します
Mark Rotteveel 2012

@Kanagavelu Sugumar:はい。DateクラスのgetTimeメソッドは、1970年1月1日00:00:00GMTからのミリ秒数を返します。
ギルバートルブラン

@MarkRotteveel Mark「タイムゾーン情報なしでTIMESTAMPフィールドに保存すると、仮想マシンの現在のタイムゾーンで同等の日付/時刻が使用されます」というポイントが得られませんでした。答えを詳しく教えてください。それは私にとってもっと役立つでしょう。
Kanagavelu Sugumar 2012

回答:


105

setTimestamp(int parameterIndex, Timestamp x)ドライバーには明示的に指定されていませんが、setTimestamp(int parameterIndex, Timestamp x, Calendar cal)javadocによって確立されたルールに従う必要があります。

指定さjava.sql.TimestampれたCalendarオブジェクトを使用して、指定されたパラメータを指定された値に設定します。ドライバーはCalendarオブジェクトを使用してSQLTIMESTAMP値を作成し、SQL値をデータベースに送信します。ではCalendar、オブジェクト、ドライバーのアカウントにカスタムのタイムゾーンを取ってタイムスタンプを計算することができます。Calendarオブジェクトが指定されていない場合、ドライバーはデフォルトのタイムゾーンを使用します。これは、アプリケーションを実行している仮想マシンのタイムゾーンです。

setTimestamp(int parameterIndex, Timestamp x)JDBCドライバーを使用して呼び出すと、仮想マシンのタイムゾーンを使用して、そのタイムゾーンのタイムスタンプの日付と時刻が計算されます。この日付と時刻はデータベースに保存されるものであり、データベース列にタイムゾーン情報が保存されていない場合、ゾーンに関する情報はすべて失われます(つまり、データベースを使用するアプリケーションは、同じタイムゾーンを一貫して作成するか、タイムゾーンを識別するための別のスキームを考え出します(つまり、別の列に保存します)。

例:ローカルタイムゾーンはGMT +2です。「2012-12-2510:00:00UTC」を保存します。データベースに保存されている実際の値は「2012-12-2512:00:00」です。再度取得します。「2012-12-2510:00:00 UTC」として再度取得しますが(ただし、を使用して取得した場合のみgetTimestamp(..))、別のアプリケーションがタイムゾーンGMT + 0でデータベースにアクセスすると、タイムスタンプを「2012-12-2512:00:00UTC」として取得します。

別のタイムゾーンで保存するsetTimestamp(int parameterIndex, Timestamp x, Calendar cal)場合は、必要なタイムゾーンでカレンダーインスタンスとともにを使用する必要があります。値を取得するときは、同じタイムゾーンで同等のゲッターも使用するようにTIMESTAMPしてください(データベースでタイムゾーンなしの情報を使用する場合)。

したがって、実際のGMTタイムゾーンを保存する場合は、次を使用する必要があります。

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stmt.setTimestamp(11, tsSchedStartTime, cal);

JDBC 4.2では、準拠ドライバーはjava.time.LocalDateTime(およびjava.time.LocalTime)からTIMESTAMP(およびTIME)までをサポートする必要がありget/set/updateObjectます。java.time.Local*(あなたのコードは、特定の時間帯を想定した場合には、問題の新しいセットを開くかもしれませんが)何の変換が適用される必要がないので、クラスは、タイムゾーンなしです。


GMTで時間を保存したい。GMTのタイムスタンプを信じています。だから私は何をすべきですか?setTimestamp(int parameterIndex、Timestamp tzGmtObj、Calendar calGmtObj)??
Kanagavelu Sugumar 2012

「setTimestamp(intparameterIndex、Timestamp x)」が仮想マシンのタイムゾーンを使用する場合。コードにおけるxの役割は何ですか?
Kanagavelu Sugumar 2012

1
@KanagaveluSugumarこれは、設定する実際のタイムスタンプ値です。ドライバーは、タイムゾーン情報にのみCalendarオブジェクトを使用し、その値には使用しません。
Mark Rotteveel 2012

1
すばらしい、動作しています:)(Date / Timestamp).getTime()がタイムゾーン固有の時間をミリ秒単位で保持していることを証明するために多くの時間を費やしています。今ではそうではないことがわかりました。タイムゾーンに関係なく、常にUTCミリ秒が与えられます。ただし、(Date / Timestamp).toString()はタイムゾーン固有です。どうもありがとう。
Kanagavelu Sugumar 2012

4
toString()上のjava.lang.Date(とjava.lang.Timestamp)常にあなたの地域のタイムゾーンにそれを提示します。
Mark Rotteveel 2012

35

正解はjava.sql.Timestampはタイムゾーン固有ではないと思います。タイムスタンプは、java.util.Dateと個別のナノ秒値の複合です。このクラスにはタイムゾーン情報はありません。したがって、Dateと同じように、このクラスは1970年1月1日00:00:00 GMT + nanosからのミリ秒数を保持します。

PreparedStatement.setTimestamp(int parameterIndex、Timestamp x、Calendar cal)では、カレンダーはデフォルトのタイムゾーンを変更するためにドライバーによって使用されます。ただし、タイムスタンプはGMTでミリ秒を保持します。

APIは、JDBCドライバーがCalendarをどの程度正確に使用することになっているのかについて明確ではありません。プロバイダーはそれを解釈する方法について自由に感じているようです。たとえば、前回MySQL 5.5カレンダーを使用したとき、ドライバーはPreparedStatement.setTimestampとResultSet.getTimestampの両方でカレンダーを単に無視していました。


MySQL 5.5に関する関心のあるコメント
Alex

2
私はこれがjava8 +の時点で間違っていると信じています。タイムスタンプの内部(java.util.Dateから拡張されているため)にはcalendar属性があり、実際にはタイムゾーンを含むzoneinfo属性があります。タイムスタンプにもUTC時刻が格納されているため、.getTime()を使用してUTC時刻を取得できますが、タイムゾーン情報があります。
Jorge.V

1
つまり、GMTではなく1970年1月1日00:00:00UTCからのミリ秒数を保持します。GMTには夏時間があります。UNIXエポックは、1970年1月1日00:00UTCからのミリ秒です。
カービー

6

Mysqlの場合、制限があります。ではドライバMySQLのドキュメントは、我々は持っています:

以下は、MySQL Connector / Jのいくつかの既知の問題と制限です。Connector/ Jが結果セットのgetTimeStamp()メソッドを使用して夏時間(DST)切り替え日のタイムスタンプを取得する場合、戻り値の一部が間違っている可能性があります。データベースに接続するときに次の接続オプションを使用することで、エラーを回避できます。

useTimezone=true
useLegacyDatetimeCode=false
serverTimezone=UTC

したがって、このパラメーターを使用せずsetTimestamp or getTimestamp、カレンダーありまたはカレンダーなしで呼び出す場合、タイムスタンプはjvmタイムゾーンにあります。

例:

jvmのタイムゾーンはGMT + 2です。データベースには、タイムスタンプがあります:1461100256 = 19/04/16 21:10:56,000000000 GMT

Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "");
props.setProperty("useTimezone", "true");
props.setProperty("useLegacyDatetimeCode", "false");
props.setProperty("serverTimezone", "UTC");
Connection con = DriverManager.getConnection(conString, props);
......
Calendar nowGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Calendar nowGMTPlus4 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4"));
......
rs.getTimestamp("timestampColumn");//Oracle driver convert date to jvm timezone and Mysql convert date to GMT (specified in the parameter)
rs.getTimestamp("timestampColumn", nowGMT);//convert date to GMT 
rs.getTimestamp("timestampColumn", nowGMTPlus4);//convert date to GMT+4 timezone

最初のメソッドは次を返します:1461100256000 = 19/04 / 2016-211056 GMT

2番目のメソッドは次を返します:1461100256000 = 19/04 / 2016-211056 GMT

3番目のメソッドは次を返します:1461085856000 = 19/04 / 2016-171056 GMT

Oracleの代わりに、同じ呼び出しを使用すると、次のようになります。

最初のメソッドは次を返します:1461093056000 = 19/04 / 2016-191056 GMT

2番目のメソッドは次を返します:1461100256000 = 19/04 / 2016-211056 GMT

3番目のメソッドは次を返します:1461085856000 = 19/04 / 2016-171056 GMT

注意: Oracleのパラメータを指定する必要はありません。


1
Oracleの場合、セッションのタイムゾーンをUTCに設定して、MySQLがカレンダー以外の場合と同じ変換を実現できると思います。" ALTER SESSION SET TIME_ZONE = 'UTC'"。
eckes 2016年

5

それはあなたのドライバーから特定されています。使用するタイムゾーンを指定するために、Javaプログラムにパラメーターを指定する必要があります。

java -Duser.timezone="America/New_York" GetCurrentDateTimeZone

さらにこれ:

to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')

変換を適切に処理する上でも価値がある場合があります。ここから撮影


stackoverflow.com/questions/2858182/…をフォローするつもり ですか?それとも、JVMアプリケーションをGMTで実行するように設定しますか?
Kanagavelu Sugumar 2012

@KanagaveluSugumarよく依存します。すべてのクエリのタイムスタンプを変更する場合は、起動時に設定します。一部(すべて未満を意味する)のみを変更する場合は、参照した投稿に示されている方法で各クエリを変更します。
woot4Moo 2012

5

答えはそれjava.sql.Timestampが混乱であり、避けるべきであるということです。java.time.LocalDateTime代わりに使用してください。

それで、なぜそれは混乱しているのですか?java.sql.TimestampJavaDocから、ajava.sql.Timestampは「JDBCAPIがjava.util.DateこれをSQLTIMESTAMP値として識別できるようにする薄いラッパー」です。以下からjava.util.DateのJavaDoc、「Dateクラスは協定世界時(UTC)を反映することを意図しています」。ISO SQL仕様から、タイムゾーンのないタイムスタンプは「タイムゾーンのない日時であるデータ型です」。TIMESTAMPは、TIMESTAMP WITHOUT TIMEZONEの略称です。したがって、java.sql.TimestampSQL TIMESTAMPが「タイムゾーンなし」である間、UTCを「反映」します。

java.sql.TimestampUTCを反映しているため、そのメソッドは変換を適用します。これは混乱の終わりを引き起こしません。SQLの観点からは、タイムスタンプには変換元のタイムゾーンがないため、SQLタイムスタンプ値を他のタイムゾーンに変換することは意味がありません。42を華氏に変換するとはどういう意味ですか?42には温度単位がないため、意味がありません。それは単なる数字です。同様に、2020-07-22T10:30:00はどのタイムゾーンにも存在しないため、2020-07-22T10:38:00のタイムスタンプを南北アメリカ/ロサンゼルスに変換することはできません。UTCやGMTなどではありません。それは裸の日時です。

java.time.LocalDateTime裸の日時でもあります。SQL TIMESTAMPのように、タイムゾーンはありません。その方法のいずれも、その動作を予測および理解するのをはるかに容易にするいかなる種類のタイムゾーン変換も適用しません。したがって、は使用しないでくださいjava.sql.Timestamp。を使用しjava.time.LocalDateTimeます。

LocalDateTime ldt = rs.getObject(col, LocalDateTime.class);
ps.setObject(param, ldt, JDBCType.TIMESTAMP);

とてもよく言われました。ありがとうございました。
OleVV20年

2

以下の方法を使用して、目的のゾーン/ゾーンIDに固有のデータベースにタイムスタンプを保存できます。

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta")) ;
Timestamp timestamp = Timestamp.valueOf(zdt.toLocalDateTime());

よくある間違いはLocaleDateTime、その瞬間のタイムスタンプを取得するために使用することです。これにより、後で変換しようとしても、ゾーンに指定された情報が破棄されます。ゾーンを理解していません。

Timestampクラスのものであることに注意してくださいjava.sql.Timestamp


解決してくれてありがとう。機能した !!!
KarthikSA20年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.