DateTimeFormatter: parseo y formateo de fechas

Intermedio
Java
Java
Actualizado: 18/04/2026

El pilar del formato de fechas en Java

java.time.format.DateTimeFormatter (introducido en Java 8) es la API moderna para convertir entre objetos Temporal (como LocalDate, LocalDateTime, ZonedDateTime) y cadenas. Reemplaza al antiguo y propenso a errores SimpleDateFormat, que era mutable y no thread-safe.

DateTimeFormatter es inmutable y thread-safe, ideal para constantes de clase.

Formatear objetos a String

Cada clase de java.time tiene un método format(DateTimeFormatter):

LocalDate hoy = LocalDate.now();
String s = hoy.format(DateTimeFormatter.ISO_LOCAL_DATE); // "2026-04-17"

LocalDateTime ahora = LocalDateTime.now();
String t = ahora.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// "2026-04-17T15:23:45.123"

Parsear strings a Temporal

Cada clase tiene un método estático parse(CharSequence, DateTimeFormatter):

LocalDate fecha = LocalDate.parse("2026-04-17");
// O explícito:
LocalDate fecha2 = LocalDate.parse("17/04/2026",
    DateTimeFormatter.ofPattern("dd/MM/yyyy"));

Si la entrada no cuadra con el formato, lanza DateTimeParseException.

Formatos predefinidos

DateTimeFormatter incluye constantes estáticas para formatos ISO comunes:

| Constante | Ejemplo | |-----------|---------| | ISO_LOCAL_DATE | 2026-04-17 | | ISO_LOCAL_TIME | 15:23:45.123 | | ISO_LOCAL_DATE_TIME | 2026-04-17T15:23:45.123 | | ISO_OFFSET_DATE_TIME | 2026-04-17T15:23:45+02:00 | | ISO_ZONED_DATE_TIME | 2026-04-17T15:23:45+02:00[Europe/Madrid] | | ISO_DATE | Varios (acepta tanto 2026-04-17 como +01000000-04-17) | | ISO_INSTANT | 2026-04-17T13:23:45.123Z | | BASIC_ISO_DATE | 20260417 | | RFC_1123_DATE_TIME | Fri, 17 Apr 2026 15:23:45 GMT |

Úsalos siempre que sea posible: son la forma canónica de intercambio de fechas.

Patrones personalizados con ofPattern

Para formatos no estándar, crea un formatter con DateTimeFormatter.ofPattern(String):

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s = hoy.format(fmt); // "17/04/2026"
LocalDate f = LocalDate.parse("01/01/2026", fmt);

Símbolos de patrón principales

| Símbolo | Significado | Ejemplos | |---------|-------------|----------| | y | Año | y a 2026, yy a 26, yyyy a 2026 | | M | Mes | M a 4, MM a 04, MMM a abr., MMMM a abril | | d | Día del mes | d a 7, dd a 07 | | E | Día de la semana | EEE a vie., EEEE a viernes | | H | Hora 24h | H a 9, HH a 09 | | h | Hora 12h | h a 9, hh a 09 | | m | Minuto | m a 5, mm a 05 | | s | Segundo | s, ss | | S | Milisegundo | SSS a 123 | | a | AM/PM | a a AM / PM | | z | Nombre de zona | z a CEST | | V | Zona ID | V a Europe/Madrid | | Z | Offset | Z a +0200, ZZZZZ a +02:00 | | 'texto' | Literal | 'T' |

Ejemplo combinado:

DateTimeFormatter f = DateTimeFormatter.ofPattern("EEEE, d 'de' MMMM 'de' yyyy");
LocalDate fecha = LocalDate.of(2026, 4, 17);
System.out.println(fecha.format(f.withLocale(new Locale("es"))));
// "viernes, 17 de abril de 2026"

Locale

El idioma de nombres de meses/días depende del Locale. Por defecto usa el locale de la JVM. Cámbialo con withLocale:

DateTimeFormatter espanol = DateTimeFormatter.ofPattern("EEEE d MMMM yyyy")
.withLocale(new Locale("es"));
DateTimeFormatter ingles = DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy")
.withLocale(Locale.US);

