2012-03-22 7 views
7

Po wyszukiwaniu i czytaniu o tym, jak obchodzić się z datą i godziną do/z Javy i MySQL wciąż jestem zdezorientowany.Ciągle mylony z datownikami Java itp. Z MySQL

Powiedzmy, że utworzę obiekt java.util.Date. Ten obiekt zawiera czas w UTC. Dowolne formatowanie lub parsowanie w innych strefach czasowych może być wykonane za pomocą np. java.text.SimpleDateFormat.

Teraz chcę przechowywać mój obiekt daty w bazie danych MySQL w UTC. Ale kiedy używam metody setTimestamp() w java.sql.PreparedStatement, jestem nieco zdezorientowany. Poniżej przykładowy kod, na którym testuję zarówno MySQL DATETIME, jak i TIMESTAMP w mojej tabeli. Wstawiam również daty metodami setString() i 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(); 

Po uruchomieniu powyższego otrzymuję następujący wynik, zgodnie z oczekiwaniami.

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] 

Ale kiedy sprawdzić tabelę w bazie danych przy użyciu narzędzia wiersza poleceń MySQL uzyskać:

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) 

Pierwsza kolumna jest tylko typ TEXT gdzie przechowywać UTC sformatowaną datę.

W pierwszym wierszu zapisałem daty, używając metody setString().

W drugim rzędzie zapisałem datę przy użyciu metody setTimestamp(i,t). Domyślam się, że JDBC automatycznie konwertuje datę przy użyciu domyślnej strefy czasowej (którą ustawiłem na EST), zanim ją zapisze. Ale TIMESTAMP nie powinien zawsze automatycznie być przechowywany w UTC. Dokumentacja MySQL mówi: MySQL konwertuje wartości TIMESTAMP z aktualnej strefy czasowej na UTC do przechowywania i powraca z UTC do bieżącej strefy czasowej do pobierania. (Numer 1).

Wreszcie, dla trzeciego rzędu użyłem pst.setTimestamp(2, t, utc.getCalendar()); do przechowywania daty z nadzieją, że kierowca powinien korzystać ze strefy czasowej UTC. Ale najwyraźniej nie (kwestia 2).

mogę łatwo rozwiązać problem przechowywania daty UTC ustawiając domyślną strefę czasową na UTC. Ale nadal chciałbym zrozumieć, co się dzieje w przypadku dwóch powyższych kwestii.

+0

* "Powrót z UTC do bieżącej strefy czasowej do pobierania" * jest tym, co widzisz na wyjściu. –

+0

Dzięki, ale czy mógłbyś to rozwinąć? – Peter

+0

Znaczniki czasu są przechowywane wewnętrznie w UTC, ale gdy wyprowadzasz je za pomocą klienta 'mysql', są one wyświetlane w czasie lokalnym, tak jak mówi dokumentacja MySQL. To samo dotyczy wydania 2. Jaki jest problem? –

Odpowiedz

0

pola MySQL datetime są niesamowite raz owinąć głowę wokół nich.

"SimpleDateFormat", którego używasz, nie ma dołączonej strefy czasowej, więc twoja baza danych zakłada, że ​​wpisujesz datę w strefie czasowej twojego serwera. Jeśli znajdujesz się w strefie czasowej -0500, dodaje ona 5 godzin, aby przekonwertować ją na UTC, a następnie po jej pobraniu odejmuje 5 godzin, aby przekonwertować ją z powrotem na czas lokalny.

Kiedy przeliczone swój znacznik czasu UTC zanim go włożone, nadal nie dołączać strefę czasową na sznurku. Twoja baza danych wykonała na niej tę samą operację. 5 godzin zostało dodanych do 2012-03-22 13:49:51, więc twoja db przechowywana 2012-03-22 18:49:51 (nadal zakładając -0500).

Naprawdę zabawa częścią tego wszystkiego jest to, że można ustawić strefę czasową na połączenia z bazą danych, a wszystkie datetimes Ci pobrać lub wstawić przesunie wraz z połączeniem!

+0

Ale używam tylko SimpleDateFormat, aby utworzyć ciąg znaków, który wprowadzam do pola TEXT w bazie danych. Bezpośrednie wstawianie znacznika czasu do pól DATETIME lub TIMESTAMP nie używa SimpleDateFormat. – Peter

1

Wreszcie, jestem zrozumienia coś, ale stosując zamiast PostgreSQL.I w zasadzie powtarza się powyższy kod używając

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

i pisanie daty jako

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

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

Kiedy TIMESTAMP WITH TIMEZONE służy uzyskać następujące dane wyjściowe z 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) 

bo to śledząc strefa czasowa systemu (serwer), która wynosi CET (+01). Daty są poprawne, chociaż wyświetlane w CET.

Gdybym zamiast używać TIMESTAMP WITHOUT TIME ZONE uzyskać

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) 

aw pierwszym przypadku wykorzystuje domyślną strefę czasową (które ustawione na EST), a data jest naturalnie „źle”, podobnie jak w Przypadek MySQL. Jednak w drugim przypadku używa UTC, ponieważ przekazałem to jako argument do metody setTimestamp() i właśnie to próbowałem zrobić w MySQL. Wydaje mi się, że sterownik java MySQL po prostu ignoruje argument Calendar w setTimestamp(). Może coś przeoczyłem.

Powiązane problemy