W3docs

Formatação de Datas em Java

Formate datas e horas em Java como strings com DateTimeFormatter e padrões padrão ou personalizados.

Formatação de datas é a conversão de um valor de data/hora em uma string legível por humanos. Este capítulo aborda DateTimeFormatter — como construí-lo (embutido, localizado ou baseado em padrão), o alfabeto completo de padrões, tratamento de locale e fuso horário, e as armadilhas que produzem saída incorreta. Funciona com todos os tipos de java.time: LocalDate, LocalTime, LocalDateTime, ZonedDateTime e Instant.

Todo tipo java.time tem um toString() que produz a representação ISO-8601: 2025-11-04, 14:30:00, 2025-11-04T14:30:00Z. Isso é adequado para logs e tráfego máquina-a-máquina. Para exibição ao usuário ("November 4, 2025" ou "4 Nov, 14:30") você precisa de um formatter.

A classe é java.time.format.DateTimeFormatter. É o substituto moderno, thread-safe e imutável para o legado java.text.SimpleDateFormat (que não era thread-safe e causava bugs sutis em produção quando compartilhado). Armazene-o como static final e reutilize-o entre threads indefinidamente — sem sincronização, sem cópias defensivas.

Três formas de construir um formatter

// 1. Built-in ISO formatters
DateTimeFormatter.ISO_LOCAL_DATE;                  // 2025-11-04
DateTimeFormatter.ISO_LOCAL_DATE_TIME;             // 2025-11-04T14:30:00
DateTimeFormatter.ISO_OFFSET_DATE_TIME;            // 2025-11-04T14:30:00-05:00
DateTimeFormatter.ISO_ZONED_DATE_TIME;             // 2025-11-04T14:30:00-05:00[America/New_York]
DateTimeFormatter.ISO_INSTANT;                     // 2025-11-04T19:30:00Z
DateTimeFormatter.BASIC_ISO_DATE;                  // 20251104

// 2. Localised formatters
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);          // November 4, 2025 (en-US)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);    // Nov 4, 2025, 2:30:00 PM

// 3. Pattern-based formatters
DateTimeFormatter.ofPattern("dd MMM yyyy");                   // 04 Nov 2025
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm zzz");          // 2025-11-04 14:30 EST

A API de padrões é a que você mais usará. A localizada é adequada quando você precisa de um formato culturalmente apropriado e quer que o JDK escolha o layout por você.

Formatação

A forma da chamada é simétrica em ambos os lados:

String s = formatter.format(temporal);
String s2 = temporal.format(formatter);                       // same thing, fluent style

Ambas funcionam. A maioria do código usa a forma fluente.

LocalDate today = LocalDate.now();
String us = today.format(DateTimeFormatter.ofPattern("MM/dd/yyyy"));         // 11/04/2025
String iso = today.format(DateTimeFormatter.ISO_LOCAL_DATE);                 // 2025-11-04
String eu = today.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));          // 04.11.2025

O alfabeto de padrões

A tabela completa — aquela a que você sempre voltará. As letras são sensíveis a maiúsculas/minúsculas e a quantidade importa.

LetraSignificadoExemplo
yanoy2025, yy25, yyyy2025
MmêsM11, MM11, MMMNov, MMMMNovember
ddia do mêsd4, dd04
Edia da semanaETue, EEEETuesday
Hhora 0-23H14, HH14
hhora 1-12h2, hh02 (use com a)
aAM/PMaPM
mminutom5, mm05
ssegundos9, ss09
Sfração de segundoSSS123 (milissegundos)
nnanossegundonnnnnnnnn123456789
znome do fusozEST, zzzzEastern Standard Time
Zoffset do fusoZ-0500, ZZ-0500, ZZZZGMT-05:00
Xoffset ISOX-05, XX-0500, XXX-05:00
VID do fusoVVAmerica/New_York

Texto literal é envolvido em aspas simples:

DateTimeFormatter.ofPattern("EEEE, MMMM d 'at' h:mm a");      // Tuesday, November 4 at 2:30 PM

Para uma aspa simples literal, use duas: ''.

O par mais confundido é m vs M (minúsculo = minuto, maiúsculo = mês) e H vs h (maiúsculo = 0-23, minúsculo = 1-12). A maioria dos bugs do tipo "a hora está errada de uma forma estranha" vêm de um desses erros de digitação.

Localização: Locale e withLocale

Um formatter usa o locale padrão da JVM, a menos que você especifique o contrário. Para saída "sempre em inglês" ou "sempre em alemão", fixe o locale:

