Cómo comparar fechas en Java (LocalDate, DateTime, mejores prácticas)

目次

1. Lo que aprenderás en este artículo (Conclusión primero)

Cuando los desarrolladores buscan “comparación de fechas en Java”, suelen querer una forma clara y confiable de comparar fechas sin errores inesperados. Este artículo te da exactamente eso. Al final de esta guía, entenderás:
  • La mejor manera de comparar fechas en Java usando la API moderna java.time
  • Qué clase de fecha/hora de Java debes usar según tu situación
  • Cómo realizar de manera segura verificaciones de antes / después / igual
  • Por qué java.util.Date causa confusión y cómo manejarlo correctamente
  • Errores comunes que cometen los principiantes al comparar fechas en Java
  • Mejores prácticas usadas en aplicaciones Java del mundo real
Respuesta corta: Si quieres comparar fechas en Java correctamente, usa LocalDate, LocalDateTime o Instant de java.time, no Date crudo ni comparación de cadenas.
Este artículo está escrito para:
  • Principiantes en Java que se sienten confundidos por la comparación de fechas
  • Desarrolladores que mantienen código heredado
  • Ingenieros que quieren código Java limpio, sin errores y a prueba de futuro

1.1 El problema principal con la comparación de fechas en Java

La comparación de fechas en Java no es difícil — pero es fácil hacerlo mal. Muchos problemas provienen de estos errores:
  • Comparar cadenas de fechas en lugar de objetos de fecha
  • Usar java.util.Date sin entender los componentes de tiempo
  • Mezclar lógica solo de fecha con lógica de fecha-hora
  • Ignorar las zonas horarias
  • Asumir que “mismo día” significa “mismo sello de tiempo”
Estos errores a menudo se compilan sin problemas pero fallan silenciosamente en producción. Por eso, Java moderno recomienda fuertemente la API de Tiempo de Java (java.time), introducida en Java 8.

1.2 Una regla que resuelve la mayoría de los problemas

Antes de escribir cualquier código de comparación, siempre responde esta pregunta:
¿Estoy comparando una fecha o una fecha-hora?
Esta sola decisión determina qué clase debes usar.
What you need to compareRecommended class
Calendar date only (YYYY-MM-DD)LocalDate
Date + time (no time zone)LocalDateTime
Exact moment in time (global)Instant
Date-time with time zoneZonedDateTime
Si eliges la clase correcta, la comparación de fechas se vuelve simple y legible.

1.3 Los casos de uso más comunes

La mayoría de las búsquedas de comparar fechas en Java caen en estos patrones:
  • ¿Está la fecha A antes que la fecha B?
  • ¿Está la fecha A después que la fecha B?
  • ¿Son dos fechas iguales?
  • ¿Está una fecha dentro de un rango?
  • ¿Cuántos días u horas hay entre dos fechas?
La buena noticia es que java.time maneja todos estos de manera limpia usando métodos expresivos como:
  • isBefore()
  • isAfter()
  • isEqual()
  • compareTo()
Los cubriremos todos paso a paso.

1.4 Por qué debes evitar la comparación de fechas basada en cadenas

Un error común de principiantes se ve así:
"2026-1-9".compareTo("2026-01-10");
Esto compara texto, no fechas. Aunque parezca funcionar en algunos casos, se rompe fácilmente cuando los formatos difieren. Esta es una de las causas más frecuentes de errores ocultos en aplicaciones Java.
Regla: Si tus fechas son cadenas, analízalas en objetos de fecha primero — siempre.
Lo cubriremos correctamente más adelante en el artículo.

1.5 En qué se enfoca esta guía (y en qué no)

Esta guía se enfoca en:
  • Comparación práctica de fechas en Java
  • Patrones de codificación del mundo real
  • Explicaciones claras para principiantes
  • Mejores prácticas para Java moderno (Java 8+)
No se enfoca en:
  • Peculiaridades históricas de las API anteriores a Java 8 (a menos que sea necesario)
  • Matemáticas de calendario de bajo nivel
  • Explicaciones excesivamente teóricas
El objetivo es simple: Ayudarte a escribir código de comparación de fechas en Java correcto con confianza.

2. Entendiendo las clases de fecha y hora en Java (Antes de comparar)

