1. Contexto histórico: como se trabalhava com datas antes do Java 8
Nos tempos remotos (antes do Java 8), para trabalhar com datas e horas usava-se as classes java.util.Date, java.util.Calendar e, para formatação — java.text.SimpleDateFormat. Era aquele caso em que programadores do mundo todo suspiravam em uníssono e, rangendo os dentes, escreviam algo como:
import java.util.Date;
Date now = new Date();
System.out.println(now); // Vai imprimir algo estranho como "Wed Jun 05 14:15:22 MSK 2025"
Parece simples, mas na prática não era nada tão bonito. Aqui estão apenas algumas das “alegrias” do API antigo:
- Date — objeto mutável (mutable). Ele podia ser alterado inadvertidamente, o que frequentemente levava a bugs.
- Os meses em Date e Calendar começavam em zero (janeiro é 0, dezembro — 11), enquanto os dias começavam em um.
- SimpleDateFormat não era thread-safe: se duas threads formatassem datas simultaneamente, resultados inesperados podiam ocorrer.
- Uma enorme quantidade de métodos foi marcada como @Deprecated (obsoletos), e a IDE vivia te assustando com avisos amarelos.
- Trabalhar com fusos horários era uma verdadeira dor: era fácil confundir hora local e UTC, e a mudança para horário de verão/inverno nem vale lembrar.
Exemplo doloroso
import java.util.Date;
Date date = new Date(2025, 5, 1); // ano 2025, mês 5 (junho?), dia 1
System.out.println(date); // Não é o que você espera!
2. O surgimento do java.time: uma nova abordagem
Em 2014 ficou claro: o API antigo não era apenas incômodo — era perigoso. Por isso, surgiu no Java o novo pacote — java.time, que implementa a especificação JSR‑310. Essa API foi inspirada na popular biblioteca Joda-Time e rapidamente se tornou o padrão de fato.
Pacotes e classes principais
- java.time — pacote principal, onde ficam todas as novas classes para data e hora.
- java.time.format — para formatação e análise (parsing) de datas e horas.
- java.time.temporal — para operações temporais mais avançadas.
- java.time.zone — para trabalhar com fusos horários.
Aqui estão os principais protagonistas da nova API:
| Classe | Para quê? | Exemplo de uso |
|---|---|---|
|
Apenas data (ano, mês, dia) | Aniversário, sem horário |
|
Apenas hora (horas, minutos, segundos) | Horário da reunião, sem data |
|
Data e hora, sem fuso horário | Evento local |
|
Data e hora com fuso horário | Reunião em Minsk no horário local |
|
Instante absoluto (UTC) | Carimbo de evento no log |
|
Intervalo de tempo (horas, minutos, seg.) | Duração da chamada |
|
Período (anos, meses, dias) | Tempo de serviço, idade |
Exemplo: criar uma data de forma moderna
import java.time.LocalDate;
LocalDate today = LocalDate.now();
System.out.println(today); // Por exemplo: "2025-06-05"
3. Vantagens da nova API
Imutabilidade (immutable)
Todas as classes de java.time são imutáveis. Isso significa: se você criou um objeto LocalDate, não é possível alterá-lo. Qualquer operação (por exemplo, adicionar um dia) retorna um novo objeto.
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
System.out.println(today); // 2025-06-05
System.out.println(tomorrow); // 2025-06-06
Tratamento explícito de fusos horários
No API antigo era fácil esquecer em qual fuso horário a data estava. Em java.time tudo é explícito: se precisar de fuso horário — use ZonedDateTime; se não precisar — use LocalDateTime.
import java.time.ZonedDateTime;
import java.time.ZoneId;
ZonedDateTime MinskTime = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
System.out.println(MinskTime); // 2025-06-05T14:23:45+03:00[Europe/Minsk]
Métodos práticos para cálculos e comparação
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plusMonths(1);
boolean isAfter = LocalDate.now().plusDays(1).isAfter(today); // true
Formatação e análise (parsing)
Formatação e análise — por meio de DateTimeFormatter (detalhes — na próxima aula):
import java.time.format.DateTimeFormatter;
LocalDate today = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
String formatted = today.format(formatter); // "05.08.2025"
4. Compatibilidade: como conviver com o código antigo
No mundo real, com muita frequência é preciso trabalhar com bibliotecas ou sistemas antigos que usam Date e Calendar. Felizmente, a nova API é amigável ao legado: é possível converter os tipos antigos para os novos e vice-versa.
Conversão Date ↔ Instant
import java.util.Date;
import java.time.Instant;
// Date → Instant
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
// Instant → Date
Date dateBack = Date.from(instant);
Conversão Calendar ↔ ZonedDateTime
import java.util.Calendar;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.Date;
// Calendar → ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zdt = ZonedDateTime.ofInstant(
calendar.toInstant(),
calendar.getTimeZone().toZoneId()
);
// ZonedDateTime → Calendar
Calendar calBack = Calendar.getInstance();
calBack.setTime(Date.from(zdt.toInstant()));
Tabela: mapeamento entre classes antigas e novas
| Classe antiga | Classe nova | Comentário |
|---|---|---|
|
|
Tempo absoluto |
|
|
Data e hora com fuso horário |
|
|
Formatação/análise de datas |
5. Prática: primeiros passos com java.time
Suponha que você tenha um usuário com data de nascimento. Vamos salvar e exibir essa data:
import java.time.LocalDate;
public class UserProfile {
private String name;
private LocalDate birthDate;
public UserProfile(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public void printProfile() {
System.out.println("Nome: " + name);
System.out.println("Data de nascimento: " + birthDate);
}
}
public class Main {
public static void main(String[] args) {
UserProfile user = new UserProfile("Alisa", LocalDate.of(1998, 12, 25));
user.printProfile();
}
}
Saída:
Nome: Alisa
Data de nascimento: 1998-12-25
6. Comparação: API antiga vs API nova
Exemplo: adicionar uma semana à data de nascimento
Modo antigo (Date/Calendar):
import java.util.Calendar;
Calendar cal = Calendar.getInstance();
cal.set(1998, Calendar.DECEMBER, 25);
cal.add(Calendar.WEEK_OF_YEAR, 1);
System.out.println(cal.getTime()); // Trabalhoso e pouco intuitivo
Modo novo (java.time):
import java.time.LocalDate;
LocalDate birthDate = LocalDate.of(1998, 12, 25);
LocalDate nextWeek = birthDate.plusWeeks(1);
System.out.println(nextWeek); // 1999-01-01
Na nova API o código é mais curto, mais simples e mais seguro.
7. Erros comuns ao trabalhar com java.time
Erro nº 1: esquecer que os objetos são imutáveis.
Se você chamar date.plusDays(1); e não guardar o resultado, a data original continuará a mesma.
Erro nº 2: confundir LocalDate com LocalDateTime.
LocalDate armazena apenas a data (ano, mês, dia), enquanto LocalDateTime também inclui a hora. Não confunda quando precisar tratar horas e minutos.
Erro nº 3: usar classes antigas em projetos novos.
Se possível — sempre use java.time. As classes antigas servem apenas para compatibilidade.
Erro nº 4: manipulação incorreta de fusos horários.
Se for necessário armazenar um evento importante para diferentes regiões, use ZonedDateTime ou pelo menos Instant. LocalDateTime não contém informações de fuso horário!
Erro nº 5: tentar comparar diretamente LocalDate e LocalDateTime.
São tipos diferentes; não é possível compará-los diretamente. Primeiro, converta para o mesmo tipo.
GO TO FULL VERSION