LocalDate f = LocalDate.of(2026, 4, 17);
f.format(espanol); // "viernes 17 abril 2026"
f.format(ingles); // "Friday, April 17, 2026"

Formatters localizados por estilo

DateTimeFormatter.ofLocalizedDate, ofLocalizedTime y ofLocalizedDateTime producen formatters adaptados al locale y a un estilo (FULL, LONG, MEDIUM, SHORT):

DateTimeFormatter f = DateTimeFormatter
.ofLocalizedDate(FormatStyle.FULL)
.withLocale(new Locale("es"));

LocalDate.now().format(f); // "viernes, 17 de abril de 2026"

Ideal cuando quieres el formato "natural" del idioma sin diseñar un patrón a mano.

Parseo robusto: errores y alternativas

El parseo lanza DateTimeParseException si no cuadra. Captúralo o valida antes:

try {
    LocalDate f = LocalDate.parse(entrada, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
} catch (DateTimeParseException e) {
    log.warn("Fecha inválida: {}", entrada, e);
}

Para probar múltiples formatos, usa DateTimeFormatterBuilder.appendOptional o intenta varios en cadena.

Modo estricto vs lenient

Por defecto, DateTimeFormatter es estricto (SMART): "2026-02-30" falla porque febrero no tiene 30. Puedes cambiar con ResolverStyle:

DateTimeFormatter lenient = DateTimeFormatter
.ofPattern("yyyy-MM-dd")
.withResolverStyle(ResolverStyle.LENIENT);

// LENIENT permite overflow: "2026-02-30" se reinterpreta como 2 de marzo

En producción, STRICT es preferible para evitar interpretaciones ambiguas.

Formatter con DateTimeFormatterBuilder

Para casos complejos que requieren composición de partes:

DateTimeFormatter f = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral('-')
.appendValue(ChronoField.DAY_OF_MONTH, 2)
.optionalStart()
.appendLiteral(' ')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.optionalEnd()
.toFormatter();

Permite formatos con partes opcionales, locale-específico, etc.

Patrones recomendados de uso

Formatter como constante

public class Fechas {
    public static final DateTimeFormatter DD_MM_YYYY =
    DateTimeFormatter.ofPattern("dd/MM/yyyy");

    public static final DateTimeFormatter ISO_MINUTE =
    DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
}

Inmutable, thread-safe, reutilizable sin coste.

Formatter por hilo (antigua necesidad con SimpleDateFormat)

Con DateTimeFormatter no lo necesitas: es thread-safe, compártelo libremente.

SimpleDateFormat (legacy)

El antiguo java.text.SimpleDateFormat tenía graves problemas:

  • Mutable: cambiar el locale o patrón modifica la instancia.
  • No thread-safe: en concurrente dabá resultados corruptos silenciosamente.
  • Patrones ligeramente distintos.

Nunca uses SimpleDateFormat en código nuevo: DateTimeFormatter es superior en todos los aspectos.

Resumen

DateTimeFormatter:

  • Inmutable y thread-safe.
  • Formatos ISO listos para usar (ISO_LOCAL_DATE, etc.).
  • Patrones personalizados con ofPattern.
  • Localización con withLocale.
  • Estilos localizados con ofLocalizedDate/Time/DateTime.

Patrones recomendados:

  • Declara formatters como static final en tus clases.
  • Para intercambio técnico, usa ISO.
  • Para usuarios, combina ofLocalizedXxx con withLocale.
  • Captura DateTimeParseException al parsear entradas externas.

Es la base del manejo de fechas en Java moderno. Dominarlo evita un 90% de los bugs clásicos con fechas.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Java es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Java

Explora más contenido relacionado con Java y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Formatear LocalDate, LocalDateTime, ZonedDateTime a String. Parsear cadenas a Temporal con formato predefinido o personalizado. Usar los formatos ISO estándar (ISO_LOCAL_DATE, ISO_DATE_TIME, etc.). Construir DateTimeFormatter.ofPattern('yyyy-MM-dd') con patrones. Localizar formato con withLocale. Manejar errores de parseo con DateTimeParseException.