Os desenvolvedores Java frequentemente enfrentavam limitações significativas ao trabalhar com datas e horários devido às bibliotecas herdadas. As classes tradicionais, como java.util.Date e SimpleDateFormat, apresentavam problemas sérios de segurança em ambientes multi-thread, além de design não intuitivo - por exemplo, no java.util.Date, o ano era representado a partir de 1900 e os meses começavam em 0. Essas deficiências levavam muitos a recorrerem a bibliotecas externas como Joda-Time.
Para resolver esses problemas de forma padronizada, o Java SE 8 introduziu uma nova API de data e hora através do JSR-310, desenvolvida em colaboração com a Oracle e os criadores da Joda-Time, agora incorporada no pacote java.time.
Fundamentos da Nova Arquitetura
A nova API baseia-se em três princípios fundamentais:
- Imutabilidade: Todas as classes principais são imutáveis, eliminando problemas de concorrência que afetavam os formatadores antigos.
- Modelagem de Domínio: Separação clara entre conceitos de data, tempo e ponto temporal. Enquanto
java.util.Daterepresentava confusamente um instante no tempo com elementos de timezone notoString(), a nova API mantém distinções claras. - Suporte a Calendários Regionais: Flexibilidade para trabalhar com diferentes sistemas temporais além do ISO-8601, como calendários usados no Japão ou Tailândia.
Classes Fundamentais
As classes LocalDate e LocalTime representam respectivamente data e tempo sem informação de fuso horário, refletindo o ambiente local do sistema. A classe LocalDateTime combina ambas as representações.
Criação e Manipulação
As instâncias são criadas através de métodos de fábrica, proporcionando clareza e segurança. A seguir, exemplos de inicialização:
LocalDateTime instante = LocalDateTime.now();
LocalDate dataEspecifica = LocalDate.of(2023, Month.MARCH, 15);
LocalDate epoca = LocalDate.ofEpochDay(200);
LocalTime partida = LocalTime.of(8, 30);
LocalTime textoParaTempo = LocalTime.parse("14:45:00");
Acesso aos componentes temporais utiliza métodos convencionais de getter:
LocalDate apenasData = instante.toLocalDate();
Month mesCorrente = instante.getMonth();
int diaDoMes = instante.getDayOfMonth();
int segundos = instante.getSecond();
Como os objetos são imutáveis, operações de modificação retornam novas instâncias:
LocalDateTime modificado = instante.withDayOfMonth(1)
.withYear(2022);
LocalDateTime avancado = modificado.plusWeeks(2)
.plus(5, ChronoUnit.DAYS);
A API enclui o conceito de Ajustadores Temporais para encapsular lógica comum de manipulação:
import static java.time.temporal.TemporalAdjusters.*;
LocalDateTime referencia = ...;
LocalDateTime finalMes = referencia.with(lastDayOfMonth());
LocalDateTime quartaProxima = referencia.with(nextOrSame(ChronoUnit.WEDNESDAY));
referencia.with(LocalTime.of(18, 0));
Truncamento Temporal
O método truncatedTo permite reduzir a precisão de um objeto temporal:
LocalTime tempoReduzido = horario.truncatedTo(ChronoUnit.MINUTES);
Fusos Horários e Ofsets
A nova API fornece uma modelagem robusta para fusos horários, representados por ZoneId (identificador como "America/Sao_Paulo") e ZoneOffset (diferença em relação ao UTC).
ZoneId zona = ZoneId.of("Asia/Tokyo");
ZonedDateTime horarioComZona = ZonedDateTime.of(instante, zona);
ZoneOffset deslocamento = ZoneOffset.of("-03:00");
Classes com Informação de Fuso
ZonedDateTime: Data, hora e fuso horário completo, ideal para representar eventos em contextos globais.OffsetDateTime: Data, hora e deslocamento numérico, útil para persistência em bancos de dados.OffsetTime: Apenas hora com deslocamento.
ZonedDateTime eventoGlobal = ZonedDateTime.parse("2024-05-20T09:30:00-04:00[America/New_York]");
OffsetDateTime registroBanco = OffsetDateTime.now(deslocamento);
OffsetTime horaLocal = OffsetTime.now();
OffsetTime mesmaInstanteDiferenteOfset = horaLocal.withOffsetSameInstant(deslocamento);
OffsetTime mesmaHoraDiferenteOfset = horaLocal.withOffsetSameLocal(deslocamento);
Períodos e Durações
A classe Period representa durações baseadas em unidades de calendário (anos, meses, dias):
Period intervalo = Period.ofMonths(3).plusDays(10);
LocalDate novaData = dataAnterior.plus(intervalo);
Duration representa durações em unidades de tempo precisas (segundos, nanossegundos):
Duration espera = Duration.ofMinutes(45);
Duration intervaloExato = Duration.between(horaInicio, horaFim);
Outras Classes Utilitárias
Para casos específicos, existem classes como MonthDay (para aniversários ou feriados) e YearMonth (para faturas ou planos de assinatura).
A integração com JDBC suporta os tipos ANSI SQL equivalentes:
| Tipo SQL | Classe Java |
|---|---|
| DATE | LocalDate |
| TIME | LocalTime |
| TIMESTAMP | LocalDateTime |
| TIMESTAMP WITH TIMEZONE | OffsetDateTime |