A classe java.util.Date herdada do JDK apresenta dseafios significativos. Seu construtor exige que o ano seja calculado a partir de 1900 e os meses comecem em zero, o que leva a erros itnuitivos. Além disso, a classe SimpleDateFormat não é segura para uso em ambientes multithread, causando problemas de concorrência. A API de tempo do Java 8, baseada no pacote java.time, oferece uma alternativa imutável e thread-safe.
Exemplo com a API Legada:
package exemplo.legado;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DemoDateAntigo {
public static void main(String[] args) {
// Criação de uma data: ano 2023 (2023 - 1900 = 123), mês junho (5, pois janeiro é 0)
Date data = new Date(123, 5, 15);
SimpleDateFormat formato = new SimpleDateFormat("dd/MM/yyyy");
System.out.println("Data formatada: " + formato.format(data));
// Problema de concorrência: múltiplas threads usando o mesmo SimpleDateFormat
Runnable tarefa = () -> {
try {
synchronized (formato) {
Date parsed = formato.parse("20/06/2023");
System.out.println(Thread.currentThread().getName() + " parseou: " + parsed);
}
} catch (Exception e) {
e.printStackTrace();
}
};
new Thread(tarefa).start();
new Thread(tarefa).start();
}
}
LocalDate: Datas Sem Hora
A classe LocalDate representa uma data sem informação de hora ou fuso horário. É imutável e segura para uso em multithreading.
package exemplo.moderno;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoField;
public class DemoLocalDate {
public static void main(String[] args) {
// Criando uma data específica
LocalDate dataFixa = LocalDate.of(2023, Month.JUNE, 15);
System.out.println("Data: " + dataFixa);
// Extraindo componentes
int ano = dataFixa.getYear();
Month mes = dataFixa.getMonth();
int diaDoMes = dataFixa.getDayOfMonth();
int diaDoAno = dataFixa.getDayOfYear();
System.out.printf("Ano: %d, Mês: %s, Dia do Mês: %d, Dia do Ano: %d%n",
ano, mes, diaDoMes, diaDoAno);
// Usando ChronoField para acesso padronizado
int mesNumero = dataFixa.get(ChronoField.MONTH_OF_YEAR);
System.out.println("Mês como número: " + mesNumero);
// Obtendo a data atual
LocalDate hoje = LocalDate.now();
System.out.println("Data de hoje: " + hoje);
}
}
LocalTime, LocalDateTime, Instant, Duration e Period
LocalTime lida apenas com horas, enquanto LocalDateTime combina data e hora. Instant representa um ponto na linha do tempo (epoch seconds). Duration e Period medem intervalos de tempo.
package exemplo.intervalos;
import java.time.*;
import java.time.temporal.ChronoUnit;
public class DemoTempos {
public static void main(String[] args) throws InterruptedException {
// LocalTime
LocalTime horaAtual = LocalTime.now();
System.out.println("Hora atual: " + horaAtual);
System.out.println("Hora: " + horaAtual.getHour() + ", Minuto: " + horaAtual.getMinute());
// LocalDateTime
LocalDate data = LocalDate.now();
LocalTime hora = LocalTime.now();
LocalDateTime dataHora = LocalDateTime.of(data, hora);
System.out.println("Data e Hora combinadas: " + dataHora);
// Instant e Duration (medindo tempo)
Instant inicio = Instant.now();
Thread.sleep(1500); // Simula processamento
Instant fim = Instant.now();
Duration duracao = Duration.between(inicio, fim);
System.out.println("Duração em milissegundos: " + duracao.toMillis());
// Period (diferença entre datas)
LocalDate inicioPeriodo = LocalDate.of(2020, Month.MARCH, 10);
LocalDate fimPeriodo = LocalDate.of(2023, Month.JUNE, 15);
Period periodo = Period.between(inicioPeriodo, fimPeriodo);
System.out.printf("Período: %d anos, %d meses, %d dias%n",
periodo.getYears(), periodo.getMonths(), periodo.getDays());
// Calculando diferença em meses usando ChronoUnit
long mesesTotal = ChronoUnit.MONTHS.between(inicioPeriodo, fimPeriodo);
System.out.println("Total de meses entre as datas: " + mesesTotal);
}
}
Formatação e Parsing com DateTimeFormatter
A classe DateTimeFormatter fornece formatação e parsing imutáveis e thread-safe, substituindo SimpleDateFormat.
package exemplo.formatador;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class DemoFormatador {
public static void main(String[] args) {
// Formatação de LocalDate
LocalDate hoje = LocalDate.now();
DateTimeFormatter formatoBasico = DateTimeFormatter.BASIC_ISO_DATE;
DateTimeFormatter formatoIso = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter formatoCustom = DateTimeFormatter.ofPattern("dd 'de' MMMM 'de' yyyy");
System.out.println("BASIC_ISO_DATE: " + hoje.format(formatoBasico));
System.out.println("ISO_LOCAL_DATE: " + hoje.format(formatoIso));
System.out.println("Customizado: " + hoje.format(formatoCustom));
// Parsing de strings para LocalDate
String textoData1 = "20230615";
String textoData2 = "2023-06-15";
String textoData3 = "15 de junho de 2023";
LocalDate parsed1 = LocalDate.parse(textoData1, formatoBasico);
LocalDate parsed2 = LocalDate.parse(textoData2, formatoIso);
LocalDate parsed3 = LocalDate.parse(textoData3, formatoCustom);
System.out.println("Parsed de '20230615': " + parsed1);
System.out.println("Parsed de '2023-06-15': " + parsed2);
System.out.println("Parsed de '15 de junho de 2023': " + parsed3);
// Exemplo com LocalDateTime e formatação completa
String inputDateTime = "2023-06-15 14:30:00";
DateTimeFormatter formatterInput = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter formatterOutput = DateTimeFormatter.ofPattern("dd/MM/yyyy HH'h'mm'min'ss's'");
LocalDateTime dateTime = LocalDateTime.parse(inputDateTime, formatterInput);
String saidaFormatada = dateTime.format(formatterOutput);
System.out.println("Saída formatada: " + saidaFormatada);
}
}