2012-03-22 14 views
7

JavaとMySQLの日付と時刻の処理方法を検索して読んだ後、私はまだ混乱しています。まだJavaのタイムスタンプなどでMySQLと混同されています

私はjava.util.Dateオブジェクトを作成します。そのオブジェクトはUTCで時刻を保持します。他の時間帯へのフォーマットまたは構文解析は、例えば、 java.text.SimpleDateFormat

今、私の日付オブジェクトをUTCのMySQLデータベースに保存したいと思います。しかし、java.sql.PreparedStatementsetTimestamp()メソッドを使用すると、ちょっと混乱します。ここでいくつかのサンプルコードに従って、MySQL DATETIMEとTIMESTAMPの両方を自分のテーブルにテストします。また、setString()setTimestamp()の両方の方法で日付を挿入します。

java.sql.Connection conn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost/test","user","password"); 
java.sql.Statement st = conn.createStatement(); 

String q = "DROP TABLE IF EXISTS tmp"; 
st.execute(q); 
q = "CREATE TABLE tmp (dt_string TEXT, dt DATETIME, ts TIMESTAMP)"; 
st.execute(q); 

java.sql.PreparedStatement pst = conn.prepareStatement("INSERT INTO tmp SET dt_string=?, dt=?, ts=?"); 

java.text.SimpleDateFormat utc = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
utc.setTimeZone(java.util.TimeZone.getTimeZone("UTC")); 

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("EST")); 

System.out.println("Default time zone: " + java.util.TimeZone.getDefault().getID()); 
java.util.Date d = new java.util.Date(); 
System.out.println("A date: " + d); 
java.sql.Timestamp t = new java.sql.Timestamp(d.getTime()); 
System.out.println("The timestamp: " + t); 


pst.setString(1, utc.format(d)); 
pst.setString(2, utc.format(d)); 
pst.setString(3, utc.format(t)); 
pst.execute(); 

pst.setTimestamp(2, t); 
pst.setTimestamp(3, t); 
pst.execute(); 

System.out.println("Use calendar: " + utc.getCalendar().getTimeZone()); 
pst.setTimestamp(2, t, utc.getCalendar()); 
pst.setTimestamp(3, t, utc.getCalendar()); 
pst.execute(); 

conn.close(); 

上記を実行すると、次のような結果が得られます。

Default time zone: EST 
A date: Thu Mar 22 08:49:51 EST 2012 
The timestamp: 2012-03-22 08:49:51.784 
Use calendar: sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null] 

しかし、私は私が手MySQLのコマンドラインツールを使用してデータベース内のテーブルを調べるとき:

mysql> select * from tmp; 
+---------------------+---------------------+---------------------+ 
| dt_string   | dt     | ts     | 
+---------------------+---------------------+---------------------+ 
| 2012-03-22 13:49:51 | 2012-03-22 13:49:51 | 2012-03-22 13:49:51 | 
| 2012-03-22 13:49:51 | 2012-03-22 08:49:51 | 2012-03-22 08:49:51 | 
| 2012-03-22 13:49:51 | 2012-03-22 08:49:51 | 2012-03-22 08:49:51 | 
+---------------------+---------------------+---------------------+ 
3 rows in set (0.00 sec) 

を最初の列は、私はUTCフォーマットされた日付を保存するだけでTEXTタイプです。

最初の行には、setString()メソッドを使用して日付が格納されていました。

2番目の行には、setTimestamp(i,t)メソッドを使用して日付が格納されていました。 JDBCは自動的に日付をデフォルトのタイムゾーン(ESTに設定)を使用して自動的に変換してから保存していると思います。しかし、TIMESTAMPは常に自動的にUTCに格納されるべきではありません。 MySQLのドキュメントによれば、は、TIMESTAMPの値を現在のタイムゾーンからUTCに変換してUTCから現在のタイムゾーンに戻して取得します。(Issue 1)

