PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Armazenando compromissos em um banco de dados SQL, como Postgres, para uso com a estrutura java.time


Depende do tipo de contratação.

Existem dois tipos de compromissos:
  • Um momento, um ponto específico na linha do tempo, ignorando quaisquer alterações nas regras de fuso horário.
    Exemplo:lançamento de foguete.
  • Uma data e hora do dia que devem ser ajustadas para alterações nas regras de fuso horário.
    Exemplo:visita médica/odontológica.


Momento


Se estamos marcando o lançamento de um foguete, por exemplo, não nos importamos com a data e a hora do dia. Só nos importamos com o momento em que (a) os céus se alinham e (b) esperamos um clima favorável.

Se nesse intervalo os políticos alterarem as regras do fuso horário em uso em nosso local de lançamento ou em nossos escritórios, isso não afetará nosso compromisso de lançamento. Se os políticos que governam nosso site de lançamento adotarem o Horário de verão (DST) , o momento do nosso lançamento continua o mesmo. Se os políticos que governam nossos escritórios decidirem mude o relógio meia hora antes por causa das relações diplomáticas com um país vizinho, o momento do nosso lançamento continua o mesmo.

Para tal nomeação, sim, sua abordagem seria correta. Você registraria o compromisso em UTC usando uma coluna do tipo TIMESTAMP WITH TIME ZONE . Após a recuperação, ajuste em qualquer fuso horário que o usuário preferir.

Bancos de dados como Postgres use qualquer informação de fuso horário que acompanhe uma entrada para ajustar em UTC e, em seguida, descarte essa informação de fuso horário. Quando você recupera o valor do Postgres, ele sempre representará uma data com hora do dia conforme visto em UTC. Cuidado, algumas ferramentas ou middleware podem ter o anti-recurso de aplicar algum fuso horário padrão entre a recuperação do banco de dados e a entrega para você, o programador. Mas fique claro:o Postgres sempre salva e recupera valores do tipo TIMESTAMP WITH TIME ZONE em UTC, sempre UTC e offset-from-UTC de zero horas-minutos-segundos.

Aqui está um exemplo de código Java.
LocalDate launchDateAsSeenInRome = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime launchTimeAsSeenInRome = LocalTime.of( 21 , 0 ) ;
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
// Assemble those three parts to determine a moment.
ZonedDateTime launchMomentAsSeenInRome = ZonedDateTime.of( launchDateAsSeenInRome , launchTimeAsSeenInRome , zoneEuropeRome ) ;

Para ver o mesmo momento em UTC, converta para um Instant . Um Instant objeto sempre representa um momento como visto em UTC.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ;  // Adjust from Rome time zone to UTC.

O Z no final do exemplo de string acima é a notação padrão para UTC e é pronunciado “Zulu”.

Infelizmente, a equipe do JDBC 4.2 deixou de exigir suporte para Instant ou ZonedDateTime tipos. Portanto, seu driver JDBC pode ou não ser capaz de ler/gravar tais objetos em seu banco de dados. Caso contrário, basta converter para OffsetDateTime . Todos os três tipos representam um momento, um ponto específico na linha do tempo. Mas OffsetDateTime tem suporte exigido pelo JDBC 4.2 por motivos que me escapam.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;

Gravando no banco de dados.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;

Recuperação do banco de dados.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;

Ajuste para o fuso horário de Nova York desejado pelo usuário.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;

Você pode ver todos os código executado ao vivo em IdeOne.com acima .

A propósito, rastrear eventos passados ​​também é tratado como um momento. Quando o paciente realmente chegou para a consulta, quando o cliente pagou a fatura, quando um novo contratado assinou seus documentos, quando o servidor travou… tudo isso é rastreado como um momento em UTC. Como discutido acima, normalmente isso seria um Instant , embora ZonedDateTime &OffsetDateTime também representam um momento. Para o banco de dados, use TIMESTAMP WITH TIME ZONE (não WITHOUT ).

Horário do dia


