Confronto tra due date java.util per vedere se sono nello stesso giorno


250

Ho bisogno di confrontare due Datesecondi (es. date1E date2) e trovare un valore boolean sameDayche sia vero per la Datecondivisione dei due secondi lo stesso giorno, e falso se non lo sono.

Come posso fare questo? Sembra che ci sia un vortice di confusione qui ... e vorrei evitare, se possibile, di evitare altre dipendenze oltre il JDK.

per chiarire: se date1e date2condividere lo stesso anno, mese e giorno, allora sameDayè vero, altrimenti è falso. Mi rendo conto che questo richiede la conoscenza di un fuso orario ... sarebbe bello passare in un fuso orario, ma posso vivere con GMT o ora locale purché sappia quale sia il comportamento.

ancora una volta, per chiarire:

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

Solo per chiarire: vuoi sapere se due oggetti Date cadono nello stesso giorno della settimana?
Rob Heiser,

Vuoi confrontare la data completa (giorno, mese, anno) o solo mese?
XpiritO,

1
@Rob: no, lo stesso giorno / mese / anno ... chiarirò.
Jason S,

allora perché non usi "uguale"?
XpiritO,

7
Perché non sono uguali se l'ora / i minuti / i secondi sono diversi.
Jason S,

Risposte:


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);

Si noti che "stesso giorno" non è un concetto così semplice come sembra quando possono essere coinvolti diversi fusi orari. Il codice sopra riportato calcolerà per entrambe le date il giorno relativo al fuso orario utilizzato dal computer su cui è in esecuzione. Se questo non è ciò di cui hai bisogno, devi passare i fusi orari pertinenti alle Calendar.getInstance()chiamate, dopo aver deciso cosa intendi esattamente con "lo stesso giorno".

E sì, Joda Time LocalDaterenderebbe il tutto molto più pulito e semplice (anche se sarebbero presenti le stesse difficoltà che coinvolgono i fusi orari).


Grazie, sembra che farà quello che voglio. Nel mio caso sto confrontando date successive in una serie, quindi sembra che potrei usare solo le istanze di Calendar anziché le istanze di Date nelle mie serie.
Jason S,

1
@Jason Potrebbe essere o meno una buona idea. Il problema principale con Calendar è che si tratta di una classe molto pesante con molto stato interno, alcuni dei quali viene utilizzato nella sua implementazione equals (). Se non copmpare le tue date per l'uguaglianza e non le metti in HashMaps, dovresti andare bene.
Michael Borgwardt,

bene, grazie, sto solo usando la logica di confronto-corrente-e-giorno precedente richiesta qui. le funzioni "uguale" e "hashcode" non dovrebbero mai essere chiamate.
Jason S,

1
@UmerHayat: il codice confronta il giorno dell'anno, non il giorno del mese. È necessario un confronto in meno, codice più breve.
Michael Borgwardt,

2
Suggerimento: confronta prima DAY_OF_YEAR, non sarà necessario controllare l'anno. Ok non è come confrontare int è davvero costoso ma ...
Martin

332

Che ne dite di:

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

È inoltre possibile impostare il fuso orario su SimpleDateFormat, se necessario.


2
(In realtà sto usando SimpleDateFormat nel mio caso, quindi sembra un po 'appropriato.)
Jason S

8
Sono davvero sorpreso di vederlo, ma anche dal punto di vista delle prestazioni, questo SimpleDateFormatmetodo è in realtà più veloce dell'altro menzionato qui usando Calendars. In media ci vuole metà del tempo come Calendarmetodo. (Almeno sul mio sistema). Complimenti!
Michael Plautz,

Gran parte del costo di questa risposta è dovuto alla creazione di SimpleDateFormat. Mettilo in un thread Campo locale in un singleton se vuoi prestazioni ancora migliori
Thierry,

1
Lo faccio anche in Swift. È sorprendente come una cosa così semplice richieda lo stesso hack nella maggior parte delle lingue. C # è l'eccezione - if (d1.Date == d2.Date) ...
alpsystems.com

2
Questo è brutto; sorpreso di vedere un hack come questo votato.
Zsolt Safrany,

162

Per fare questo uso il pacchetto "apache commons lang" (ovvero org.apache.commons.lang.time.DateUtils )

boolean samedate = DateUtils.isSameDay(date1, date2);  //Takes either Calendar or Date objects

2
Questo utilizza una dipendenza esterna ... ma è bene sapere per il futuro.
Jason S,

20
Copia la fonte e chiamala un giorno :)
Anton Kuzmin,

