2012-03-22 21 views
7

Sau khi tìm kiếm và đọc về cách xử lý ngày và giờ đến/từ Java và MySQL, tôi vẫn còn bối rối.Vẫn còn bị nhầm lẫn bởi Java Timestamps vv với MySQL

Cho phép nói rằng tôi tạo đối tượng java.util.Date. Đối tượng đó giữ thời gian trong UTC. Bất kỳ định dạng hoặc phân tích cú pháp nào vào các múi giờ khác đều có thể được thực hiện với ví dụ: java.text.SimpleDateFormat.

Bây giờ tôi muốn lưu trữ đối tượng ngày tháng của tôi vào cơ sở dữ liệu MySQL trong UTC. Nhưng khi tôi sử dụng phương pháp setTimestamp() trong java.sql.PreparedStatement Tôi thấy hơi bối rối. Dưới đây là một số mã mẫu nơi tôi thử nghiệm cả hai DATETIME MySQL và TIMESTAMP trong bảng của tôi. Tôi cũng chèn ngày với cả hai phương thức 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(); 

Khi tôi chạy ở trên, tôi nhận được kết quả như mong đợi.

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] 

Nhưng khi tôi kiểm tra bảng trong cơ sở dữ liệu bằng cách sử dụng công cụ dòng lệnh MySQL tôi nhận được:

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) 

Cột đầu tiên chỉ là một loại TEXT nơi tôi lưu trữ các UTC ngày định dạng.

Trong hàng đầu tiên, tôi đã lưu trữ ngày bằng phương pháp setString().

Trong hàng thứ hai, tôi đã lưu ngày bằng phương pháp setTimestamp(i,t). Tôi đoán rằng JDBC sẽ tự động chuyển đổi ngày sử dụng múi giờ mặc định (mà tôi đặt thành EST) trước khi nó lưu trữ nó. Nhưng không nên TIMESTAMP luôn tự động được lưu trữ trong UTC. Tài liệu MySQL cho biết MySQL chuyển đổi giá trị TIMESTAMP từ múi giờ hiện tại sang UTC để lưu trữ và ngược lại từ UTC thành múi giờ hiện tại để truy xuất. (Số phát hành 1).

Cuối cùng, đối với hàng thứ ba, tôi đã sử dụng pst.setTimestamp(2, t, utc.getCalendar()); để lưu ngày với hy vọng người lái xe nên sử dụng múi giờ UTC. Nhưng dường như không (Vấn đề 2).

Tôi có thể dễ dàng khắc phục vấn đề để lưu trữ ngày trong UTC bằng cách đặt múi giờ mặc định thành UTC. Tuy nhiên, tôi vẫn muốn hiểu những gì đang xảy ra cho hai vấn đề trên.

+0

* "quay lại từ UTC đến múi giờ hiện tại để truy xuất" * là những gì bạn thấy ở đầu ra. –

+0

Cảm ơn bạn, nhưng bạn có thể mở rộng về điều này không? – Peter

+0

Dấu thời gian được lưu trữ trong UTC nội bộ, nhưng khi bạn xuất chúng bằng cách sử dụng trình khách 'mysql', chúng được hiển thị cho bạn theo giờ địa phương, như tài liệu MySQL cho bạn biết. Tương tự cho vấn đề 2. Vấn đề ở đây là gì? –

Trả lời

0

Trường ngày giờ MySql thật tuyệt vời khi bạn quấn đầu quanh chúng.

"SimpleDateFormat" bạn đang sử dụng không có múi giờ được gắn với nó, vì vậy cơ sở dữ liệu của bạn giả định rằng bạn đang nhập ngày trong múi giờ của máy chủ. Nếu bạn đang ở trong một múi giờ -0500, nó thêm 5 giờ để nó chuyển đổi nó sang UTC, và sau đó khi bạn lấy nó, nó trừ đi 5 giờ để chuyển đổi nó trở lại thời gian địa phương của bạn.

Khi bạn chuyển đổi dấu thời gian thành utc trước khi chèn, bạn vẫn không gắn múi giờ vào chuỗi. Cơ sở dữ liệu của bạn đã thực hiện cùng một thao tác trên nó. 5 giờ đã được thêm vào 2012-03-22 13:49:51, do đó, db của bạn được lưu trữ 2012-03-22 18:49:51 (vẫn giả định -0500).

Phần thực sự thú vị của tất cả điều này là bạn có thể đặt múi giờ trên kết nối cơ sở dữ liệu và tất cả thời gian bạn nạp hoặc chèn sẽ thay đổi cùng với kết nối của bạn!

+0

Nhưng tôi chỉ sử dụng SimpleDateFormat để tạo chuỗi mà tôi nhập vào trường TEXT trong cơ sở dữ liệu. Chèn trực tiếp Dấu thời gian vào các trường DATETIME hoặc TIMESTAMP không sử dụng SimpleDateFormat. – Peter

1

Cuối cùng, tôi hiểu điều gì đó, nhưng thay vì sử dụng PostgreSQL.Tôi về cơ bản lặp đi lặp lại các mã trên sử dụng

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

và viết ngày tháng như

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

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

Khi TIMESTAMP WITH TIMEZONE được sử dụng tôi nhận được đầu ra sau đây từ 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) 

vì nó theo dõi các múi giờ hệ thống (máy chủ), là CET (+01). Ngày tháng chính xác mặc dù được hiển thị trong CET.

Nếu tôi thay vì sử dụng TIMESTAMP WITHOUT TIME ZONE tôi nhận được

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) 

và trong trường hợp đầu tiên nó sử dụng múi giờ mặc định (mà tôi thiết lập để EST), và ngày là một cách tự nhiên "sai", cũng giống như trong các Trường hợp MySQL. Tuy nhiên, trong trường hợp thứ hai nó sử dụng UTC bởi vì tôi đã thông qua đó như là một đối số cho phương pháp setTimestamp(), và đây là những gì tôi đã cố gắng làm trong MySQL. Với tôi, có vẻ như trình điều khiển java MySQL chỉ bỏ qua đối số Calendar trong setTimestamp(). Có lẽ tôi đã bỏ lỡ một cái gì đó.