7

オブジェクトがjava.util.Dateあり、それを MySQL の日時フィールドに形式で挿入する必要がありUTCます。

java.util.Date date = myDateFromSomewhereElse;
PreparedStatement prep = con.prepareStatement(
    "INSERT INTO table (t1, t2) VALUES (?,?)");

java.sql.Timestamp t = new Timestamp(date.getTime());
prep.setTimestamp(1, t, Calendar.getInstance(TimeZone.getTimeZone("PST"));
prep.setTimestamp(2, t, Calendar.getInstance(TimeZone.getTimeZone("UTC"));
System.out.println(prep.toString());

これにより、準備された SQL ステートメント文字列が得られます。

INSERT INTO table (t1, t2) VALUES ('2012-05-09 11:37:08','2012-05-09 11:37:08');

返されるタイムスタンプは、指定したタイムゾーンに関係なく同じタイムスタンプです。指定したタイムゾーンを持つ Calendar オブジェクトを無視しています。何が起こっていて、何が間違っていますか?

4

3 に答える 3

9

ジョーダン、実際あなたは正しい考えを持っていました。問題は、MySQL JDBCドライバーにバグがあり、Calendar引数がデフォルトで完全に無視されることです。PreparedStatementのソースコードを見て、実際に何が起こっているかを確認してください。

JVMのタイムゾーンを使用したタイムスタンプの形式であることに注意してください。これは、JVMがUTCタイムゾーンを使用している場合にのみ機能します。Calendarオブジェクトは完全に無視されます。

this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''", Locale.US);
timestampString = this.tsdf.format(x);

MySQLがCalendar引数を使用するには、次の接続オプションを使用して従来の日付/時刻コードを無効にする必要があります。

useLegacyDatetimeCode=false

したがって、次のようにデータベースに接続するときに使用できます。

String url = "jdbc:mysql://localhost/tz?useLegacyDatetimeCode=false"

上記の行を使用して従来の日時コードを無効にすると、ターゲットカレンダーのタイムゾーンでタイムスタンプがレンダリングされます。

if (targetCalendar != null) {
    targetCalendar.setTime(x);
    this.tsdf.setTimeZone(targetCalendar.getTimeZone());

     timestampString = this.tsdf.format(x);
} else {
    this.tsdf.setTimeZone(this.connection.getServerTimezoneTZ());
    timestampString = this.tsdf.format(x);
}

ここで何が起こっているかを確認するのは非常に簡単です。Calendarオブジェクトを渡すと、データをフォーマットするときにこれが使用されます。それ以外の場合は、データベースのタイムゾーンを使用してデータをフォーマットします。不思議なことに、カレンダーを渡すと、時刻も指定されたタイムスタンプ値に設定されます(これは無意味に思えます)。

于 2012-05-14T21:36:45.010 に答える
3

MySQLの説明については、このリンクを確認してください(Oracleに関するアドバイスをMySQLに適用しようとしないでください)。

TIMESTAMPデータ型は、日付と時刻の両方の部分を含む値に使用されます。TIMESTAMPの範囲は「1970-01-0100:00:01」UTCから「2038-01-1903:14:07」UTCです。

MySQLは、TIMESTAMP値を現在のタイムゾーンからUTCに変換して保存し、UTCから現在のタイムゾーンに戻して取得します。(これは、DATETIMEなどの他のタイプでは発生しません。)デフォルトでは、各接続の現在のタイムゾーンはサーバーの時刻です。

于 2012-05-09T19:36:04.330 に答える
2

TimeZones は、日付 (特定の時点) を表示するさまざまな方法です。ここに小さな例を書きました(アサートに細心の注意を払ってください):

// timezone independent date (usually interpreted by the timezone of 
// the default locale of the user machine)
Date now = new Date();

// now lets get explicit with how we wish to interpret the date
Calendar london =  Calendar.getInstance(TimeZone.getTimeZone("Europe/London"));
Calendar paris = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));

// now set the same date on two different calendar instance
london.setTime(now);
paris.setTime(now);

// the time is the same
assert london.getTimeInMillis() == paris.getTimeInMillis();

// London is interpreted one hour earlier than Paris (as of post date of 9th May 2012)
String londonTime = london.get(Calendar.HOUR) + ":" + london.get(Calendar.MINUTE);
String londonTZ = london.getTimeZone().getDisplayName(london.getTimeZone().inDaylightTime(london.getTime()), TimeZone.SHORT);
System.out.println(londonTime + " " + londonTZ);

// Paris is interpreted one hour later than Paris (as of post date of 9th May 2012)
String parisTime = paris.get(Calendar.HOUR) + ":" + paris.get(Calendar.MINUTE);
String parisTZ = paris.getTimeZone().getDisplayName(paris.getTimeZone().inDaylightTime(paris.getTime()), TimeZone.SHORT);
System.out.println(parisTime + " " + parisTZ);

このスニペットへの出力は次のとおりです (結果は実行日時によって異なります)。

8:18 BST
9:18 CEST

質問のスニペットは、保存されている日付に関して何もしていません。通常、データベースはネイティブ TimeZone 用に構成されます。日付を解釈するときに使用する TimeZone を表す追加のフィールドを保存することをお勧めします。

日付を変更することは (一般的には) 良い考えではありません (これは本質的に固定された時点の前後数ミリ秒です)。時間)。

またはこれ: http://puretech.paawak.com/2010/11/02/how-to-handle-oracle-timestamp-with-timezone-from-Java/

于 2012-05-09T19:25:35.140 に答える