Ma genera un'eccezione argomento illegale se uno qualsiasi dei giorni è nullo, che in alcuni casi non è l'ideale.
Raviraja,

1
Come commentato in stackoverflow.com/a/2517824/1665809 questa soluzione potrebbe non essere appropriata se sono coinvolti diversi fusi orari.
martedì

24

È possibile evitare dipendenze esterne e l'hit di prestazioni dell'utilizzo del Calendario calcolando il numero del giorno giuliano per ciascuna delle date e quindi confrontando queste:

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;
}

4
Ma attenzione che questo non tiene conto delle modifiche alla lunghezza del giorno a causa dell'ora legale. Ma poi i diritti Datenon incapsulano la nozione di fusi orari, quindi non c'è modo di risolverli in modo non arbitrario.
AyeJay,

3
Questa è sicuramente la soluzione più veloce - grazie mille, esattamente quello che stavo cercando; come devo controllare molte date ...
Ridcully,

2
Nitpick: date1.getTime () non restituisce il numero del giorno giuliano, ma millisecondi dal 1970-01-01. Differenza enorme.
Per Lindberg, il

2
Ciò esegue il confronto nel fuso orario UTC. Se volevi il tuo fuso orario locale o qualche altro luogo sul pianeta, non puoi sapere se il risultato che ottieni è corretto.
Ole VV,

20

Joda-Time

Per quanto riguarda l'aggiunta di una dipendenza, temo che java.util.Date e .Calendar siano davvero così male che la prima cosa che faccio a qualsiasi nuovo progetto è aggiungere la libreria Joda-Time. In Java 8 è possibile utilizzare il nuovo pacchetto java.time, ispirato a Joda-Time.

Il nucleo di Joda-Time è la DateTimeclasse. A differenza di java.util.Date, comprende il fuso orario assegnato ( DateTimeZone). Durante la conversione da juDate, assegnare una zona.

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

LocalDate

Un modo per verificare se due date-time arrivano alla stessa data è convertire in LocalDateoggetti.

Tale conversione dipende dal fuso orario assegnato. Per confrontare gli LocalDateoggetti, devono essere stati convertiti con la stessa zona.

Ecco un piccolo metodo di utilità.

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;
}

Meglio sarebbe un altro argomento, specificando il fuso orario piuttosto che assumere il fuso orario del primo oggetto DateTime dovrebbe essere usato.

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 );
}

Rappresentazione di stringhe

Un altro approccio è quello di creare una rappresentazione in forma di stringa della parte della data di ciascuna data-ora, quindi confrontare le stringhe.

Ancora una volta, il fuso orario assegnato è cruciale.

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;

Lasso di tempo

La soluzione generalizzata è definire un intervallo di tempo, quindi chiedere se l'intervallo contiene il tuo obiettivo. Questo codice di esempio è in Joda-Time 2.4. Si noti che le classi correlate a "mezzanotte" sono obsolete. Utilizzare invece il withTimeAtStartOfDaymetodo Joda-Time offre tre classi per rappresentare un arco di tempo in vari modi: intervallo, periodo e durata.

Utilizzando l'approccio "Half-Open" in cui l'inizio dell'intervallo è inclusivo e la fine esclusiva.

Il fuso orario del target può essere diverso dal fuso orario dell'intervallo.

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 e versioni successive vengono forniti con il framework java.time . Ispirato da Joda-Time, definito da JSR 310, e ampliato dal progetto ThreeTen-Extra. Vedi Tutorial .

I creatori di Joda-Time hanno dato istruzioni a tutti noi di passare a java.time non appena è conveniente. Nel frattempo Joda-Time continua come progetto attivamente mantenuto. Ma aspettatevi che i lavori futuri avvengano solo in java.time e ThreeTen-Extra piuttosto che in Joda-Time.

