2012-03-22 10 views
7

Dopo aver cercato e letto come gestire data e ora da/per Java e MySQL, sono ancora confuso.Ancora confuso da Java Timestamps ecc. Con MySQL

Diciamo che creo un oggetto java.util.Date. Questo oggetto contiene il tempo in UTC. Qualsiasi formattazione o analisi in altri fusi orari può essere effettuata con ad es. java.text.SimpleDateFormat.

Ora voglio memorizzare il mio oggetto data nel database MySQL in UTC. Ma quando uso il metodo setTimestamp() in java.sql.PreparedStatement mi sento un po 'confuso. Segue un esempio di codice in cui collaudo sia MySQL DATETIME che TIMESTAMP nella mia tabella. Inserisco anche le date con entrambi i metodi setString() e 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(); 

Quando corro quanto sopra, ottengo il seguente output che è come previsto.

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] 

Ma quando ho ispezionare la tabella nel database utilizzando lo strumento a riga di comando MySQL ottengo:

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) 

La prima colonna è solo un tipo di testo in cui devo conservare l'UTC data formattata.

Nella prima riga, ho memorizzato le date utilizzando il metodo setString().

Nella seconda riga ho memorizzato la data utilizzando il metodo setTimestamp(i,t). Immagino che JDBC converta automaticamente la data usando il fuso orario predefinito (che ho impostato su EST) prima di memorizzarlo. Ma TIMESTAMP non dovrebbe essere sempre automaticamente archiviato in UTC. La documentazione di MySQL dice MySQL converte i valori TIMESTAMP dal fuso orario corrente a UTC per l'archiviazione e viceversa da UTC al fuso orario corrente per il recupero. (numero 1).

Infine, per la terza riga ho utilizzato lo pst.setTimestamp(2, t, utc.getCalendar()); per memorizzare la data con la speranza che il driver debba utilizzare il fuso orario UTC. Ma apparentemente no (numero 2).

Posso facilmente risolvere il problema per memorizzare le date in UTC impostando il fuso orario predefinito su UTC. Ma ancora, mi piacerebbe capire cosa sta succedendo per le due questioni di cui sopra.

+0

* "ritorno da UTC al fuso orario corrente per il recupero" * è ciò che si vede lì nell'output. –

+0

Grazie, ma potresti approfondire questo? – Peter

+0

I timestamp sono memorizzati internamente in UTC, ma quando li si stampa usando il client 'mysql', vengono presentati nell'ora locale, come la documentazione di MySQL dice. Lo stesso per il numero 2. Qual è il problema qui? –

risposta

0

I campi MySQl datetime sono fantastici una volta avvolti nella loro mente.

il "SimpleDateFormat" che si sta utilizzando non ha un fuso orario ad esso collegato, quindi il database presume che si stia inserendo una data nel fuso orario del proprio server. Se ci si trova in un fuso orario di -0500, aggiunge 5 ore per convertirlo in UTC, quindi quando lo si preleva, sottrae le 5 ore per convertirlo all'ora locale.

Quando hai convertito il timestamp in utc prima di inserirlo, non hai ancora aggiunto un fuso orario alla stringa. Il tuo database ha eseguito la stessa operazione su di esso. 5 ore sono state aggiunte a 2012-03-22 13:49:51, quindi il tuo db è stato archiviato 2012-03-22 18:49:51 (presupponendo ancora -0500).

La parte davvero divertente di tutto questo è che è possibile impostare il fuso orario su una connessione al database e tutti i tempi di recupero o inserimento passeranno insieme alla connessione!

+0

Ma io uso solo SimpleDateFormat per creare una stringa che inserisco nel campo TESTO nel database. Gli inserimenti diretti del Timestamp nei campi DATETIME o TIMESTAMP non utilizzano SimpleDateFormat. – Peter

1

Infine, sto capendo qualcosa, ma utilizzando PostgreSQL invece.Io fondamentalmente ripetuto il codice di cui sopra utilizzando

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

e scrivere le date come

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

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

Quando TIMESTAMP WITH TIMEZONE viene utilizzato ottengo il seguente output da 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) 

perché è tenere traccia del fuso orario del sistema (server), che è CET (+01). Le date sono corrette anche se visualizzate in CET.

Se io invece uso TIMESTAMP WITHOUT TIME ZONE ottengo

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) 

e nel primo caso si utilizza il fuso orario predefinito (che ho impostato a EST), e la data è naturalmente "sbagliato", proprio come nel Caso MySQL. Tuttavia, nel secondo caso usa UTC perché l'ho passato come argomento al metodo setTimestamp(), e questo è quello che ho cercato di fare in MySQL. Per me, sembra che il driver java MySQL ignora semplicemente l'argomento Calendar in setTimestamp(). Forse mi sono perso qualcosa.