Vergleichen Sie zwei java.util.Dates, um festzustellen, ob sie am selben Tag sind

249

Ich muss zwei Dates (zB date1und date2) vergleichen und ein finden, boolean sameDaydas für beide giltDate am selben Tag gilt, und falsch, wenn dies nicht .

Wie kann ich das machen? Hier scheint es einen Wirbelwind der Verwirrung zu geben ... und ich möchte es vermeiden, andere Abhängigkeiten über das JDK hinaus einzubeziehen, wenn dies überhaupt möglich ist.

zur Verdeutlichung: Wenn date1und date2teilen Sie das gleiche Jahr, den gleichen Monat und den gleichen Tag, dann sameDayist es wahr, sonst ist es falsch. Mir ist klar, dass dies die Kenntnis einer Zeitzone erfordert ... es wäre schön, eine Zeitzone einzugeben, aber ich kann entweder mit GMT oder Ortszeit leben, solange ich weiß, wie sich das verhält.

noch einmal, um zu verdeutlichen:

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.
quelle
Nur zur Verdeutlichung: Sie möchten wissen, ob zwei Datumsobjekte auf denselben Wochentag fallen?
Rob Heiser
Möchten Sie das vollständige Datum (Tag, Monat, Jahr) oder nur den Monatstag vergleichen?
XpiritO
1
@Rob: nein, am selben Tag / Monat / Jahr ... werde ich klären.
Jason S
Warum benutzt du dann nicht "gleich"?
XpiritO
7
Weil sie nicht gleich sind, wenn Stunde / Minute / Sekunde unterschiedlich sind.
Jason S

Antworten:

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

Beachten Sie, dass "am selben Tag" kein so einfaches Konzept ist, wie es sich anhört, wenn verschiedene Zeitzonen beteiligt sein können. Der obige Code berechnet für beide Daten den Tag relativ zur Zeitzone, die von dem Computer verwendet wird, auf dem er ausgeführt wird. Wenn dies nicht erforderlich ist, müssen Sie die entsprechenden Zeitzonen an die Calendar.getInstance()Anrufe übergeben, nachdem Sie entschieden haben, was genau Sie mit "am selben Tag" meinen.

Und ja, Joda Time's LocalDatewürde das Ganze viel sauberer und einfacher machen (obwohl die gleichen Schwierigkeiten mit Zeitzonen vorhanden wären).

Michael Borgwardt
quelle
Danke, das sieht so aus, als würde es tun, was ich will. In meinem Fall vergleiche ich aufeinanderfolgende Daten in einer Reihe, sodass ich anscheinend nur Kalenderinstanzen anstelle von Datumsinstanzen in meiner Reihe verwenden könnte.
Jason S
1
@ Jason Das kann eine gute Idee sein oder auch nicht. Das Hauptproblem bei Calendar ist, dass es sich um eine sehr schwere Klasse mit vielen internen Status handelt, von denen einige in der Implementierung von equals () verwendet werden. Wenn Sie Ihre Daten nicht auf Gleichheit kopieren und nicht in HashMaps einfügen, sollte es Ihnen gut gehen.
Michael Borgwardt
cool, danke, ich verwende nur die hier angeforderte Logik zum Vergleichen des aktuellen und des vorherigen Tages. Die Funktionen "equals" und "hashcode" sollten niemals aufgerufen werden.
Jason S
1
@UmerHayat: Der Code vergleicht den Tag des Jahres, nicht den Tag des Monats. Ein Vergleich weniger erforderlich, kürzerer Code.
Michael Borgwardt
2
Vorschlag: Vergleichen Sie zuerst DAY_OF_YEAR, da das Jahr nicht überprüft werden muss. Ok, es ist nicht so, als wäre der Vergleich von int wirklich teuer, aber ...
Martin P.
332

Wie wäre es mit:

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

Bei Bedarf können Sie die Zeitzone auch auf SimpleDateFormat einstellen.

Binil Thomas
quelle
2
(Ich benutze in meinem Fall sowieso SimpleDateFormat, also scheint es irgendwie angemessen.)
Jason S
8
Ich bin tatsächlich überrascht, dies zu sehen, aber selbst unter dem Gesichtspunkt der Leistung ist diese SimpleDateFormatMethode tatsächlich schneller als die andere, die hier mit Calendars erwähnt wird. Im Durchschnitt dauert es die Hälfte der Zeit als CalendarMethode. (Zumindest auf meinem System). Ein großes Lob!
Michael Plautz
Der größte Teil der Kosten für diese Antwort entfällt auf die Erstellung von SimpleDateFormat. Setzen Sie es in ein threadLocal-Feld in einem Singleton, wenn Sie noch bessere Leistung wünschen
Thierry
1
Ich mache das auch in Swift. Es ist erstaunlich, wie eine so einfache Sache in den meisten Sprachen denselben Hack erfordert. C # ist die Ausnahme - if (d1.Date == d2.Date) ...
alpsystems.com
2
Das ist hässlich; überrascht, einen Hack wie diesen positiv bewertet zu sehen.
Zsolt Safrany
162