DateTimeFormatter english = DateTimeFormatter.ofPattern("EEEE, MMMM d", Locale.US);
DateTimeFormatter german  = DateTimeFormatter.ofPattern("EEEE, d. MMMM", Locale.GERMAN);
DateTimeFormatter french  = DateTimeFormatter.ofPattern("EEEE d MMMM", Locale.FRENCH);

today.format(english);   // Tuesday, November 4
today.format(german);    // Dienstag, 4. November
today.format(french);    // mardi 4 novembre

Para conteúdo renderizado no servidor, sempre passe um locale. O "locale padrão da JVM" é imprevisível em servidores de produção e é a fonte dos bugs "funciona certo no meu laptop, errado no servidor".

Exibição de fuso horário

ZonedDateTime e Instant são os únicos tipos que possuem informações de fuso horário. Formatar um LocalDateTime com um padrão que inclui z ou Z lança exceção — não há fuso para imprimir. Converta primeiro:

ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/New_York"));
zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"));   // 2025-11-04 14:30 EST

Para Instant, o formatter também precisa de um fuso — Instant não tem nenhum, portanto formatters com campos dependentes de fuso precisam de withZone:

DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
    .withZone(ZoneId.of("America/New_York"));
f.format(Instant.now());                                      // formatter supplies the zone for display

Sem withZone, formatar um Instant com um padrão no formato de calendário lança exceção.

Formatters estilizados com FormatStyle

As fábricas localizadas oferecem quatro tamanhos canônicos:

DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);   // 11/4/25 (en-US), 04.11.25 (de-DE)
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);  // Nov 4, 2025
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);    // November 4, 2025
DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);    // Tuesday, November 4, 2025

Os mesmos quatro tamanhos existem para ofLocalizedTime e ofLocalizedDateTime. Use-os quando quiser que o layout siga o locale do usuário em vez de impor um único formato. Combine-os com .withLocale(...) para fixar o locale.

Exemplo prático: uma data, seis variantes de exibição

O programa abaixo formata um ZonedDateTime de seis formas comuns: ISO para logs de máquina, 12 horas no estilo americano para usuários em inglês, 24 horas no estilo europeu para usuários em alemão, uma forma longa localizada, um padrão personalizado com texto literal embutido, e um formatter Instant-via-withZone para timestamps brutos.

java— editable, runs on the server

O que observar na execução:

  • Os campos static final DateTimeFormatter em cache têm o formato correto. DateTimeFormatter é imutável e thread-safe; criar um é barato, mas não gratuito, e reutilizar a mesma instância em todos os lugares é o padrão recomendado pelo JDK. Não construa um novo dentro de um loop quente.
  • O mesmo ZonedDateTime produziu seis strings diferentes dependendo do formatter. O objeto de valor nunca mudou; o formatter é o único que controla o layout. Essa é a separação para a qual DateTimeFormatter existe — mantenha o tipo de valor limpo e delegue a apresentação ao formatter.
  • O bloco de "erros comuns de digitação" imprimiu 14:11 para HH:MM porque M é mês, não minuto. Os dois são o par mais confundido no alfabeto de padrões. Se a hora exibida parecer suspeita com um componente de data, verifique as maiúsculas/minúsculas no padrão.
  • A escada de FormatStyle produziu quatro strings progressivamente mais longas. Use FormatStyle.MEDIUM como padrão sensato para "mostrar uma data ao usuário sem pensar muito"; LONG e FULL para contextos onde o ano e o dia da semana precisam ser inequívocos; SHORT para espaços de UI reduzidos.
  • LocalDateTime com um padrão contendo fuso lançou exceção — o formatter precisa de dados de fuso, e LocalDateTime não tem nenhum. A correção é converter (ldt.atZone(zone)) ou remover o campo dependente de fuso do padrão. De qualquer forma, o modo de falha fica claro em tempo de execução.

O que vem a seguir

Formatação é a direção valor → string. O próximo capítulo, Parsing de Datas em Java, é o inverso — string → valor — usando os mesmos padrões de DateTimeFormatter e o mesmo conjunto de ressalvas. Os dois juntos formam a fronteira de E/S para qualquer código que troca datas com usuários, configurações, logs ou APIs remotas.

Prática

Prática
Um servidor web registra timestamps com `ZonedDateTime.now().format(DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm'))`. Em uma JVM com locale alemão, o mês é impresso como 'Nov' em vez de '11'. Qual é a causa mais provável?
Um servidor web registra timestamps com `ZonedDateTime.now().format(DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm'))`. Em uma JVM com locale alemão, o mês é impresso como 'Nov' em vez de '11'. Qual é a causa mais provável?
Was this page helpful?