Qualquer estratégia para armazenar dados de data e hora no PostgreSQL deve, IMO, contar com estes dois pontos:
- Sua solução deve nunca dependem da configuração de fuso horário do servidor ou cliente.
- Atualmente, o PostgreSQL (como a maioria dos bancos de dados) não possui um tipo de dados para armazenar um arquivo completo data e hora com fuso horário. Então, você precisa decidir entre um
Instantou umLocalDateTimetipo de dados.
Minha receita segue.
Se você quiser gravar o instantâneo físico quando um determinado evento ocorreu, (um verdadeiro "timestamp " , normalmente algum evento de criação/modificação/exclusão), então use:
- Java:
Instant(Java 8 ou Jodatime). - JDBC:
java.sql.Timestamp - PostgreSQL:
TIMESTAMP WITH TIMEZONE(TIMESTAMPTZ)
(Não deixe que tipos de dados peculiares do PostgreSQL
WITH TIMEZONE /WITHOUT TIMEZONE confundir você:nenhum deles realmente armazena um fuso horário) Algum código clichê:o seguinte assume que
ps é uma PreparedStatement , rs um ResultSet e tzUTC é um Calendar estático objeto correspondente a UTC fuso horário. public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Escreva
Instant para o banco de dados TIMESTAMPTZ :Instant instant = ...;
Timestamp ts = instant != null ? Timestamp.from(instant) : null;
ps.setTimestamp(col, ts, tzUTC); // column is TIMESTAMPTZ!
Leia
Instant do banco de dados TIMESTAMPTZ :Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? ts.toInstant() : null;
Isso funciona com segurança se seu tipo de PG for
TIMESTAMPTZ (Nesse caso, o calendarUTC não tem efeito nesse código; mas é sempre aconselhável não depender de fusos horários padrão). "Com segurança" significa que o resultado não dependerá do fuso horário do servidor ou do banco de dados , ou informações de fusos horários:a operação é totalmente reversível e, aconteça o que acontecer com as configurações de fusos horários, você sempre obterá o mesmo "instante de tempo" que tinha originalmente no lado do Java. Se, em vez de um carimbo de data/hora (um instante na linha do tempo física), você estiver lidando com uma data e hora local "civil" (ou seja, o conjunto de campos
{year-month-day hour:min:sec(:msecs)} ), você usaria:- Java:
LocalDateTime(Java 8 ou Jodatime). - JDBC:
java.sql.Timestamp - PostgreSQL:
TIMESTAMP WITHOUT TIMEZONE(TIMESTAMP)
Leia
LocalDateTime do banco de dados TIMESTAMP :Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null )
localDt = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);
Escreva
LocalDateTime para o banco de dados TIMESTAMP : Timestamp ts = null;
if( localDt != null)
ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
ps.setTimestamp(colNum,ts, tzUTC);
Novamente, essa estratégia é segura e você pode dormir tranquilamente:se você armazenou
2011-10-30 23:59:30 , você recuperará esses campos precisos (hora =23, minuto =59 ... etc) sempre, não importa o que aconteça - mesmo que amanhã o fuso horário do seu servidor Postgresql (ou cliente) mude, ou sua JVM ou fuso horário do seu sistema operacional, ou se o seu país modificar suas regras de horário de verão, etc. Adicionado:Se você quiser (parece um requisito natural) armazenar a especificação completa de data e hora (um
ZonedDatetime :o carimbo de data e hora junto com o fuso horário, que implicitamente também inclui as informações completas de data e hora civil - mais o fuso horário) ... . Você deve criar seu próprio armazenamento, talvez em um par de campos:podem ser os dois tipos acima (altamente redundantes, embora eficientes para recuperação e cálculo), ou um deles mais o deslocamento de tempo (você perde as informações de fuso horário, alguns cálculos se tornam difícil, e alguns impossíveis), ou um deles mais o fuso horário (como string; alguns cálculos podem ser extremamente caros).