Ich benutze dazu das "apache commons lang" -Paket (nämlich org.apache.commons.lang.time.DateUtils )

boolean samedate = DateUtils.isSameDay(date1, date2);  //Takes either Calendar or Date objects
Brent Watson
quelle
2
Dies verwendet eine externe Abhängigkeit ... aber es ist gut zu wissen für die Zukunft.
Jason S
20
Kopieren Sie einfach die Quelle und nennen Sie es einen Tag :)
Anton Kuzmin
Es wird jedoch eine illegale Argumentausnahme ausgelöst, wenn einer der Tage null ist, was in einigen Fällen nicht ideal ist.
Raviraja
1
Wie in stackoverflow.com/a/2517824/1665809 kommentiert, ist diese Lösung möglicherweise nicht geeignet, wenn unterschiedliche Zeitzonen betroffen sind.
Mrod
24

Sie können externe Abhängigkeiten und den Leistungseinbruch bei der Verwendung des Kalenders vermeiden, indem Sie die Julianische Tageszahl für jedes Datum berechnen und diese dann vergleichen:

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;
}
AyeJay
quelle
4
Beachten Sie jedoch, dass Änderungen der Tageslänge aufgrund der Sommerzeit nicht berücksichtigt werden . Aber dann Datekapseln gerade s nicht den Begriff der Zeitzonen, so dass es keine Möglichkeit gibt, dies nicht willkürlich zu beheben.
AyeJay
3
Dies ist übrigens die schnellste Lösung - vielen Dank, genau das, wonach ich gesucht habe; da muss ich viele Daten überprüfen ...
Ridcully
2
Nitpick: date1.getTime () gibt nicht die julianische Tagesnummer zurück, sondern Millisekunden seit dem 01.01.1970. Großer Unterschied.
Per Lindberg
2
Dies führt den Vergleich in der UTC-Zeitzone durch. Wenn Sie Ihre lokale Zeitzone oder einen anderen Ort auf dem Planeten haben möchten, können Sie nicht wissen, ob das Ergebnis, das Sie erhalten, korrekt ist.
Ole VV
20

Joda-Zeit

Was das Hinzufügen einer Abhängigkeit betrifft, befürchte ich, dass java.util.Date & .Calendar wirklich so schlecht sind, dass ich bei jedem neuen Projekt als erstes die Joda-Time-Bibliothek hinzufüge. In Java 8 können Sie das neue Paket java.time verwenden, das von Joda-Time inspiriert ist.

Der Kern von Joda-Time ist die DateTimeKlasse. Im Gegensatz zu java.util.Date versteht es die zugewiesene Zeitzone ( DateTimeZone). Weisen Sie beim Konvertieren von juDate eine Zone zu.

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

LocalDate

Eine Möglichkeit zu überprüfen, ob zwei Datums- und Uhrzeitangaben am selben Datum landen, besteht in der Konvertierung in LocalDateObjekte.

Diese Konvertierung hängt von der zugewiesenen Zeitzone ab. VergleichenLocalDate Objekte können, müssen sie mit derselben Zone konvertiert worden sein.

Hier ist eine kleine Dienstprogrammmethode.

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

Besser wäre ein anderes Argument, die Zeitzone anzugeben, anstatt anzunehmen, dass die Zeitzone des ersten DateTime-Objekts verwendet werden sollte.

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

Zeichenfolgendarstellung

Ein anderer Ansatz besteht darin, eine Zeichenfolgendarstellung des Datumsabschnitts jeder Datums- / Uhrzeit zu erstellen und dann Zeichenfolgen zu vergleichen.

Auch hier ist die zugewiesene Zeitzone entscheidend.

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;

Zeitspanne

Die allgemeine Lösung besteht darin, eine Zeitspanne zu definieren und dann zu fragen, ob die Zeitspanne Ihr Ziel enthält. Dieser Beispielcode befindet sich in Joda-Time 2.4. Beachten Sie, dass die mit "Mitternacht" verbundenen Klassen veraltet sind. Verwenden Sie stattdessen die withTimeAtStartOfDayMethode. Joda-Time bietet drei Klassen an, um eine Zeitspanne auf verschiedene Arten darzustellen: Intervall, Periode und Dauer.

Verwenden Sie den "Half-Open" -Ansatz, bei dem der Anfang der Spanne inklusive und das Ende exklusiv ist.

Die Zeitzone des Ziels kann sich von der Zeitzone des Intervalls unterscheiden.

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 und höher wird mit dem Java.time- Framework geliefert . Inspiriert von Joda-Time, definiert durch JSR 310 und erweitert durch das ThreeTen-Extra-Projekt. Siehe Tutorial .

Die Macher von Joda-Time haben uns alle angewiesen, so schnell wie möglich zu java.time zu wechseln. In der Zwischenzeit wird Joda-Time als aktiv gepflegtes Projekt fortgesetzt. Erwarten Sie jedoch, dass zukünftige Arbeiten nur in java.time und ThreeTen-Extra statt in Joda-Time ausgeführt werden.