Espero que a maioria dos aplicativos voltados para negócios estejam focados em compromissos do outro tipo, em que visamos uma data com uma hora do dia em vez de um momento específico.

Se o usuário marcar uma consulta com seu médico para revisar os resultados de um teste, ele o fará para uma determinada hora do dia nessa data. Se entretanto os políticos alterarem as regras do seu fuso horário, adiantando ou atrasando o relógio uma hora ou meia hora ou qualquer outra quantidade de tempo, a data e hora do dia dessa consulta médica permanecem as mesmas. Na verdade, o ponto da linha do tempo da nomeação original será alterado depois que os políticos alterarem o fuso horário, mudando para um ponto anterior/posterior na linha do tempo.

Para tais compromissos, não armazenar a data e hora do dia como visto em UTC. Nós não use o tipo de coluna do banco de dados TIMESTAMP WITH TIME ZONE .

Para esses compromissos, armazenamos a data com a hora do dia sem levar em consideração o fuso horário. Usamos uma coluna de banco de dados do tipo TIMESTAMP WITHOUT TIME ZONE (observe WITHOUT em vez de WITH ). O tipo de correspondência em Java é LocalDateTime .
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;

Escreva isso no banco de dados.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;

Seja claro sobre isso:um LocalDateTime objeto não representar um momento, não um ponto específico na linha do tempo. Um LocalDateTime objeto representa um intervalo de possíveis momentos ao longo de cerca de 26-27 horas da linha do tempo (o intervalo de fusos horários ao redor do globo). Para dar um significado real a um LocalDateTime , devemos associar um fuso horário pretendido.

Para esse fuso horário pretendido, use uma segunda coluna para armazenar o identificador de zona. Por exemplo, as strings Europe/Rome ou America/New_York . Consulte lista de nomes de zonas .
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;

Escreva isso no banco de dados como texto.
myPreparedStatement.setString( … , zoneEuropeRome ) ;

Recuperação. Recupere o nome da zona como texto e instancie um ZoneId objeto.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;

Junte essas duas peças para determinar um momento representado como um ZonedDateTime objeto. Faça isso dinamicamente quando precisar agendar um calendário. Mas não armazenar o momento. Se os políticos redefinirem o(s) fuso(s) horário(s) no futuro, um momento diferente deve ser calculado então.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;

O usuário está viajando para Nova York, EUA. Eles precisam saber quando ligar para o médico em Milão, Itália, de acordo com os relógios na parede em sua localização temporária em Nova York. Portanto, ajuste de um fuso horário para outro. Mesmo momento, hora diferente do relógio de parede.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;

tzdata


Esteja ciente de que, se as regras dos fusos horários desejados estiverem mudando, você deverá atualizar a cópia das definições de fuso horário em seus computadores.

Java contém sua própria cópia do tzdata , assim como o mecanismo de banco de dados Postgres. E seu sistema operacional host também. Este código específico mostrado aqui requer apenas que o Java esteja atualizado. Se você usa o Postgres para fazer ajustes de fuso horário, seu tzdata também deve estar atualizado. E para log e tal, seu sistema operacional host deve ser mantido atualizado. Para uma observação adequada do relógio pelo usuário, o sistema operacional de sua máquina cliente também deve estar atualizado.

Cuidado:os políticos de todo o mundo mostraram uma propensão a mudar seus fusos horários com frequência surpreendente e muitas vezes com pouco aviso prévio.

Sobre java.time


O java.time framework é construído em Java 8 e posterior. Essas classes substituem o antigo e problemático legado classes de data e hora, como java.util.Date , Calendar , &SimpleDateFormat .

Para saber mais, consulte o Tutorial do Oracle . E pesquise no Stack Overflow muitos exemplos e explicações. A especificação é JSR 310 .

O Joda-Time projeto, agora em modo de manutenção , aconselha a migração para o java.time Aulas.

Você pode trocar java.time objetos diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou mais tarde. Não há necessidade de strings, não há necessidade de java.sql.* Aulas. Suporte para Hibernate 5 e JPA 2.2 java.time .

Onde obter as classes java.time?