Antes de comparar fechas en Java, debes entender qué representa realmente cada clase de fecha/hora. La mayoría de la confusión proviene de usar la clase incorrecta para el trabajo.

2.1 Dos generaciones de API de fecha en Java

Java tiene dos sistemas diferentes de fecha/hora:

API heredada (Antigua, propensa a errores)

  • java.util.Date
  • java.util.Calendar

API moderna (Recomendada)

  • java.time.LocalDate
  • java.time.LocalDateTime
  • java.time.ZonedDateTime
  • java.time.Instant
Mejor práctica: Siempre prefiera java.time. Use las APIs heredadas solo cuando no pueda evitarlas.

2.2 LocalDate: Cuando solo necesita la fecha

Use LocalDate cuando solo le importe la fecha del calendario. Ejemplos:
  • Cumpleaños
  • Fechas límite
  • Días festivos
  • Fechas de vencimiento
    LocalDate today = LocalDate.now();
    LocalDate deadline = LocalDate.of(2026, 1, 31);
    
Características clave:
  • Almacena año, mes y día
  • Sin hora, sin zona horaria
  • Ideal para comparación de fechas
Esta es la clase más comúnmente usada para la comparación de fechas en Java.

2.3 LocalDateTime: Cuando la hora importa

Use LocalDateTime cuando tanto la fecha como la hora son importantes. Ejemplos:
  • Horas de reserva
  • Programas de eventos
  • Marcas de tiempo de registro (sin zona horaria)
    LocalDateTime meeting =
        LocalDateTime.of(2026, 1, 9, 18, 30);
    
Características clave:
  • Incluye fecha y hora
  • Sin información de zona horaria
  • Preciso pero solo en contexto local

2.4 ZonedDateTime: Cuando la ubicación importa

Si los usuarios o sistemas están en diferentes zonas horarias, use ZonedDateTime.
ZonedDateTime tokyo =
    ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
Esta clase:
  • Almacena fecha, hora y zona horaria
  • Maneja el horario de verano correctamente
  • Es lo mejor para mostrar fecha-hora a los usuarios

2.5 Instant: La mejor opción para comparación y almacenamiento

Instant representa un momento único en el tiempo, globalmente.
Instant now = Instant.now();
Por qué importa:
  • Independiente de la zona horaria
  • Perfecto para comparación
  • Ideal para bases de datos y registros
Mejor práctica usada en sistemas de producción: Almacene y compare fechas como Instant, convierta a ZonedDateTime solo para mostrar.

2.6 Resumen: Elija la clase correcta primero

RequirementUse this class
Date onlyLocalDate
Date + timeLocalDateTime
Global comparisonInstant
User-facing timeZonedDateTime
Una vez que la clase es correcta, la comparación de fechas se vuelve directa y segura.

3. Comparación de fechas en Java con java.time (Sección más importante)

Esta sección es el núcleo de la comparación de fechas en Java. Si entiende esta parte, puede manejar la mayoría de los escenarios de comparación de fechas del mundo real de manera segura y confiada.

3.1 Comparación básica de fechas: isBefore, isAfter, isEqual

Al usar java.time, la comparación de fechas está diseñada para ser clara y legible.

Ejemplo con LocalDate

LocalDate date1 = LocalDate.of(2026, 1, 9);
LocalDate date2 = LocalDate.of(2026, 1, 10);

System.out.println(date1.isBefore(date2)); // true
System.out.println(date1.isAfter(date2));  // false
System.out.println(date1.isEqual(date2));  // false
Estos nombres de métodos describen exactamente lo que hacen:
  • isBefore() → verifica si una fecha es anterior
  • isAfter() → verifica si una fecha es posterior
  • isEqual() → verifica si ambas fechas representan el mismo día
Esto hace que su código sea fácil de leer y difícil de malinterpretar, lo cual es excelente para la mantenibilidad y tutoriales amigables con SEO.

3.2 Comparación de fecha y hora con LocalDateTime

Los mismos métodos de comparación funcionan para LocalDateTime.
LocalDateTime t1 =
    LocalDateTime.of(2026, 1, 9, 18, 30);
LocalDateTime t2 =
    LocalDateTime.of(2026, 1, 9, 19, 0);

