W3docs

Java LocalDate

Represente datas sem hora ou fuso horário em Java com LocalDate — criação, manipulação e consultas.

LocalDate é uma data de calendário — ano, mês, dia — sem hora do dia e sem fuso horário. Ela representa a mesma data em qualquer relógio do mundo: quando você escreve LocalDate.of(2025, 11, 4), isso é o quarto de novembro no calendário ISO, ponto final. Sem 14:30 anexado, sem offset UTC, sem ambiguidade entre Tóquio e Honolulu.

Isso a torna o tipo certo para muitas coisas que o legado java.util.Date tratava mal: aniversários, datas de contratos, datas de faturas, a data selecionada por um seletor de datas na interface. Em qualquer lugar onde um dia de calendário é a unidade, LocalDate é a classe.

Criando

As três fábricas padrão:

LocalDate today  = LocalDate.now();                          // system default zone
LocalDate stardate = LocalDate.of(2025, 11, 4);              // year, month (1-12), day (1-31)
LocalDate parsed = LocalDate.parse("2025-11-04");            // ISO-8601 yyyy-MM-dd

now() lê a data atual no fuso horário padrão da JVM. Isso é quase sempre o que você quer; para testes é um problema, e as formas sobrecarregadas com Clock (LocalDate.now(clock)) permitem injetar um relógio fixo. O capítulo sobre análise cobre parse com formatos personalizados; o padrão aceita apenas datas ISO-8601.

Você também pode usar um enum Month em vez de um inteiro 1..12:

LocalDate.of(2025, Month.NOVEMBER, 4);                       // type-safe; no risk of using 0 for January

Se você já escreveu new GregorianCalendar(2025, 11, 4) e obteve dezembro (porque a API legada usa meses com base zero), a forma com enum é a atualização que você quer.

Inspecionando

O catálogo de acessores:

int year      = date.getYear();
Month month   = date.getMonth();                              // enum
int monthVal  = date.getMonthValue();                         // 1-12
int day       = date.getDayOfMonth();
DayOfWeek dow = date.getDayOfWeek();                          // enum: MONDAY, TUESDAY, ...
int dayOfYear = date.getDayOfYear();                          // 1-366
boolean leap  = date.isLeapYear();
int monthLen  = date.lengthOfMonth();                         // 28-31
int yearLen   = date.lengthOfYear();                          // 365 or 366

Month e DayOfWeek são enums. Use-os; eles tornam o código que compara com um dia ou mês específico dramaticamente mais claro:

if (date.getDayOfWeek() == DayOfWeek.MONDAY) ...              // type-safe
if (date.getMonth() == Month.NOVEMBER) ...                    // no off-by-one risk

Cada enum possui seus próprios métodos auxiliares — Month.length(boolean leap), DayOfWeek.getValue() retornando 1-7 com segunda-feira = 1, e DayOfWeek.plus(7) para "o mesmo dia, n dias a partir de agora."

Modificando — todo método retorna uma nova instância

Os métodos aritméticos:

date.plusDays(7);                                              // a week later
date.plusWeeks(2);
date.plusMonths(1);                                            // careful: month length varies
date.plusYears(1);

date.minusDays(30);
date.minusYears(5);

E as formas de "substituir um campo":

date.withYear(2026);
date.withMonth(1);
date.withDayOfMonth(1);
date.withDayOfYear(1);                                         // first day of the year

Todos esses retornam um novo LocalDate. O original permanece inalterado. date.plusDays(7) sem capturar o resultado é uma operação sem efeito — e um bug que todos já escrevemos pelo menos uma vez.

O aviso "o comprimento do mês varia" para plusMonths: quando adicionar um mês resultaria em um dia que não existe no mês de destino, java.time limita ao último dia. LocalDate.of(2025, 1, 31).plusMonths(1) é 2025-02-28 (ou 02-29 em ano bissexto), não 2025-03-03. O comportamento é documentado e consistente, mas significa que plusMonths(1) e minusMonths(1) nem sempre são inversos.

Comparando

date.isBefore(other);
date.isAfter(other);
date.isEqual(other);                                           // same as equals here; useful on ZonedDateTime
date.compareTo(other);                                         // -1 / 0 / +1

LocalDate implementa Comparable<LocalDate>, portanto ordena naturalmente em qualquer coleção. Para "esta data está em [início, fim]?" a forma típica é !date.isBefore(start) && !date.isAfter(end).

Distância: until e ChronoUnit.between

Quantos dias há entre duas datas?

long days = ChronoUnit.DAYS.between(start, end);               // a long; signed
long weeks = ChronoUnit.WEEKS.between(start, end);
long months = ChronoUnit.MONTHS.between(start, end);

