Qual è il modo più raccomandato per archiviare il tempo in PostgreSQL usando Java?

Sto memorizzando due date nel database PostgreSQL. In primo luogo, sono i dati di visita di una pagina Web, e la seconda è la data dell’ultima modifica della pagina Web (questa operazione è lunga).

Ho qualche dubbio quale sia la migliore strategia per memorizzare questi valori.

Ho solo bisogno di giorno / mese / anno e ora: secondi e questo sarà solo per le proposte statistiche.

Quindi, alcuni dubbi:

  • è il miglior archivio per quanto tempo e convertire su recupero di informazioni o memorizzare nel formato dati sopra?
  • è meglio impostare la data di visita sul software o nell’inserimento nel database?
  • in Java, quali sono le migliori classi per gestire le date?

Qualsiasi strategia per la memorizzazione dei dati di data e ora in Postgresql dovrebbe basarsi su questi due punti:

  • La tua soluzione non dovrebbe mai dipendere dall’impostazione del fuso orario del server o del client.
  • Attualmente, Postgresql (come la maggior parte dei database) non ha un tipo di dati per memorizzare una data e ora complete con fuso orario. Pertanto, è necessario (concettualmente) decidere tra un tipo di dati Instant o LocalDateTime .

La mia ricetta per ogni scenario:


Se si desidera registrare l’ istante fisico in cui si verificava un evento particolare (in genere una creazione / modifica / eliminazione), (un vero ” timestamp “), quindi utilizzare:

  • Java: Instant (Java 8 o Jodatime).
  • JDBC: java.sql.Timestamp
  • Postgresql: TIMESTAMP WITH TIMEZONE ( TIMESTAMPTZ )

(Non lasciare che i tipi di dati peculiari di Postgresql WITH TIMEZONE / WITH TIMEZONE ti confondano: nessuno di loro memorizza in realtà un fuso orario)

Qualche codice boilerplan: quanto segue presuppone che ps sia un PreparedStatement , rs a ResultSet e tzUTC è un object Calendar statico corrispondente al fuso orario UTC .

 public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 

Scrivi Instant al database TIMESTAMPTZ :

 Instant instant = ...; Timestamp ts = instant != null ? new Timestamp(instant.toEpochMilli()) : null; ps.setTimestamp(col, ts, tzUTC); // column is TIMESTAMPTZ! 

Leggi Instant dal database TIMESTAMPTZ :

 Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ Instant inst = ts !=null ? Instant.ofEpochMilli(ts.getTime()) : null; 

Funziona in modo sicuro se il tuo tipo PG è TIMESTAMPTZ (in tal caso, il calendarUTC non ha alcun effetto in quel codice, ma è sempre consigliabile non dipendere dai fusi orari predefiniti). “Sicuro” significa che il risultato non dipenderà dal fuso orario del server o del database o dalle informazioni sui fusi orari: l’operazione è completamente reversibile e qualunque cosa accada alle impostazioni dei fusi orari, avrai sempre lo stesso “istante di tempo” che avevi in ​​origine Lato Java.


Se, invece di un timestamp (un istante sulla timeline fisica), si dispone di una data-ora “civile” locale (ovvero, l’insieme di campi {year-month-day hour:min:sec} ), è necessario utilizzare

  • Java: LocalDateTime (Java 8 o Jodatime).
  • JDBC: java.sql.Timestamp
  • Postgresql: TIMESTAMP WITHOUT TIMEZONE ( TIMESTAMP )

Leggi LocalDateTime dal database TIMESTAMP :

 Timestamp ts = rs.getTimestamp(col, tzUTC); // LocalDateTime localDt = null; if( ts != null ) localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC); 

Scrivi LocalDateTime nel database TIMESTAMP :

  Timestamp ts = null; if( localDt != null) ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC); ps.setTimestamp(colNum,ts, tzUTC); 

Ancora una volta, questa strategia è sicura e puoi dormire sonni tranquilli: se hai archiviato 2011-10-30 23:59:30 , recupererai sempre quei campi precisi (ora = 23, minuto = 59 … ecc.), Non importa cosa – anche se domani il fuso orario del tuo server Postgresql (o client) cambia, o la tua JVM o il tuo fuso orario del sistema operativo, o se il tuo paese modifica le sue regole dell’ora legale, ecc.


Aggiunto: Se vuoi (sembra un requisito naturale) archiviare la specifica datetime completa (un ZonedDatetime : il timestamp insieme al fuso orario, che include implicitamente anche le informazioni complete del datetime civile, più il fuso orario), allora sei fuori di fortuna : Postgresql non ha un tipo di dati per questo (né altri database, per quanto ne so). Devi inventare la tua memoria, magari in una coppia di campi: potrebbero essere i due tipi sopra (altamente ridondanti, ma efficienti per il recupero e il calcolo), o uno di essi più l’offset di tempo (perdi le informazioni sul fuso orario, alcuni calcoli diventano difficile, e alcuni imansible), o uno di loro più il fuso orario (come stringa, alcuni calcoli possono essere estremamente costosi).

Utilizzare java.util.Date nell’applicazione Java e timestamp with time zone nel database.

java.time

Non è bello ma questo è ciò che ha funzionato per me con un’istanza di ZonedDateTime utilizza il nuovo framework java.time in Java 8 e versioni successive ( Tutorial ):

 ZonedDateTime receivedTimestamp = some_ts_value; Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli()); ps.setTimestamp( 1, ts, Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone())) );