System.out.println(t1.isBefore(t2)); // true
Diferencia importante:
  • Dos valores en la misma fecha pero con horas diferentes no son iguales
  • isEqual() verifica tanto la fecha como la hora
Este comportamiento es correcto, pero los principiantes a menudo esperan que “mismo día” sea verdadero — lo que lleva a la siguiente sección.

3.3 Cómo verificar “Mismo Día” correctamente

Si quiere saber si dos marcas de tiempo caen en el mismo día del calendario, no compare LocalDateTime directamente. En su lugar, conviértalas a LocalDate.
boolean sameDay =
    t1.toLocalDate().isEqual(t2.toLocalDate());
Por qué esto funciona:
  • Los componentes de hora se eliminan
  • La comparación se vuelve solo de fecha
  • La intención es cristalina en el código
.> Mejor práctica:
Comprobación del mismo día = convertir a LocalDate primero.

3.4 Uso de compareTo() para ordenar y clasificar

El método compareTo() es útil cuando necesitas un resultado de comparación numérico.
int result = date1.compareTo(date2);

if (result < 0) {
    System.out.println("date1 is before date2");
}
Cómo funciona el resultado:
  • Negativo → anterior
  • Cero → igual
  • Positivo → posterior
Este método es especialmente potente para ordenar colecciones.
List<LocalDate> dates = List.of(
    LocalDate.of(2026, 1, 10),
    LocalDate.of(2026, 1, 8),
    LocalDate.of(2026, 1, 9)
);

dates.stream()
     .sorted()
     .forEach(System.out::println);
Como LocalDate implementa Comparable, Java sabe cómo ordenarlo de forma natural.

3.5 equals() vs isEqual(): ¿Cuál deberías usar?

Para LocalDate, ambos suelen devolver el mismo resultado.
date1.equals(date2);
date1.isEqual(date2);
Sin embargo, sirven para propósitos diferentes:
  • isEqual() → comparación semántica de fechas (recomendado en lógica)
  • equals() → igualdad de objetos (comúnmente usado en colecciones)
Usar isEqual() mejora la legibilidad del código, especialmente en tutoriales y lógica de negocio.

3.6 Manejo seguro de null en la comparación de fechas

Uno de los errores en tiempo de ejecución más comunes es NullPointerException.
LocalDate date = null;
date.isBefore(LocalDate.now()); // throws exception
Para evitarlo:
  • Define siempre qué significa null en tu sistema
  • Verifica null antes de comparar
  • Considera encapsular la lógica en métodos auxiliares
Ejemplo:
boolean isBefore(LocalDate a, LocalDate b) {
    if (a == null || b == null) {
        return false;
    }
    return a.isBefore(b);
}
Las decisiones de diseño respecto a null deben ser explícitas, no accidentales.

3.7 Puntos clave para la comparación de fechas con java.time

  • Usa isBefore, isAfter, isEqual para mayor claridad
  • Usa compareTo para ordenar y lógica numérica
  • Convierte a LocalDate para comprobaciones del mismo día
  • Maneja null de forma intencional
Una vez que domines estos patrones, la comparación de fechas en Java se vuelve predecible y segura.

4. Comparación de fechas con java.util.Date (código heredado)

Incluso hoy, muchos proyectos Java aún dependen de java.util.Date. Necesitas saber cómo manejarlo — sin introducir errores.

4.1 Comparación básica con before() y after()

Date ofrece métodos de comparación simples.
Date d1 = new Date();
Date d2 = new Date(System.currentTimeMillis() + 1000);

System.out.println(d1.before(d2)); // true
System.out.println(d1.after(d2));  // false
Esto funciona, pero recuerda:
  • Date siempre incluye tiempo hasta milisegundos
  • No existe el concepto de “solo fecha”

4.2 La mayor trampa: “Mismo día” no existe en Date

Con Date, estos no son iguales:
  • 2026-01-09 00:00
  • 2026-01-09 12:00
    d1.equals(d2); // false
    
Esta es una de las fuentes más comunes de errores lógicos.

4.3 Convertir Date a java.time inmediatamente (recomendado)

El enfoque más seguro es convertir el Date heredado a objetos java.time lo antes posible.
Date legacyDate = new Date();

