2012-03-22 65 views
7

在搜索和閱讀如何處理來自Java和MySQL的日期和時間後,我仍然感到困惑。仍然困惑於Java時間戳等與MySQL

比方說,我創建了一個java.util.Date對象。該對象以UTC保存時間。任何格式化或解析到其他時區都可以用例如java.text.SimpleDateFormat

現在我想將我的日期對象以UTC存儲到MySQL數據庫中。但是當我在java.sql.PreparedStatement中使用setTimestamp()方法時,我有點困惑。這裏有一些示例代碼,我在表中測試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()方法存儲日期。

在第二行中,我使用setTimestamp(i,t)方法存儲日期。我猜JDBC在存儲它之前會自動使用默認時區(我設置爲EST)轉換日期。但是不應該總是自動地以UTC存儲TIMESTAMP。 MySQL文檔說MySQL將TIMESTAMP值從當前時區轉換爲UTC存儲,並從UTC返回到當前時區以供檢索。(問題1)。

最後,對於第三行,我使用了pst.setTimestamp(2, t, utc.getCalendar());來存儲日期,希望驅動程序應該使用UTC時區。但顯然不是(問題2)。

我可以通過將默認時區設置爲UTC來輕鬆修復問題以便以UTC存儲日期。但是,我仍然想知道上述兩個問題正在發生什麼。

+0

*「從UTC返回到當前時區進行檢索」*是您在輸出中看到的內容。 – 2012-03-22 18:52:52

+0

謝謝,但你能擴展嗎? – Peter 2012-03-22 19:01:03

+0

時間戳存儲在UTC內部,但是當您使用'mysql'客戶端輸出它們時,它們會在當地時間呈現給您,就像MySQL文檔告訴您的那樣。問題2也一樣。這裏有什麼問題? – 2012-03-22 19:01:54

回答

0

MySql日期時間字段是真棒,一旦你圍繞他們的頭。

您正在使用的「SimpleDateFormat」沒有附加時區,因此您的數據庫假定您在服務器的時區中輸入日期。如果您處於-0500時區,則將其轉換爲UTC時間爲5小時,然後當您獲取它時,它將減去5小時將其轉換回當地時間。

在插入它之前將時間戳轉換爲utc時,您仍然沒有將時區加到字符串上。您的數據庫對其執行相同的操作。 2012-03-22 13:49:51加了5個小時,所以你的db存儲2012-03-22 18:49:51(仍然假設-0500)。

所有這一切的真正有趣的部分是,您可以在數據庫連接上設置時區,並且您獲取或插入的所有日期時間將隨着連接一起移動!

+0

但我只使用SimpleDateFormat創建一個字符串,我輸入數據庫中的TEXT字段。 Timestamp直接插入到DATETIME或TIMESTAMP字段中不使用SimpleDateFormat。 – Peter 2012-03-23 08:30:36

1

最後,我理解的東西,但使用PostgreSQL來代替。我基本上重複上面使用

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(); 

代碼當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) 

,因爲它跟蹤的系統(服務器)時區,即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) 

,並在第一種情況下,它使用的默認時區(我設置爲美國東部時間),以及日期自然是「錯誤的」,就像在MySQL案例。但是,在第二種情況下,它使用UTC,因爲我將它作爲參數傳遞給setTimestamp()方法,這是我在MySQL中試圖做的。對我而言,MySQL java驅動程序似乎忽略setTimestamp()中的Calendar參數。也許我錯過了什麼。