sql >> Databáze >  >> RDS >> PostgreSQL

Jaký je nejvíce doporučený způsob ukládání času v PostgreSQL pomocí Javy?

Jakákoli strategie pro ukládání dat data a času v PostgreSQL by se měla, IMO, spoléhat na tyto dva body:

  • Vaše řešení by nemělo nikdy závisí na nastavení časového pásma serveru nebo klienta.
  • V současné době PostgreSQL (jako většina databází) nemá datový typ pro uložení úplného datum a čas s časovým pásmem. Musíte se tedy rozhodnout mezi Instant nebo LocalDateTime datový typ.

Můj recept následuje.

Pokud chcete zaznamenat fyzické okamžité kdy došlo k určité události (skutečné „časové razítko " , obvykle nějaká událost vytvoření/úpravy/smazání), pak použijte:

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

(Nenechte PostgreSQL zvláštní datové typy WITH TIMEZONE /WITHOUT TIMEZONE zmást vás:žádný z nich ve skutečnosti neukládá časové pásmo)

Nějaký standardní kód:následující předpokládá, že ps je PreparedStatement , rs ResultSet a tzUTC je statický Calendar objekt odpovídající UTC časové pásmo.

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

Napište Instant do databáze TIMESTAMPTZ :

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

Přečtěte si Instant z databáze TIMESTAMPTZ :

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

Toto funguje bezpečně, pokud je váš typ PG TIMESTAMPTZ (V takovém případě calendarUTC nemá na tento kód žádný vliv; ale vždy je vhodné nezáviset na výchozích časových pásmech). "Bezpečně" znamená, že výsledek nezávisí na časovém pásmu serveru nebo databáze , neboli informace o časových pásmech:operace je plně vratná a ať se stane s nastavením časových pásem cokoli, vždy získáte stejný „okamžitý okamžik“, jaký jste původně měli na straně Java.

Pokud místo časového razítka (okamžik na fyzické časové ose) máte co do činění s "civilním" místním datem a časem (tj. sada polí {year-month-day hour:min:sec(:msecs)} ), použijete:

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

Přečtěte si LocalDateTime z databáze TIMESTAMP :

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

Napište LocalDateTime do databáze TIMESTAMP :

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

Opět platí, že tato strategie je bezpečná a můžete klidně spát:pokud jste uložili 2011-10-30 23:59:30 , získáte tato přesná pole (hodina=23, minuta=59... atd.) vždy, bez ohledu na to, co se děje – i když se zítra změní časové pásmo vašeho Postgresql serveru (nebo klienta) nebo vaše JVM nebo časové pásmo vašeho OS, nebo pokud vaše země upraví svá pravidla DST atd.

Přidáno:Pokud chcete (zdá se, že je to přirozený požadavek) uložit úplnou specifikaci data a času (ZonedDatetime :časové razítko spolu s časovou zónou, která implicitně zahrnuje i úplné civilní datum a čas - plus časové pásmo)... pak pro vás mám špatnou zprávu:PostgreSQL pro to nemá datový typ (ani jiné databáze, pokud vím) . Musíte si vymyslet vlastní úložiště, možná ve dvojici polí:mohly by to být dva výše uvedené typy (vysoce redundantní, i když efektivní pro vyhledávání a výpočty), nebo jeden z nich plus časový posun (ztratíte informace o časovém pásmu, některé výpočty se stanou obtížné a některé nemožné), nebo jeden z nich plus časové pásmo (jako řetězec; některé výpočty mohou být extrémně nákladné).



  1. Křížová tabulka s velkým nebo nedefinovaným počtem kategorií

  2. SQLite - Export dat do souboru CSV

  3. Jak najít duplicitní záznamy v PostgreSQL

  4. Jak mohu dotazovat hodnotu ve sloupci SQL Server XML