Każda strategia przechowywania danych daty i czasu w PostgreSQL powinna, IMO, opierać się na tych dwóch punktach:
- Twoje rozwiązanie powinno nigdy zależy od ustawienia strefy czasowej serwera lub klienta.
- Obecnie PostgreSQL (jak większość baz danych) nie ma typu danych do przechowywania pełnego data i godzina ze strefą czasową. Musisz więc wybrać między
Instant
lubLocalDateTime
typ danych.
Mój przepis jest zgodny.
Jeśli chcesz nagrać fizyczne natychmiastowe kiedy miało miejsce określone wydarzenie (prawdziwa „sygnatura czasowa ”, zazwyczaj jakieś zdarzenie tworzenia/modyfikacji/usuwania), a następnie użyj:
- Java:
Instant
(Java 8 lub Jodatime). - JDBC:
java.sql.Timestamp
- PostgreSQL:
TIMESTAMP WITH TIMEZONE
(TIMESTAMPTZ
)
(Nie zezwalaj na specyficzne typy danych PostgreSQL WITH TIMEZONE
/WITHOUT TIMEZONE
mylić:żaden z nich nie przechowuje strefy czasowej)
Jakiś standardowy kod:poniżej zakłada się, że ps
jest PreparedStatement
, rs
ResultSet
i tzUTC
jest statycznym Calendar
obiekt odpowiadający UTC
strefa czasowa.
public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Napisz Instant
do bazy danych TIMESTAMPTZ
:
Instant instant = ...;
Timestamp ts = instant != null ? Timestamp.from(instant) : null;
ps.setTimestamp(col, ts, tzUTC); // column is TIMESTAMPTZ!
Przeczytaj Instant
z bazy danych TIMESTAMPTZ
:
Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? ts.toInstant() : null;
Działa to bezpiecznie, jeśli Twój typ PG to TIMESTAMPTZ
(W takim przypadku calendarUTC
nie ma wpływu na ten kod; ale zawsze zaleca się, aby nie polegać na domyślnych strefach czasowych). „Bezpiecznie” oznacza, że wynik nie będzie zależał od strefy czasowej serwera lub bazy danych , lub informacje o strefach czasowych:operacja jest w pełni odwracalna i cokolwiek stanie się z ustawieniami stref czasowych, zawsze uzyskasz ten sam „chwilowy moment”, który miałeś pierwotnie po stronie Java.
Jeśli zamiast znacznika czasu (chwila na fizycznej osi czasu) masz do czynienia z "cywilną" lokalną datą i godziną (czyli zestaw pól {year-month-day hour:min:sec(:msecs)}
), użyjesz:
- Java:
LocalDateTime
(Java 8 lub Jodatime). - JDBC:
java.sql.Timestamp
- PostgreSQL:
TIMESTAMP WITHOUT TIMEZONE
(TIMESTAMP
)
Przeczytaj LocalDateTime
z bazy danych TIMESTAMP
:
Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null )
localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
Napisz LocalDateTime
do bazy danych TIMESTAMP
:
Timestamp ts = null;
if( localDt != null)
ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
ps.setTimestamp(colNum,ts, tzUTC);
Ponownie, ta strategia jest bezpieczna i możesz spać spokojnie:jeśli zapisałeś 2011-10-30 23:59:30
, będziesz pobierać te dokładne pola (godzina=23, minuta=59... itd.) zawsze, bez względu na wszystko — nawet jeśli jutro zmieni się strefa czasowa serwera (lub klienta) Postgresql, strefa czasowa JVM lub systemu operacyjnego, lub jeśli twój kraj modyfikuje zasady czasu letniego itp.
Dodano:Jeśli chcesz (wydaje się to naturalnym wymogiem) przechowywać pełną specyfikację daty i godziny (ZonedDatetime
:znacznik czasu wraz ze strefą czasową, która domyślnie zawiera również pełną cywilną informację o dacie i godzinie - plus strefę czasową)... to mam dla ciebie złą wiadomość:PostgreSQL nie ma dla tego typu danych (ani inne bazy danych, o ile mi wiadomo) . Musisz wymyślić własne miejsce do przechowywania, być może w parze pól:mogą to być dwa powyższe typy (wysoce nadmiarowe, ale wydajne do wyszukiwania i obliczania) lub jeden z nich plus przesunięcie czasowe (tracisz informacje o strefie czasowej, niektóre obliczenia stają się trudne, a niektóre niemożliwe) lub jeden z nich plus strefa czasowa (jako ciąg; niektóre obliczenia mogą być niezwykle kosztowne).