Instant instant = legacyDate.toInstant();
LocalDate localDate =
    instant.atZone(ZoneId.systemDefault()).toLocalDate();
Una vez convertido, usa java.time exclusivamente para comparación y lógica.

4.4 Mejores prácticas para sistemas heredados

  • Acepta Date solo en los límites del sistema
  • Convierte a Instant o LocalDate
  • Realiza todas las comparaciones usando java.time
Regla usada en sistemas Java profesionales: APIs heredadas en los bordes, APIs modernas en el núcleo.

5. Comparación de cadenas de fecha en Java (la forma correcta)

Una de las intenciones de búsqueda más comunes detrás de “java date comparison” es cómo comparar cadenas de fecha de forma segura. Esto también es una de las fuentes más comunes de errores en aplicaciones Java.

5.1 Por qué nunca debes comparar cadenas de fecha directamente

.A primera vista, comparar cadenas parece tentador:
"2026-1-9".compareTo("2026-01-10");
Esta comparación es lexicográfica, no cronológica. Problemas con la comparación de cadenas:
  • Los diferentes formatos rompen el orden
  • La falta de ceros iniciales causa resultados incorrectos
  • Las diferencias de configuración regional y formato introducen errores silenciosos
Aunque funcione una vez, eventualmente fallará.
Regla: Nunca compares directamente cadenas de fechas. Siempre conviértelas a objetos de fecha.

5.2 El flujo de trabajo correcto: Analizar → Comparar

El flujo de trabajo seguro y profesional es siempre:
  1. Analiza la cadena a un objeto de fecha/hora
  2. Compara usando java.time

Ejemplo de formato ISO (yyyy-MM-dd)

String s1 = "2026-01-09";
String s2 = "2026-01-10";

LocalDate d1 = LocalDate.parse(s1);
LocalDate d2 = LocalDate.parse(s2);

System.out.println(d1.isBefore(d2)); // true
Este enfoque es:
  • Legible
  • Seguro
  • Totalmente soportado por Java

5.3 Manejo de formatos de fecha personalizados con DateTimeFormatter

En sistemas del mundo real, los formatos de fecha varían:
  • 2026/01/09
  • 01-09-2026
  • 2026-01-09 18:30
Para estos casos, define explícitamente el formato.
DateTimeFormatter formatter =
    DateTimeFormatter.ofPattern("yyyy/MM/dd");

LocalDate date =
    LocalDate.parse("2026/01/09", formatter);
Para fecha y hora:
DateTimeFormatter formatter =
    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

LocalDateTime dateTime =
    LocalDateTime.parse("2026-01-09 18:30", formatter);
Los formatos explícitos hacen que tu código sea predecible y mantenible.

5.4 Manejo de múltiples formatos posibles (Validación de entrada)

Al tratar con entrada de usuarios o APIs externas, los formatos pueden variar. Una estrategia segura:
  • Intenta los formatos conocidos en orden
  • Falla rápidamente si ninguno coincide
    List<DateTimeFormatter> formatters = List.of(
        DateTimeFormatter.ofPattern("yyyy-MM-dd"),
        DateTimeFormatter.ofPattern("yyyy/MM/dd")
    );
    
    LocalDate parseDate(String text) {
        for (DateTimeFormatter f : formatters) {
            try {
                return LocalDate.parse(text, f);
            } catch (Exception ignored) {}
        }
        throw new IllegalArgumentException("Invalid date format");
    }
    
Este enfoque es común en sistemas de nivel de producción.

5.5 Manejo de excepciones y estrategia de validación

Parsear fechas inválidas lanza una excepción:
LocalDate.parse("2026-99-99"); // throws exception
Mejores prácticas:
  • Trata las fechas inválidas como errores de validación
  • Registra los fallos de parseo
  • Nunca ignores silenciosamente la entrada inválida
Fallar temprano previene corrupción de datos más adelante.

5.6 Puntos clave para la comparación de fechas basada en cadenas

  • La comparación de cadenas es poco fiable
  • Siempre analiza las cadenas a LocalDate o LocalDateTime
  • Usa DateTimeFormatter explícitamente
  • Valida y maneja los errores de forma intencional

