Comparando dos fechas java.util. para ver si están en el mismo día

250

Necesito comparar dos Dates (por ejemplo, date1y date2) y llegar a una boolean sameDayque sea verdadera para las dos Dates compartidas el mismo día, y falsa si no lo son.

¿Cómo puedo hacer esto? Parece que hay un torbellino de confusión aquí ... y me gustaría evitar atraer otras dependencias más allá del JDK si es posible.

para aclarar: si date1y date2comparte el mismo año, mes y día, entonces sameDayes cierto, de lo contrario es falso. Me doy cuenta de que esto requiere el conocimiento de una zona horaria ... sería bueno pasar en una zona horaria, pero puedo vivir con GMT o la hora local siempre que sepa cuál es el comportamiento.

de nuevo, para aclarar:

date1 = 2008 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = true

date1 = 2009 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
  => sameDate = false

date1 = 2008 Aug 03 12:00:00
date2 = 2008 Jun 03 12:00:00
  => sameDate = false
Jason S
fuente
Solo para aclarar: ¿desea saber si dos objetos Date caen el mismo día de la semana?
Rob Heiser
¿Desea comparar la fecha completa (día, mes, año) o solo el día del mes?
XpiritO
1
@Rob: no, el mismo día / mes / año ... lo aclararé.
Jason S
entonces, ¿por qué no usas "iguales"?
XpiritO
77
Porque no son iguales si la hora / minuto / segundo son diferentes.
Jason S

Respuestas:

416
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTime(date1);
cal2.setTime(date2);
boolean sameDay = cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
                  cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);

Tenga en cuenta que "el mismo día" no es un concepto tan simple como parece cuando pueden involucrarse diferentes zonas horarias. El código anterior calculará para ambas fechas el día relativo a la zona horaria utilizada por la computadora en la que se está ejecutando. Si esto no es lo que necesita, debe pasar la (s) zona (s) horaria (s) correspondiente (s) a las Calendar.getInstance()llamadas, después de haber decidido exactamente qué quiere decir con "el mismo día".

Y sí, Joda Time's LocalDateharía que todo fuera mucho más limpio y fácil (aunque las mismas dificultades relacionadas con las zonas horarias estarían presentes).

Michael Borgwardt
fuente
Gracias, parece que hará lo que quiero. En mi caso, estoy comparando fechas sucesivas en una serie, por lo que parece que podría usar instancias de calendario en lugar de instancias de fecha en mi serie.
Jason S
1
@ Jason Eso puede o no ser una buena idea. El principal problema con Calendar es que es una clase muy pesada con mucho estado interno, algunos de los cuales se usan en su implementación equals (). Si no copulas tus fechas para la igualdad y no las pones en HashMaps, deberías estar bien.
Michael Borgwardt
genial, gracias, solo estoy usando la lógica de comparar el actual y el día anterior que se pregunta aquí. las funciones "igual" y "código hash" nunca deberían llamarse.
Jason S
1
@UmerHayat: el código compara el día del año, no el día del mes. Una comparación menos necesaria, código más corto.
Michael Borgwardt
2
Sugerencia: Compare DAY_OF_YEAR primero, no tendrá que verificar el año. Ok, no es como comparar int es realmente caro, pero ...
Martin P.
332

Qué tal si:

SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
return fmt.format(date1).equals(fmt.format(date2));

También puede establecer la zona horaria en SimpleDateFormat, si es necesario.

Binil Thomas
fuente
2
(De todos modos, estoy usando SimpleDateFormat de todos modos en mi caso, por lo que parece apropiado)
Jason S
8
De hecho, estoy sorprendido de ver esto, pero incluso desde el punto de vista del rendimiento, este SimpleDateFormatmétodo es realmente más rápido que el otro mencionado aquí usando Calendars. En promedio, toma la mitad del tiempo que el Calendarmétodo. (Al menos en mi sistema). ¡Prestigio!
Michael Plautz
La mayor parte del costo de esta respuesta está en la creación de SimpleDateFormat. Póngalo en un hilo Campo local en un singleton si desea un rendimiento aún mejor
Thierry
1
También hago esto en Swift. Es sorprendente cómo una cosa tan simple requiere el mismo truco en la mayoría de los idiomas. C # es la excepción - if (d1.Date == d2.Date) ...
alpsystems.com
2
Esto es feo; sorprendido de ver un truco como este votado.
Zsolt Safrany
162

