Cómo comparar fechas en Java

En esta guía, veremos en detalle cómo comparar fechas en Java, utilizando el antiguo paquete java.util, la nueva API java.time y Joda-Time.

Introducción

Las fechas son algo que encontramos en la vida cotidiana, ya sea que se usen para calendarios, programar citas o incluso recordar cumpleaños. Naturalmente, cuando trabajemos con fechas, a menudo necesitaremos saber si una determinada fecha es anterior o posterior a otra, o si representan la misma fecha del calendario.

En este artículo, veremos cómo comparar dos fechas en Java.

Comparando fechas

La clase java.util.Date representa un instante específico en el tiempo con una precisión de milisegundos.

También tiene varios métodos integrados que nos permiten comparar cada instancia de Date con otras instancias de Date:

  • Fecha.compareTo(Fecha fecha)
  • Fecha.equals(Objeto obj)
  • Fecha.antes (Fecha cuando)
  • Fecha.después(Fecha cuando)

Nota: Tenga en cuenta que la clase Date tiene varios métodos obsoletos, incluido el constructor normal. Es una clase antigua, reemplazada por la nueva API de Fecha/Hora en Java 8, cubierta justo después de esta sección. Si está utilizando Java 8+, consulte las secciones después de este.

Si está utilizando una versión anterior a Java 8, consulte la Sección Joda-Time.

Usando Date.compareTo()

Como es habitual con los métodos compareTo() implementados desde la interfaz Comparable, este método acepta otro argumento Date y lo compara con el valor de la Date que invoca, devolviendo 0 si los valores son *iguales *, un valor menor que 0 si la fecha de invocación es anterior al argumento, y un valor mayor que 0 si la fecha de invocación es después del argumento.

De lo contrario, lanza una NullPointerException, si el argumento es null.

Vamos a instanciar dos objetos Date y compararlos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Date date1 = new Date(2020, 1, 1);
Date date2 = new Date(2020, 1, 1);
int result = date1.compareTo(date2);

if(result == 0)
    System.out.println("Both dates are equal");
else if (result < 0)
    System.out.println("Date1 is before Date2");
else
    System.out.println("Date1 is after Date2");

Ejecutar este código da como resultado:

1
Both dates are equal

Nota: Nuevamente, esto solo funciona para versiones de Java anteriores a Java 8. Desde entonces, el constructor Date(int year, int month, int day) ha quedado obsoleto y solo Date(long date) sigue en pie.

Uso de Date.before(), Date.after() y Date.equals()

El método after() prueba si la invocación de Date es después del objeto Date del argumento, devolviendo valores verdadero o falso respectivamente. Sin embargo, before() hace lo mismo, verifica si la Date que invoca es antes del argumento Date.

El método equals() compara la igualdad de dos Fechas y devuelve verdadero si y solo si el argumento no es nulo y es un objeto Date que representa el mismo punto en el tiempo que el objeto invocador , de lo contrario devolverá falso. En otras palabras, comprueba si las referencias apuntan al mismo objeto en memoria.

Los tres arrojan una NullPointerException si el argumento es null. Ahora, podemos hacer que el fragmento de código anterior sea más fácil de usar y legible, si reemplazamos las comprobaciones de enteros con estos métodos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SimpleDateFormat formatter = new SimpleDateFormat("YYYY-MM-dd");
Date date1 = new Date(); // Returns the current date and time
Date date2 = new Date(2021, 2, 12);
        
String formatted1 = formatter.format(date1);
String formatted2 = formatter.format(date2);

if(date1.equals(date2))
    System.out.println("Both dates are equal");
else if(date1.after(date2))
    System.out.println(String.format("%s comes after %s", formatted1, formatted2));
else if(date1.before(date2))
    System.out.println(String.format("%s comes before %s", formatted1, formatted2));

If you're unfamiliar with the SimpleDateFormat class, you can read our Guía sobre formato de fechas en Java con SimpleDateFormat