6. Comprobaciones de rangos de fechas en Java (Lógica de período / plazo)

Otro tema muy buscado relacionado con comparación de fechas en Java es verificar si una fecha cae dentro de un rango. Esto es extremadamente común en:
  • Sistemas de reservas
  • Periodos de campaña
  • Control de acceso
  • Verificaciones de validez de contratos

6.1 Define claramente los límites inclusivos vs exclusivos

Antes de escribir código, decide:
  • ¿Se incluye la fecha de inicio?
  • ¿Se incluye la fecha de fin?

Ejemplo de rango inclusivo (inicio ≤ objetivo ≤ fin)

boolean inRange =
    !target.isBefore(start) && !target.isAfter(end);
Esto se lee naturalmente:
  • No antes del inicio
  • No después del fin

6.2 Fecha de fin exclusiva (Muy común en la práctica)

Para plazos y accesos basados en tiempo:
boolean valid =
    !target.isBefore(start) && target.isBefore(end);
Significa:
  • El inicio es inclusivo
  • El fin es exclusivo
Este patrón evita ambigüedades y errores de desbordamiento de uno.

6.3 Comprobaciones de rango con LocalDateTime

La misma lógica se aplica a valores de fecha y hora.
boolean active =
    !now.isBefore(startTime) && now.isBefore(endTime);
Esto se usa ampliamente en:
  • Sistemas de reservas
  • Lógica de expiración de sesiones
  • Toggles de características

6.4 Manejo de Rangos Abiertos (null Values)

En sistemas reales, las fechas de inicio o fin pueden faltar. Política de ejemplo:
  • Inicio null → válido desde el principio de los tiempos
  • Fin null → válido indefinidamente
    boolean isWithin(
        LocalDate target,
        LocalDate start,
        LocalDate end
    ) {
        if (start != null && target.isBefore(start)) {
            return false;
        }
        if (end != null && target.isAfter(end)) {
            return false;
        }
        return true;
    }
    
Encapsular esta lógica previene duplicaciones y errores.

6.5 Conciencia de Zona Horaria en Verificaciones de Rangos

Al verificar rangos que involucran “hoy”:
LocalDate today =
    LocalDate.now(ZoneId.of("Asia/Tokyo"));
Siempre sé explícito sobre la zona horaria si:
  • Los usuarios están en diferentes regiones
  • Los servidores se ejecutan en diferentes entornos

6.6 Mejores Prácticas para la Lógica de Rangos de Fechas

  • Decide los límites antes de codificar
  • Prefiere métodos auxiliares
  • Evita condiciones complejas en línea
  • Documenta las reglas de negocio claramente

7. Cálculo de Diferencias de Fecha y Hora en Java (Period / Duration)

Después de aprender cómo comparar fechas, el siguiente requisito común es calcular qué tan separadas están dos fechas o tiempos. Java proporciona dos herramientas diferentes para este propósito: Period y Duration. Entender la diferencia entre ellas es crítico para obtener resultados correctos.

7.1 Diferencias Basadas en Fechas: Period (Años, Meses, Días)

Usa Period cuando quieras una diferencia basada en el calendario.
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate end   = LocalDate.of(2026, 1, 31);

Period period = Period.between(start, end);

System.out.println(period.getYears());  // 0
System.out.println(period.getMonths()); // 0
System.out.println(period.getDays());   // 30
Características de Period:
  • Expresa diferencias en años, meses y días
  • Respeta los límites del calendario
  • Ideal para diferencias legibles por humanos
Casos de uso típicos:
  • Cálculo de edad
  • Períodos de suscripción
  • Duraciones de contratos

7.2 Obtener Días Totales Entre Fechas

Si solo necesitas el número total de días, usa ChronoUnit.
long days =
    ChronoUnit.DAYS.between(start, end);
Esto devuelve:
  • Un valor numérico único
  • Independiente de meses o años
Esto a menudo se prefiere para sistemas de facturación y contadores.

7.3 Diferencias Basadas en Tiempo: Duration (Horas, Minutos, Segundos)

Usa Duration para diferencias basadas en tiempo.
LocalDateTime t1 =
    LocalDateTime.of(2026, 1, 9, 18, 0);
