web-dev-qa-db-ja.com

Java.sql.Timestampタイムゾーン固有ですか?

UTC dateTimeをDBに保存する必要があります。
特定のタイムゾーンで指定されたdateTimeをUTCに変換しました。そのため、以下のコードに従いました。
入力日時は「20121225 10:00:00 Z」タイムゾーンは「アジア/カルカッタ」です
私のサーバー/ DB(Oracle)は同じタイムゾーン(IST)「アジア/カルカッタ」で実行されています

この特定のタイムゾーンで日付オブジェクトを取得

        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)は、UTCではなく同じdateTime: "20121225 10:00:00を保存しています。

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

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

DBサーバーも同じタイムゾーン「Asia/Calcutta」で実行されています

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

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

もう1つの質問:

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

73

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

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

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

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

別のタイムゾーンに保存する場合は、必要なタイムゾーンのCalendarインスタンスで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を介してTIMESTAMP(およびTIME)に対してJava.time.LocalTime(およびget/set/updateObject)をサポートする必要があります。 Java.time.Local*クラスにはタイムゾーンがないため、変換を適用する必要はありません(ただし、コードが特定のタイムゾーンを想定している場合、新しい問題が発生する可能性があります)。

95
Mark Rotteveel

正しい答えは、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ドライバーがカレンダーをどの程度正確に使用するかについて明確ではありません。プロバイダーは、それを解釈する方法について自由に感じているようです。前回MySQL 5.5カレンダーで作業したとき、ドライバーはPreparedStatement.setTimestampとResultSet.getTimestampの両方でカレンダーを単に無視しました。

29

それはあなたのドライバーに固有のものです。 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')

変換を適切に処理することにも価値があります。 here から取得

5
Woot4Moo

Mysqlには、制限があります。 driver Mysql doc には、次のものがあります。

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-21:10:56 GMT

2番目のメソッドは以下を返します:1461100256000 = 19/04/2016-21:10:56 GMT

3番目のメソッドは以下を返します:1461085856000 = 19/04/2016-17:10:56 GMT

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

最初のメソッドは以下を返します:1461093056000 = 19/04/2016-19:10:56 GMT

2番目のメソッドは以下を返します:1461100256000 = 19/04/2016-21:10:56 GMT

3番目のメソッドは以下を返します:1461085856000 = 19/04/2016-17:10:56 GMT

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

5
Abdelhafid