Ejecutar este código da como resultado:

1
2021-03-24 comes after 2021-02-12

Comparación de fechas locales

A partir de Java 8, tuvimos algunos cambios importantes con respecto a la fecha y la hora. Había muchos problemas con la API de fecha y hora existente, como la falta de clases y métodos seguros para subprocesos, la falta de lógica de zona horaria y el diseño general de la API no era muy fácil de entender.

Guiado por estos problemas, el autor de Joda-Tiempo (un popular sustituto de la deslucida API anterior a Java 8) y Oracle, nos presentó el nuevo Date /Time API a través de java.time.

La clase LocalDate es una de las clases más utilizadas de java.time, junto con LocalTime y LocalDateTime. Representa campos de fecha sin hora o zona horaria con un formato de fecha predeterminado yyyy-mm-dd.

Los métodos incorporados para la comparación son bastante similares a la clase Date:

  • FechaLocal.esDespués()
  • FechaLocal.esAntes()
  • FechaLocal.isEqual()
  • FechaLocal.compareTo()
  • FechaLocal.equals()

Uso de LocalDate.isAfter(), LocalDate.isBefore() y LocalDate.isEqual()

Los tres métodos comprueban si la LocalDate invocada es anterior, posterior o igual al argumento, devolviendo valores booleanos respectivamente.

Vamos a crear instancias de dos instancias de LocalDate, utilizando el método auxiliar of() y pasando los valores year, month y day:

1
2
3
4
5
6
7
8
9
LocalDate date1 = LocalDate.of(2020, 3, 25);
LocalDate date2 = LocalDate.of(2020, 7, 29);

if (date1.isAfter(date2))
    System.out.println(String.format("%s comes after %s", date1, date2));
else if (date1.isBefore(date2))
    System.out.println(String.format("%s comes before %s", date1, date2));
else if (date1.isEqual(date2))
    System.out.println("Both dates are equal");

Esto resulta en:

1
2020-03-25 comes before 2020-07-29

Usando LocalDate.compareTo() y LocalDate.equals()

Nuevamente, como siempre, compareTo() compara instancias y devuelve -1, 0 o 1 dependiendo de los valores de LocalDates. Por otro lado, el método equals() comprueba si las referencias apuntan al mismo objeto en memoria:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
LocalDate date1 = LocalDate.of(2021, 12, 12);
LocalDate date2 = LocalDate.of(2021, 12, 12);
    
if(date1.equals(date2)) {
    System.out.println("Same LocalDate");
} else if(date1.compareTo(date2) > 0) {
    System.out.println(String.format("%s is after %s", date1, date2));
} else if(date1.compareTo(date2) < 0) {
    System.out.println(String.format("%s is before %s", date1, date2));
} else {
    System.out.println(String.format("%s and %s represent the same date", date1, date2));
}

Ya que hemos creado dos LocalDates con los mismos valores, sin usar la palabra clave nuevo que asegura un nuevo Objeto, estos son los mismos LocalDate:

1
Same LocalDate

De hecho, dado que LocalDate no tiene un constructor público, nunca podemos llamar nuevo en él.

Comparación de calendarios

La clase java.util.Calendar es parte de la antigua API de Fecha/Hora, y se usa principalmente para la conversión entre instantes y campos de calendario, como DÍA_DE_MES, etc.

También pueden contener datos relacionados con la fecha y, por supuesto, se pueden comparar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Calendar c1 = Calendar.getInstance();
c1.set(2020, Calendar.AUGUST, 15);
Calendar c2 = Calendar.getInstance();
c2.set(2020, Calendar.JULY, 4);

if(c1.equals(c2))
    System.out.println("Calendars are equal");
else if(c1.after(c2))
    System.out.println(c1.getTime() + " comes after " + c1.getTime());
else if(c1.before(c2))
    System.out.println(c1.getTime() + " comes before " + c2.getTime());

Esto resulta en:

1
Sat Aug 15 14:22:24 UTC 2020 comes after Sat Aug 15 14:22:24 UTC 2020

Comparación con Joda-Time

Joda-Time era la biblioteca estandarizada de fecha y hora de terceros para Java, anterior a Java 8. Abordaba los problemas presentes en la API original, que había muchos. También sirvió como inspiración y punto de partida para revisar toda la API, lo que dio como resultado el paquete java.time actual.

Con Joda-Time podemos utilizar hasta ocho sistemas de calendario diferentes, manipular zonas horarias o incluso definir nuestros propios calendarios personalizados. Pero lo que es más importante, no tenemos que preocuparnos por la mutabilidad.

En lugar de representar fechas como instancias en el tiempo, Joda-Time las representa como fechas reales. Para mostrar cómo comparamos fechas usando Joda-Time, usaremos la clase LocalDate (clase Joda-Time) que es segura para subprocesos.

Usando DateTimeComparator

La clase DateTimeComparator está hecha específicamente para comparar fechas. Funciona con ReadableInstants, Strings, Dates, Calendars y Longs. Entonces, tendremos que convertir nuestras Joda-Time LocalDates, a Dates:

1
2
3
4
5
6
LocalDate date1 = new LocalDate(2021, 11, 11);
LocalDate date2 = new LocalDate(2021, 12, 12);

int result = DateTimeComparator.getInstance()
             .compare(date1.toDate(), date2.toDate());
System.out.println(result);

Este método devuelve un resultado entero, al igual que el método compareTo(), donde -1 significa que la primera fecha es anterior a la segunda, 0 significa que son iguales y 1 significa que el primer objeto está después del segundo:

1
-1

Usando LocalDate.isEqual()

De manera similar a la API de Fecha/Hora actual, podemos usar isEqual():

1
2
3
4
LocalDate date1 = new LocalDate(2021, 11, 11);
LocalDate date2 = new LocalDate(2021, 12, 12);

System.out.println(date1.isEqual(date2));

Esto da como resultado un booleano:

1
false

Usando LocalDate.isBefore() y LocalDate.isAfter()

Nuevamente, al igual que la API de Java 8, podemos usar los métodos isBefore() y isAfter() para comparar dos LocalDates:

1
2
3
4
5
LocalDate date1 = new LocalDate(2021, 11, 11);
LocalDate date2 = new LocalDate(2021, 12, 12);

System.out.println(date1.isBefore(date2));
System.out.println(date1.isAfter(date2));

Esto resulta en:

1
2
true
false

Usando LocalDate.compareTo()

Extendiendo Comparable, la clase LocalDate tiene un método compareTo(), que devuelve un número entero mayor, menor o igual a cero. Los campos se comparan en orden y el primer campo que no es igual se usa para determinar el resultado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
LocalDate today = LocalDate.now();
LocalDate tomorrow = new LocalDate(today.getYear(),
                                   today.getMonthOfYear(), 
                                   today.getDayOfMonth() + 1);
int result = today.compareTo(tomorrow);

if(result == 0)
    System.out.println("Dates are equal");
else if (result < 0)
    System.out.println(today + " is before " + tomorrow);
else
    System.out.println(today + " is after " + tomorrow);
    

Esto resulta en:

1
2021-04-14 is before 2021-04-15

Usando LocalDate.equals()

Y finalmente, el método equals() verifica la igualdad de referencia de objetos:

1
2
3
LocalDate date1 = new LocalDate(2021, 3, 25);
LocalDate date2 = new LocalDate(2021, 2, 25);
System.out.println(date1.equals(date2));

Esto resulta en:

1
false

Conclusión

En este artículo, hemos cubierto varias formas de comparar fechas en Java. Hemos usado la clase java.util.Date ahora en desuso, para proyectos más antiguos, y la clase java.util.Calendar.

Luego, saltamos a la API de fecha/hora de Java 8 más moderna y, finalmente, a Joda-Time.