Utilizo el paquete "apache commons lang" para hacer esto (es decir, org.apache.commons.lang.time.DateUtils )

boolean samedate = DateUtils.isSameDay(date1, date2);  //Takes either Calendar or Date objects
Brent Watson
fuente
2
Esto utiliza una dependencia externa ... pero es bueno saberlo para el futuro.
Jason S
20
Solo copia la fuente y llámalo un día :)
Anton Kuzmin
Pero arroja una excepción de argumento ilegal si alguno de los días es nulo, lo que no es ideal en algunos casos.
raviraja
1
Como se comentó en stackoverflow.com/a/2517824/1665809, esta solución puede no ser apropiada si hay diferentes zonas horarias.
mrod
24

Puede evitar las dependencias externas y el impacto en el rendimiento del uso del Calendario calculando el Número de día juliano para cada una de las fechas y luego comparándolas:

public static boolean isSameDay(Date date1, Date date2) {

    // Strip out the time part of each date.
    long julianDayNumber1 = date1.getTime() / MILLIS_PER_DAY;
    long julianDayNumber2 = date2.getTime() / MILLIS_PER_DAY;

    // If they now are equal then it is the same day.
    return julianDayNumber1 == julianDayNumber2;
}
Aye Jay
fuente
44
Pero tenga en cuenta que esto no tiene en cuenta los cambios en la duración del día debido al horario de verano. Pero luego, las líneas rectas Dateno encapsulan la noción de zonas horarias, por lo que no hay forma de solucionar esto de manera no arbitraria.
AyeJay
3
Por cierto, esta es la solución más rápida: muchas gracias, exactamente lo que estaba buscando; como tengo que consultar muchas fechas ...
Ridcully
2
Nitpick: date1.getTime () no devuelve el número de día juliano, sino milisegundos desde 1970-01-01. Gran diferencia.
Según Lindberg el
2
Esto realiza la comparación en la zona horaria UTC. Si quería su zona horaria local o algún otro lugar del planeta, no puede saber si el resultado que obtiene es correcto.
Ole VV
20

Joda-Time

En cuanto a agregar una dependencia, me temo que java.util.Date & .Calendar realmente son tan malos que lo primero que hago a cualquier proyecto nuevo es agregar la biblioteca Joda-Time. En Java 8 puede usar el nuevo paquete java.time, inspirado en Joda-Time.

El núcleo de Joda-Time es la DateTimeclase. A diferencia de java.util.Date, entiende su zona horaria asignada ( DateTimeZone). Al convertir de juDate, asigne una zona.

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTimeQuébec = new DateTime( date , zone );

LocalDate

Una forma de verificar si dos fechas y horas aterrizan en la misma fecha es convertirlas en LocalDateobjetos.

Esa conversión depende de la zona horaria asignada. Para comparar LocalDateobjetos, deben haberse convertido con la misma zona.

Aquí hay un pequeño método de utilidad.

static public Boolean sameDate ( DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1 );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( dt1.getZone() ) );
    Boolean match = ld1.equals( ld2 );
    return match;
}

Mejor sería otro argumento, especificando la zona horaria en lugar de asumir que se debe usar la primera zona horaria del objeto DateTime.

static public Boolean sameDate ( DateTimeZone zone , DateTime dt1 , DateTime dt2 )
{
    LocalDate ld1 = new LocalDate( dt1.withZone( zone ) );
    // LocalDate determination depends on the time zone.
    // So be sure the date-time values are adjusted to the same time zone.
    LocalDate ld2 = new LocalDate( dt2.withZone( zone ) );
    return ld1.equals( ld2 );
}