Period diff = start.until(end);                                // a Period (years/months/days)

ChronoUnit.X.between é a chamada certa para "quantos X inteiros existem entre esses?" until retorna um Period, que é o detalhamento em forma de calendário — útil para "você é membro há 2 anos, 3 meses e 14 dias."

Observe a convenção de sinal: between(start, end) é positivo quando end é posterior a start, negativo caso contrário.

O atalho "que dia da semana é..."

O pacote de ajustadores temporais oferece os predicados que você calcularia manualmente:

import static java.time.temporal.TemporalAdjusters.*;

date.with(firstDayOfMonth());
date.with(lastDayOfMonth());
date.with(firstDayOfNextMonth());
date.with(next(DayOfWeek.MONDAY));                             // next Monday strictly after `date`
date.with(nextOrSame(DayOfWeek.MONDAY));                       // today if today is Monday, else next
date.with(previousOrSame(DayOfWeek.SUNDAY));
date.with(lastInMonth(DayOfWeek.FRIDAY));                      // last Friday of the month

O capítulo sobre Temporal Adjusters cobre esses em profundidade. Por agora, a conclusão: não escreva "próxima segunda-feira após esta data" manualmente; os ajustadores já têm isso.

Aviso sobre fuso horário

LocalDate não tem fuso, então LocalDate.now() precisa escolher um para saber qual dia de calendário "agora" é. O padrão é o fuso padrão da JVM (ZoneId.systemDefault()). Se você estiver rodando em um servidor configurado para UTC às 23:30 horário local de Nova York, LocalDate.now() retorna a data de amanhã na perspectiva de Nova York — porque o fuso da JVM diz que já passou da meia-noite UTC.

Para uma data local em um fuso conhecido, passe o fuso explicitamente:

LocalDate tokyoToday = LocalDate.now(ZoneId.of("Asia/Tokyo"));

Isso acontece em produção exatamente quando o laptop do desenvolvedor está em um fuso diferente do servidor implantado. Seja explícito quando o fuso importa.

Um exemplo trabalhado: aritmética de data de fatura

O programa abaixo usa LocalDate para o tipo de trabalho que um pequeno sistema de faturamento faria — gerar uma data de fatura, calcular datas de vencimento, contar dias em aberto, calcular o final do mês e encontrar o próximo dia útil. É a forma realista do código com LocalDate.

java— editable, runs on the server

O que extrair da execução:

  • LocalDate.of(2025, Month.NOVEMBER, 4) foi a forma segura. A sobrecarga com inteiro (2025, 11, 4) funciona, mas o enum Month torna impossível usar 0 para janeiro — o erro legado do GregorianCalendar. Quando o segundo argumento pode ser qualquer um, use o enum.
  • plusDays(30) retornou um novo LocalDate; imprimir o original ao final do programa mostrou-o inalterado. Todo método aritmético e with* segue essa regra, o que torna o tipo seguro para threads por construção. Nenhuma cópia defensiva necessária; passar um LocalDate para um método é sempre seguro.
  • A demonstração de plusMonths(1) mostrou o comportamento de limitação: 31 de janeiro + 1 mês = 28 de fevereiro (ou 29 em ano bissexto). O comportamento é documentado e consistente, mas jan31.plusMonths(1).minusMonths(1) retorna 28 de janeiro, não 31 de janeiro. Ir e voltar esperando o original funciona para plusDays/minusDays, não para plusMonths/minusMonths.
  • Os ajustadores temporais (lastDayOfMonth, firstDayOfNextMonth, nextOrSame(MONDAY)) substituíram percursos manuais de calendário com várias linhas. Encadeados, expressam "a primeira segunda-feira no ou após o primeiro do próximo mês" em dois ajustadores. O próximo capítulo sobre LocalTime e o capítulo dedicado a Temporal Adjusters vão mais fundo.
  • ChronoUnit.DAYS.between(invoice, today) retornou um long com sinal. O companheiro invoiceDate.until(today) retornou um Period — em forma de calendário, com campos separados de ano/mês/dia. Os dois respondem perguntas diferentes: ChronoUnit.X para "quantos X inteiros," Period para "em forma amigável ao calendário." Escolha o que melhor corresponde ao formato de saída desejado.

O que vem a seguir

LocalDate foi o lado da data. O próximo capítulo, Java LocalTime, é seu espelho — hora do dia, sem data anexada e sem fuso. Mesma API fluente, classe menor, mesmas garantias de imutabilidade.

Prática

Prática
`LocalDate.of(2025, 1, 31).plusMonths(1)` retorna qual valor, e por quê?
`LocalDate.of(2025, 1, 31).plusMonths(1)` retorna qual valor, e por quê?
Was this page helpful?