Um java.time auf den Punkt zu bringen… An Instantist ein Moment auf der Timeline in UTC. Wenden Sie eine Zeitzone ( ZoneId) an, um ein ZonedDateTimeObjekt zu erhalten. Um die Timeline losfahren, die vage unbestimmte Idee eines Datum-Zeit zu erhalten, verwenden Sie die „lokale“ Klassen: LocalDateTime, LocalDate, LocalTime.

Die im Abschnitt Joda-Zeit dieser Antwort beschriebene Logik gilt für java.time.

Die alte Klasse java.util.Date verfügt über eine neue toInstantMethode zur Konvertierung in java.time.

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

Das Bestimmen eines Datums erfordert eine Zeitzone.

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

Wir wenden dieses Zeitzonenobjekt auf das Instantan, um a zu erhalten ZonedDateTime. Daraus extrahieren wir einen Nur-Datum-Wert (a LocalDate), da unser Ziel darin besteht, Daten (nicht Stunden, Minuten usw.) zu vergleichen.

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

Machen Sie dasselbe mit dem zweiten java.util.DateObjekt, das wir zum Vergleich benötigen. Ich werde stattdessen nur den aktuellen Moment nutzen.

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

Verwenden Sie die spezielle isEqualMethode, um auf denselben Datumswert zu testen.

Boolean sameDate = localDate1.isEqual( localDate2 );
Basil Bourque
quelle
java.time: Oder Sie können einfach tunLocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
CyberMew
1
@CyberMew Er? fromDateFields()In meiner LocalDateKlasse gibt es keine .
Ole VV
1
Entschuldigung, ich hätte klarer sein sollen. Der Kommentar ist für die Joda-Time LocalDate- Klasse relevant .
CyberMew
7

Konvertieren Sie Daten in Java 8 java.time.LocalDate wie hier gezeigt .

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
quelle
Das ist meine Lieblingsantwort. Wenn Sie die verwendete Zeitzone steuern möchten, anstatt sich auf die Einstellungen des Computers zu verlassen, können Sie diese beispielsweise ZoneId.of("America/Phoenix") oder ZoneId.of("Europe/Budapest")anstelle der Systemvorgabe einfach eingeben.
Ole VV
6

Java 8

Wenn Sie Java 8 in Ihrem Projekt verwenden und vergleichen java.sql.Timestamp, können Sie die folgende LocalDateKlasse verwenden:

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

Wenn Sie verwenden java.util.Date, werfen Sie einen Blick auf die Antwort von Istvan, die weniger mehrdeutig ist.

Amanteaux
quelle
Die Verwendung von Java 8 ist eine gute Idee. Nehmen Sie an date1und date2sind java.sql.Timestamp? Nach der Frage, die sie sind Date, würde ich verstehen java.util.Date. Außerdem verwendet Ihr Code die Zeitzoneneinstellung des Computers, die für viele Zwecke in Ordnung ist. Trotzdem würde ich es vorziehen, diese Tatsache im Code explizit zu machen.
Ole VV
@ OleV.V. Vielen Dank für Ihren Kommentar, meine ursprüngliche Antwort war in der Tat über java.sql.Timestamp. Wie Sie sagten, ist es im Allgemeinen besser, die Zeitzone explizit festzulegen.
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
quelle
5

FÜR ANDROID-BENUTZER:

Sie können verwenden DateUtils.isToday(dateMilliseconds) überprüfen, ob das angegebene Datum der aktuelle Tag ist oder nicht.

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

Hemant Kaushik
quelle
1
Diese Methode verwendet das Objekt Time, das seit API 22 veraltet ist: developer.android.com/reference/android/text/format/Time.html
Oleksandr Bodashko
2
+1 für Android-Entwickler. Diese Methode verwendet das Grundelement long as Parameter, einfach verwenden DateUtils.isToday(x.getTime())(x ist eine java.util.date-Instanz) wird tun
jackycflau
1

zusätzlich zur Binil Thomas Lösung

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

Verwendung

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

Für Kotlin-Entwickler ist dies die Version mit dem Vergleich formatierter Zeichenfolgen:

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

Es ist nicht der beste Weg, aber es ist kurz und funktioniert.

Micer
quelle
1
Die SimpleDateFormatKlasse wurde vor Jahren durch die java.time.DateTimeFormatterin JSR 310 definierte moderne Klasse ersetzt. Die Verwendung im Jahr 2019 vorzuschlagen, ist ein schlechter Rat.
Basil Bourque
2
Dieser Code ignoriert das entscheidende Problem der Zeitzone. Für jeden Moment variiert das Datum weltweit je nach Zone.
Basil Bourque
-4

Sie können dieselbe Logik wie die SimpleDateFormat-Lösung anwenden, ohne sich auf SimpleDateFormat verlassen zu müssen

date1.getFullYear()*10000 + date1.getMonth()*100 + date1.getDate() == 
date2.getFullYear()*10000 + date2.getMonth()*100 + date2.getDate()
jawa
quelle
6
Diese Methoden sind in java.util.Date veraltet. Sie sollten dazu den Kalender verwenden. Es ist auch getYear, nicht getFullYear
Kris