O estado atual das coisas ao longo do tempo

Desde a época em que o JDBC foi inventado e suas interfaces foram padronizadas, 20 anos se passaram e, durante esse tempo, muitas coisas mudaram.

Em primeiro lugar, o mundo se tornou global e agora um servidor pode atender usuários de todo o mundo. A velocidade da internet aumentou. Portanto, outro tipo de dado foi adicionado ao SQL para trabalhar com o tempo. Agora os tipos ficam assim:

  • DATA - armazena a data: ano, mês, dia.
  • TIME - armazena o tempo: horas, minutos, segundos.
  • TIMESTAMP - armazena um ponto específico no tempo: data, hora e milissegundos.
  • TIMESTAMP WITH TIME ZONE - TIMESTAMP e fuso horário (nome da zona ou deslocamento).

Em segundo lugar, Java introduziu a API DateTime para gerenciamento de tempo global. Possui as seguintes aulas:

  • Data e hora :
    • LocalData
    • Horário local
  • Momento exato :
    • java.time.Instant
    • java.time.LocalDateTime
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime
  • Hora com fuso horário :
    • java.time.OffsetDateTime
    • java.time.ZonedDateTime

O terceiro ponto interessante é que muitos clientes SQL gostariam de receber tempo do servidor já em sua zona local . Claro, você pode converter o tempo na hora, mas não é conveniente e há erros.

Por exemplo, desejo obter todas as tarefas de hoje do banco de dados. O SQL Server tem uma função CURDATE() para isso. Só aqui o servidor está nos EUA e eu no Japão. E gostaria que ele devolvesse todos os registros do “meu hoje”, e não do “hoje dele”.

Em geral, o servidor SQL também deve ser capaz de trabalhar de forma inteligente com clientes em diferentes fusos horários.

Problemas modernos exigem soluções modernas

Em princípio, novos tipos da API Java DateTime e tipos do SQL podem ser convenientemente mapeados. Para representar o tipo DATE em Java, você precisa usar a classe java.time.LocalDate da API JDK 8 DateTime.

O tipo TIME do banco de dados pode ser representado por dois tipos de Java: java.time.LocalTime e java.time.OffsetTime . Nada complicado também.

Um ponto específico no tempo, representado pelo tipo TIMESTAMP no banco de dados, pode ser representado em Java por 4 tipos:

  • java.time.Instant
  • java.time.LocalDateTime
  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

E, finalmente, TIMESTAMP WITH TIME ZONE pode ser representado por dois tipos:

  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

Como você já está familiarizado com a API DateTime, lembrar desse assunto não será difícil para você :)

Vou escrever em forma de tabela, assim ficará mais fácil:

TIPO SQL Tipo Java
DATA java.time.LocalDate
TEMPO java.time.LocalTime
java.time.OffsetTime
TIMESTAMP java.time.Instant
java.time.LocalDateTime
java.time.OffsetDateTime
java.time.ZonedDateTime
TIMESTAMP COM FUSO HORÁRIO java.time.OffsetDateTime
_

Obtendo a data

Eu tenho uma boa notícia para você. Primeiro em muito tempo. Podemos contornar a limitação do método getDate() , que retorna um tipo Java.sql Date.

A questão é que o objetoconjunto de resultadosexiste outro método interessante - getObject() . Este método recebe dois parâmetros: uma coluna e um tipo, e retorna o valor da coluna convertida para o tipo fornecido. A forma geral do método é a seguinte:

ClassName Name = getObject(column, ClassName);

E se você quiser converter o tipo DATE para o tipo java.time.LocalDate , então você precisa escrever algo como:

LocalDate localDate = results.getObject(4, LocalDate.class);

E qualquer TIMESTAMP em geral pode ser convertido em vários tipos:

java.time.Instant instant = results.getObject(9, java.time.Instant.class);
java.time.LocalDateTime local = results.getObject(9, java.time. LocalDateTime.class);
java.time.OffsetDateTime offset = results.getObject(9, java.time. OffsetDateTime.class);
java.time.ZonedDateTime zoned = results.getObject(9, java.time. ZonedDateTime.class);

Importante! Este código não funcionará se você tiver um MySQL JDBC Driver desatualizado . Preste atenção na versão do "mysql-connector-java" escrita em seu pom.xml, ou adicionada nas Bibliotecas nas configurações do projeto.

A propósito, da mesma forma, você pode contornar a incapacidade de armazenar null para tipos primitivos. Se uma coluna da tabela for do tipo INT, existem algumas maneiras de obter nulo dela. Veja exemplo abaixo:

Integer id1 = results.getObject(8, Integer.class);    	 // this will work
Integer id2 = results.getObject(8, int.class);                 //this will also work
int id3 = results.getObject(8,  Integer.class);            	//method will return null, JVM will throw NPE
int id4 = results.getObject(8,  int.class);                    	//method will return null, JVM will throw NPE

Configuração de fuso horário no MySQL

Muitas coisas interessantes aconteceram com o MySQL também. Como você sabe, ao criar uma conexão MySQL, você pode adicionar vários parâmetros a ela :
mysql://localhost:3306/db_scheme?Name=meaning&Name=meaning

Então, três parâmetros foram adicionados para trabalhar com fusos horários no MySQL. Você pode passar esses parâmetros ao estabelecer uma conexão com o servidor.

Abaixo darei uma tabela com eles:

Parâmetro valores Valor padrão
connectionTimeZone LOCAL | SERVIDOR | zona de usuário SERVIDOR
forceConnectionTimeZoneToSession verdadeiro | falso verdadeiro
preservarInstantes verdadeiro | falso falso

Utilizando o parâmetro connectionTimeZone , selecionamos o fuso horário (time zone) no qual todas as requisições serão executadas. Do ponto de vista do cliente, o servidor está sendo executado no fuso horário especificado.

O parâmetro forceConnectionTimeZoneToSession faz com que a variável time_zone da sessão seja ignorada e substituída por connectionTimeZone.

Finalmente, o parâmetro preserveInstants controla a conversão de tempo exato entre timeZone e connectionTimeZone da JVM.

As configurações mais comuns são:

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=false - corresponde ao antigo driver MySQL JDBC versão 5.1 com useLegacyDatetimeCode=true.

  • connectionTimeZone=LOCAL & forceConnectionTimeZoneToSession=true é um novo modo que fornece a maneira mais natural de lidar com valores de data e hora.

  • connectionTimeZone=SERVER & preserveInstants=true - Corresponde ao antigo driver MySQL JDBC versão 5.1 com useLegacyDatetimeCode=false.

  • connectionTimeZone=user_defined & preserveInstants=true - Ajuda a superar a situação em que o fuso horário do servidor não pode ser reconhecido pelo conector porque é definido como uma abreviação genérica, como CET/CEST.

Sim, as datas são um tema interessante e há muitos problemas com elas. Como diz o ditado: é assustador, claro, mas também não estou chateado! :)