Representación de cuerdas

Otro enfoque es crear una representación de cadena de la porción de fecha de cada fecha-hora, luego comparar cadenas.

Nuevamente, la zona horaria asignada es crucial.

DateTimeFormatter formatter = ISODateTimeFormat.date();  // Static method.
String s1 = formatter.print( dateTime1 );
String s2 = formatter.print( dateTime2.withZone( dt1.getZone() )  );
Boolean match = s1.equals( s2 );
return match;

Lapso de tiempo

La solución generalizada es definir un período de tiempo, luego preguntar si el período contiene su objetivo. Este código de ejemplo está en Joda-Time 2.4. Tenga en cuenta que las clases relacionadas con la "medianoche" están en desuso. En su lugar, use el withTimeAtStartOfDaymétodo. Joda-Time ofrece tres clases para representar un lapso de tiempo de varias maneras: intervalo, período y duración.

Usando el enfoque "medio abierto" donde el comienzo del tramo es inclusivo y el final exclusivo.

La zona horaria del objetivo puede ser diferente a la zona horaria del intervalo.

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime target = new DateTime( 2012, 3, 4, 5, 6, 7, timeZone );
DateTime start = DateTime.now( timeZone ).withTimeAtStartOfDay();
DateTime stop = start.plusDays( 1 ).withTimeAtStartOfDay();
Interval interval = new Interval( start, stop );
boolean containsTarget = interval.contains( target );

java.time

Java 8 y posterior viene con el framework java.time . Inspirado por Joda-Time, definido por JSR 310, y ampliado por el proyecto ThreeTen-Extra. Ver tutorial .

Los creadores de Joda-Time nos han ordenado a todos que nos traslademos a java.time tan pronto como sea conveniente. Mientras tanto, Joda-Time continúa como un proyecto mantenido activamente. Pero espere que el trabajo futuro ocurra solo en java.time y ThreeTen-Extra en lugar de Joda-Time.

Para resumir java.time en pocas palabras ... Un Instantes un momento en la línea de tiempo en UTC. Aplique una zona horaria ( ZoneId) para obtener un ZonedDateTimeobjeto. Para mover fuera de la línea de tiempo, para tener una idea vaga indefinida de una fecha-hora, utilice las clases "locales": LocalDateTime, LocalDate, LocalTime.

La lógica discutida en la sección Joda-Time de esta Respuesta se aplica a java.time.

La antigua clase java.util.Date tiene un nuevo toInstantmétodo para la conversión a java.time.

Instant instant = yourJavaUtilDate.toInstant(); // Convert into java.time type.

Determinar una fecha requiere una zona horaria.

ZoneId zoneId = ZoneId.of( "America/Montreal" );

Aplicamos ese objeto de zona horaria Instantpara obtener a ZonedDateTime. De ahí extraemos un valor de solo fecha (a LocalDate) ya que nuestro objetivo es comparar fechas (no horas, minutos, etc.).

ZonedDateTime zdt1 = ZonedDateTime.ofInstant( instant , zoneId );
LocalDate localDate1 = LocalDate.from( zdt1 );

Haga lo mismo con el segundo java.util.Dateobjeto que necesitamos para comparar. Solo usaré el momento actual en su lugar.

ZonedDateTime zdt2 = ZonedDateTime.now( zoneId );
LocalDate localDate2 = LocalDate.from( zdt2 );

Use el isEqualmétodo especial para probar el mismo valor de fecha.

Boolean sameDate = localDate1.isEqual( localDate2 );
Albahaca Bourque
fuente
java.time: O simplemente puede hacerLocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
CyberMew
1
@CyberMew Er? No hay fromDateFields()en mi LocalDateclase .
Ole VV
1
Lo siento, debería haber sido más claro. El comentario es relevante para la clase Joda-Time LocalDate .
CyberMew
7