最後に、3行目では、ドライバーがUTCタイムゾーンを使用することを期待して、pst.setTimestamp(2, t, utc.getCalendar());を使用して日付を保存しました。しかし明らかにそうではありませんでした(問題2)。

デフォルトのタイムゾーンをUTCに設定することで、日付をUTCに保存する問題を簡単に修正できます。しかし、私は上記の2つの問題について何が起こっているのかを理解したいと思います。

+0

* "UTCから現在のタイムゾーンに戻って検索する" *は出力に表示されます。 –

+0

ありがとうございますが、これを拡張できますか? – Peter

+0

タイムスタンプは内部的にはUTCに保存されますが、mysqlクライアントを使用して出力すると、現地時間にMySQLドキュメントが示すように表示されます。問題2と同じです。問題は何ですか? –

答えて

0

MySql datetimeフィールドは、一度頭を囲むとすばらしいです。

使用している「SimpleDateFormat」にはタイムゾーンが設定されていないため、データベースではサーバーのタイムゾーンに日付を入力していることを前提としています。 -0500時間帯にいる場合は、UTCに変換するのに5時間を追加し、フェッチすると5時間を減算して現地時間に戻します。

タイムスタンプを挿入する前にタイムスタンプをutcに変換しても、タイムゾーンを文字列に追加することはありません。あなたのデータベースは同じ操作を実行しました。 5時間が2012-03-22 13:49:51に追加されましたので、2012-03-22 18:49:51に保存されます(まだ-0500と仮定)。

これの本当に楽しい部分は、データベース接続でタイムゾーンを設定できることです。フェッチまたは挿入するすべての日付時刻は、接続とともにシフトします。

+0

しかし、私はSimpleDateFormatを使用して、データベースのTEXTフィールドに入力する文字列を作成します。 DATETIMEまたはTIMESTAMPフィールドにタイムスタンプを直接挿入すると、SimpleDateFormatは使用されません。 – Peter

1

最後に、私は何かを理解していますが、代わりにPostgreSQLを使用しています。TIMESTAMP WITH TIMEZONEは、それがの追跡することですので、私はpsql

postgres=# select * from tmp; 
     ts_string  |    ts    
-------------------------+---------------------------- 
2012-03-23 12:48:28.057 | 2012-03-23 13:48:28.057+01 
2012-03-23 12:48:28.057 | 2012-03-23 13:48:28.057+01 
(2 rows) 

から次の出力を取得する使用されているとき、私は基本的に

Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost"); 

st.execute("CREATE TABLE tmp (ts_string TEXT, ts TIMESTAMP WITH TIME ZONE)"); 
//st.execute("CREATE TABLE tmp (ts_string TEXT, ts TIMESTAMP WITHOUT TIME ZONE)"); 

を使用して

pst.setString(1, utc.format(d)); 
pst.setTimestamp(2, t); 
pst.execute(); 

pst.setTimestamp(2, t, utc.getCalendar()); 
pst.execute(); 

と日付を書いて上記のコードを繰り返し、システム(サーバー)のタイムゾーンはCET(+01)です。日付はCETに表示されますが、正しいです。

私が代わりにTIMESTAMP WITHOUT TIME ZONEを使用している場合は、私が

postgres=# select * from tmp; 
     ts_string  |   ts   
-------------------------+------------------------ 
2012-03-23 12:49:04.120 | 2012-03-23 07:49:04.12 
2012-03-23 12:49:04.120 | 2012-03-23 12:49:04.12 
(2 rows) 

だけのように、それは(私がESTに設定)デフォルトのタイムゾーンを使用し、日付が自然に「間違った」である最初のケースで取得MySQLの場合。しかし、2番目のケースでは、setTimestamp()メソッドへの引数として渡したのでUTCを使用しています。これがMySQLでやろうとしたものです。私にとっては、MySQLのjavaドライバはCalendar引数をただ無視しているようだ。setTimestamp()。たぶん私は何かを逃した。

関連する問題