LocalDateTime t2 =
    LocalDateTime.of(2026, 1, 9, 20, 30);

Duration duration = Duration.between(t1, t2);

System.out.println(duration.toHours());   // 2
System.out.println(duration.toMinutes()); // 150
Características de Duration:
  • Mide el tiempo en segundos y nanosegundos
  • Ignora los límites del calendario
  • Siempre trata un día como 24 horas

7.4 Trampa Importante: Hora de Verano

Cuando se involucran zonas horarias y hora de verano:
  • Un día puede tener 23 o 25 horas
  • Duration aún asume 24 horas
Para manejar esto correctamente, usa ZonedDateTime y convierte a Instant.
Duration duration =
    Duration.between(
        zonedDateTime1.toInstant(),
        zonedDateTime2.toInstant()
    );

7.5 Elegir la Herramienta Correcta para Diferencias

RequirementUse
Human-friendly date differencePeriod
Total daysChronoUnit.DAYS
Time differenceDuration
Global elapsed timeInstant + Duration

8. Trampas de Zona Horaria en la Comparación de Fechas en Java

Las zonas horarias son una de las fuentes más peligrosas de errores en la comparación de fechas.

8.1 Por Qué LocalDateTime Puede Ser Engañoso

LocalDateTime now = LocalDateTime.now();
Este valor:
  • Depende de la zona horaria predeterminada del sistema
  • No tiene información de ubicación
  • Puede cambiar de comportamiento entre entornos
Para aplicaciones globales, esto es riesgoso.

8.2 ZonedDateTime: Cuando la Ubicación Importa

ZonedDateTime tokyo =
    ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime newYork =
    ZonedDateTime.now(ZoneId.of("America/New_York"));
Ambos representan el mismo instante, pero muestran diferentes horas locales. Casos de uso:
  • Pantallas orientadas al usuario
  • Programación específica por región

8.3 Instant: La opción más segura para comparación y almacenamiento

Instant a = Instant.now();
Instant b = Instant.now();

System.out.println(a.isBefore(b));
Por qué los profesionales prefieren Instant:
  • Independiente de la zona horaria
  • Perfecto para comparaciones
  • Ideal para bases de datos y registros
Mejor práctica de la industria: Almacene y compare el tiempo como Instant, convierta a ZonedDateTime solo para la visualización.

8.4 Patrón de arquitectura recomendado

Un enfoque probado y seguro:
  1. Entrada → ZonedDateTime
  2. Lógica interna y almacenamiento → Instant
  3. Salida → ZonedDateTime
Esto elimina la mayoría de los errores de zona horaria.

9. Resumen: Guía rápida de comparación de fechas en Java

9.1 ¿Qué clase debería usar?

ScenarioClass
Date onlyLocalDate
Date + timeLocalDateTime
Global comparisonInstant
User displayZonedDateTime

9.2 Métodos de comparación para recordar

  • Antes / Después → isBefore() / isAfter()
  • Igualdad → isEqual()
  • Orden → compareTo()

9.3 Errores comunes a evitar

  • Comparar cadenas de fecha
  • Usar Date para lógica de negocio
  • Ignorar zonas horarias
  • Mezclar lógica de solo fecha y de fecha y hora
  • Límites de rango ambiguos

Preguntas frecuentes

P1. ¿Cuál es la mejor manera de comparar fechas en Java?

Utilice la API java.time (LocalDate, LocalDateTime, Instant). Evite java.util.Date siempre que sea posible.

P2. ¿Cómo verifico si dos fechas están en el mismo día?

Convierta ambos valores a LocalDate y compárelos usando isEqual().

P3. ¿Puedo comparar cadenas de fecha directamente en Java?

No. Siempre analice las cadenas a objetos de fecha antes de compararlas.

P4. ¿Cómo manejo correctamente las zonas horarias?

Utilice Instant para almacenamiento y comparación, y ZonedDateTime para la visualización.

P5. ¿Está java.util.Date obsoleto?

No está formalmente obsoleto, pero se desaconseja fuertemente para nuevos desarrollos.

Reflexión final

Si elige primero la clase de fecha/hora correcta, la comparación de fechas en Java se vuelve simple, predecible y libre de errores.