Convierta fechas a Java 8 java.time.LocalDate como se ve aquí .

LocalDate localDate1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate localDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

// compare dates
assertTrue("Not on the same day", localDate1.equals(localDate2));
Istvan
fuente
Esta es mi respuesta favorita. Si desea controlar la zona horaria utilizada en lugar de depender de la configuración de la computadora, es fácil de instalar, por ejemplo, ZoneId.of("America/Phoenix") o en ZoneId.of("Europe/Budapest")lugar de la configuración predeterminada del sistema.
Ole VV
6

Java 8

Si está utilizando Java 8 en su proyecto y está comparando java.sql.Timestamp, podría usar la LocalDateclase:

sameDate = date1.toLocalDateTime().toLocalDate().equals(date2.toLocalDateTime().toLocalDate());

Si está utilizando java.util.Date, eche un vistazo a la respuesta de Istvan, que es menos ambigua.

amanteaux
fuente
Usar Java 8 es una buena idea. ¿Estás asumiendo date1y date2estás java.sql.Timestamp? Según la pregunta que son Date, lo entendería java.util.Date. Además, su código utiliza la configuración de zona horaria de la computadora, que para muchos propósitos estará bien; aun así, preferiría hacer explícito este hecho en el código.
Ole VV
@ OleV.V. gracias por tu comentario, mi respuesta original fue sobre java.sql.Timestamp. Como dijiste, generalmente es mejor establecer explícitamente la zona horaria.
amanteaux
5
private boolean isSameDay(Date date1, Date date2) {
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(date1);
        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(date2);
        boolean sameYear = calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
        boolean sameMonth = calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
        boolean sameDay = calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
        return (sameDay && sameMonth && sameYear);
    }
evya
fuente
5

PARA USUARIOS DE ANDROID:

Puede usar DateUtils.isToday(dateMilliseconds)para verificar si la fecha dada es el día actual o no.

Referencia API: https://developer.android.com/reference/android/text/format/DateUtils.html#isToday(long)

Hemant Kaushik
fuente
1
Este método utiliza Object Time, que está en desuso desde API 22: developer.android.com/reference/android/text/format/Time.html
Oleksandr Bodashko
2
+1 para desarrolladores de Android. este método usa la primitiva larga como parámetro, simplemente use DateUtils.isToday(x.getTime())(x es una instancia de java.util.date) servirá
jackycflau
1

Además de la solución Binil Thomas

public static boolean isOnSameDay(Timestamp... dates) {
    SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
    String date1 = fmt.format(dates[0]);
    for (Timestamp date : dates) {
        if (!fmt.format(date).equals(date1)) {
            return false;
        }
    }
    return true;
}

uso

    isOnSameDay(date1,date2,date3 ...);
//or 
    isOnSameDay(mydates);
Alnamrouti Fareed
fuente
1

Para los desarrolladores de Kotlin, esta es la versión con enfoque de comparación de cadenas formateadas:

val sdf = SimpleDateFormat("yyMMdd")
if (sdf.format(date1) == sdf.format(date2)) {
    // same day
}

No es la mejor manera, pero es corta y funciona.

Micer
fuente
1
La SimpleDateFormatclase fue suplantada hace años por la java.time.DateTimeFormatterclase moderna definida en JSR 310. Sugerir su uso en 2019 es un mal consejo.
Basil Bourque
2
Este código ignora la cuestión crucial de la zona horaria. Para cualquier momento dado, la fecha varía en todo el mundo por zona.
Basil Bourque
-4

puede aplicar la misma lógica que la solución SimpleDateFormat sin depender de SimpleDateFormat

date1.getFullYear()*10000 + date1.getMonth()*100 + date1.getDate() == 
date2.getFullYear()*10000 + date2.getMonth()*100 + date2.getDate()
jawa
fuente
66
Estos métodos están en desuso en java.util.Date. Deberías usar Calendar para hacer esto. También es getYear, no getFullYear
Kris