Riassumendo java.time in breve ... An Instantè un momento sulla linea temporale in UTC. Applicare un fuso orario ( ZoneId) per ottenere un ZonedDateTimeoggetto. Per spostare fuori la linea temporale, per avere l'idea indefinita vaga di un data-ora, utilizzare le classi "locali": LocalDateTime, LocalDate, LocalTime.

La logica discussa nella sezione Joda-Time di questa risposta si applica a java.time.

La vecchia classe java.util.Date ha un nuovo toInstantmetodo per la conversione in java.time.

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

Determinare una data richiede un fuso orario.

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

Applichiamo quell'oggetto fuso orario al Instantper ottenere un ZonedDateTime. Da ciò estraiamo un valore di sola data (a LocalDate) poiché il nostro obiettivo è confrontare date (non ore, minuti, ecc.).

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

Fai lo stesso con il secondo java.util.Dateoggetto di cui abbiamo bisogno per il confronto. Userò solo il momento attuale invece.

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

Utilizzare il isEqualmetodo speciale per verificare lo stesso valore di data.

Boolean sameDate = localDate1.isEqual( localDate2 );

java.time: O puoi semplicemente farloLocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
CyberMew

1
@CyberMew Er? Non c'è fromDateFields()nella mia LocalDateclasse .
Ole VV,

1
Scusa, avrei dovuto essere più chiaro. Il commento è rilevante per la classe LocalDate Joda-Time .
CyberMew,

7

Converti le date in Java 8 java.time.LocalDate come mostrato qui .

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));

Questa è la mia risposta preferita Se si desidera controllare il fuso orario utilizzato anziché fare affidamento sull'impostazione del computer, è semplice inserire, ad esempio ZoneId.of("America/Phoenix") o al ZoneId.of("Europe/Budapest")posto del valore predefinito del sistema.
Ole VV,

6

Java 8

Se stai usando Java 8 nel tuo progetto e java.sql.Timestampstai confrontando , puoi usare la LocalDateclasse:

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

Se stai usando java.util.Date, dai un'occhiata alla risposta di Istvan che è meno ambigua.


L'uso di Java 8 è una buona idea. Stai assumendo date1e lo date2sei java.sql.Timestamp? Secondo la domanda che sono Date, capirei java.util.Date. Inoltre, il codice utilizza l'impostazione del fuso orario del computer, che per molti scopi andrà bene; preferirei comunque rendere esplicito questo fatto nel codice.
Ole VV,

@ OleV.V. grazie per il tuo commento, la mia risposta originale riguardava davvero java.sql.Timestamp. Come hai detto, in genere è meglio impostare esplicitamente il fuso orario.
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);
    }

5

PER UTENTI ANDROID:

È possibile utilizzare DateUtils.isToday(dateMilliseconds)per verificare se la data specificata è il giorno corrente o meno.

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


1
Questo metodo usa l'oggetto Time che è obsoleto dall'API 22: developer.android.com/reference/android/text/format/Time.html
Oleksandr Bodashko

2
+1 per gli sviluppatori Android. questo metodo utilizza la primitiva long come parametro, basta usare DateUtils.isToday(x.getTime())(x è un'istanza java.util.date)
jackycflau

1

oltre alla soluzione 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);

1

Per gli sviluppatori di Kotlin questa è la versione con il confronto tra stringhe formattate:

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

Non è il modo migliore, ma è breve e funzionante.


1
La SimpleDateFormatclasse è stata soppiantata anni fa dalla java.time.DateTimeFormatterclasse moderna definita in JSR 310. Suggerire il suo uso nel 2019 è un cattivo consiglio.
Basil Bourque,

2
Questo codice ignora il problema cruciale del fuso orario. Per ogni dato momento, la data varia in tutto il mondo per zona.
Basil Bourque,

-4

puoi applicare la stessa logica della soluzione SimpleDateFormat senza fare affidamento su SimpleDateFormat

date1.getFullYear()*10000 + date1.getMonth()*100 + date1.getDate() == 
date2.getFullYear()*10000 + date2.getMonth()*100 + date2.getDate()

6
Questi metodi sono obsoleti in java.util.Date. Dovresti usare Calendar per fare questo. Inoltre è getYear, non getFullYear
Kris il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.