Anzahl der Tage zwischen zwei Daten in Joda-Time


335

Wie finde ich den Unterschied in Tagen zwischen zwei Joda-Time- DateTime Instanzen? Mit 'Differenz in Tagen' meine ich, wenn Start am Montag und Ende am Dienstag ist, erwarte ich einen Rückgabewert von 1, unabhängig von der Stunde / Minute / Sekunde des Start- und Enddatums.

Days.daysBetween(start, end).getDays() gibt mir 0, wenn der Start am Abend ist und am Morgen endet.

Ich habe auch das gleiche Problem mit anderen Datumsfeldern, daher hatte ich gehofft, dass es eine generische Möglichkeit gibt, die Felder von geringerer Bedeutung zu ignorieren.

Mit anderen Worten, die Monate zwischen Februar und dem 4. März wären ebenso 1 wie die Stunden zwischen 14:45 und 15:12 Uhr. Der Stundenunterschied zwischen 14:01 und 14:55 wäre jedoch 0.

Antworten:


393

Ärgerlicherweise ist die Antwort withTimeAtStartOfDay falsch, aber nur gelegentlich. Sie wollen:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

Es stellt sich heraus, dass "Mitternacht / Tagesanfang" manchmal 1 Uhr morgens bedeutet (Sommerzeit tritt an einigen Stellen auf diese Weise auf), was Days.daysBetween nicht richtig handhabt.

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Möchten Sie über eine LocalDateausweicht das ganze Thema.


Können Sie einen Beispielfall mit bestimmten Daten angeben, bei denen Sie der Meinung Days.daysBetweensind, dass sie falsch sind?
Basil Bourque

Wenn Sie sagen, zu verwenden Instant, sprechen Sie nicht nur darüber start.toInstant(), oder?
Patrick M

@PatrickM Ja, das war ich. Beim Nachdenken ist nicht klar, welche Einschränkungen dies auferlegen soll, daher werde ich diesen letzten Satz entfernen. Vielen Dank!
Alice Purcell

@chrispy daysBetween doc gibt an, dass die Anzahl der GANZEN Tage zurückgegeben wird. In meinem Fall sollten 2 Tage und 1 Stunde mir 3 Tage zurückgeben. In Ihrem Beispiel werden 2 Tage zurückgegeben. Gibt es einen Weg, dies zu erreichen?
Sujit Joshi

@ SujitJoshi Ich fürchte, ich verstehe deine Frage nicht. Ich schlage vor, eine vollständige Stapelüberlauf-Frage zu stellen und hier einen Link für mich einzufügen :)
Alice Purcell

186

Days Klasse

Die Verwendung der DaysKlasse mit der withTimeAtStartOfDayMethode sollte funktionieren:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 

1
Vielen Dank! Ich habe versucht, das Verhalten von android.text.format.DateUtils.getRelativeTimeSpanString () mit joda zu erreichen, und das war wirklich nützlich.
Gosho_ot_pochivka

1
Sir, in Bezug auf diesen Beitrag ist die Methode toDateMidnight()vom Typ DateTime veraltet.
Aniket Kulkarni

2
Sie sollten jetzt .withTimeAtStartOfDay () anstelle von .toDateMidnight ()
bgolson

2
Was ist, wenn das endvor dem ist start, gibt es negative Tage zurück?
Akhy

7
@akhyar: warum versuchst du es nicht?
Michael Borgwardt

86

Sie können verwenden LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 

Sir, in Bezug auf diesen Beitrag ist die Methode toDateMidnight()vom Typ DateTime veraltet.
Aniket Kulkarni

Warum new LocalDate(date)über verwenden date.toLocalDate()?
SARose

9

tl; dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.toLocalDate(), 
    later.toLocalDate() 
)

…oder…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

Zu Ihrer Information , das Joda-Time- Projekt befindet sich jetzt im Wartungsmodus. Das Team empfiehlt die Migration zu den Klassen java.time .

Das Äquivalent von Joda-Time DateTimeist ZonedDateTime.

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

Anscheinend möchten Sie die Tage nach Datum zählen, was bedeutet, dass Sie die Tageszeit ignorieren möchten. Wenn Sie beispielsweise eine Minute vor Mitternacht beginnen und eine Minute nach Mitternacht enden, sollte dies zu einem einzigen Tag führen. Extrahieren LocalDateSie für dieses Verhalten ein aus Ihrem ZonedDateTime. Die LocalDateKlasse repräsentiert einen Nur-Datum-Wert ohne Tageszeit und ohne Zeitzone.

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Verwenden Sie die ChronoUnitAufzählung, um die verstrichenen Tage oder andere Einheiten zu berechnen.

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Kürzen

Wenn Sie nach einer allgemeineren Methode für diese Zählung fragen, bei der Sie das Delta der Stunden als Stunde und nicht die vollständigen Stunden als Zeitspanne von 60 Minuten interessieren, verwenden Sie die truncatedToMethode.

Hier ist Ihr Beispiel von 14:45 bis 15:12 am selben Tag.

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

Dies funktioniert seit Tagen nicht mehr. Verwenden Sie in diesem Fall toLocalDate ().


Über java.time

Das java.time- Framework ist in Java 8 und höher integriert. Diese Klassen verdrängen die lästigen alten Legacy - Datum-Zeit - Klassen wie java.util.Date, Calendar, & SimpleDateFormat.

Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration zu den Klassen java.time .

Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .

Sie können java.time- Objekte direkt mit Ihrer Datenbank austauschen . Verwenden Sie einen JDBC-Treiber, der mit JDBC 4.2 oder höher kompatibel ist . Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für java.sql.*Klassen.

Woher bekomme ich die java.time-Klassen?

Das ThreeTen-Extra- Projekt erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen von java.time. Sie können einige nützliche Klassen hier wie finden Interval, YearWeek, YearQuarter, und mehr .

Das ThreeTen-Extra- Projekt erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen von java.time. Sie können einige nützliche Klassen hier wie finden Interval, YearWeek, YearQuarter, und mehr .


Ihr TL; DR für Tage mit abgeschnittenem Fall ist dem in der akzeptierten Antwort aufgeführten Fehler zum Opfer gefallen, nämlich, dass er nach Tagesbeginn um 1 Uhr morgens ausgeschaltet ist. Ich habe es aktualisiert, um die richtige Antwort zu verwenden, die Sie weiter unten mit toLocalDate gegeben haben.
Alice Purcell

2

Die akzeptierte Antwort erstellt zwei LocalDateObjekte, die ziemlich teuer sind, wenn Sie viele Daten lesen. Ich benutze das:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

Beim Aufruf verwenden getMillis()Sie bereits vorhandene Variablen.
MILLISECONDS.toDays()verwendet dann eine einfache arithmetische Berechnung, erstellt kein Objekt.


1
Diese Antwort funktioniert nur, wenn Ihre Definition von "Tagen" genau 24 Stunden lang ist, ohne Rücksicht auf Zeitzonen und Daten. Wenn ja, verwenden Sie diesen Ansatz. Wenn nicht, schauen Sie sich die anderen Antworten an, die sich mit Zeitzonen befassen.
Basil Bourque

@BasilBourque, Sie haben Recht, dennoch würde ich mich für eine Lösung entscheiden, die auf arithmetischen Berechnungen basiert, anstatt teure Objekte für jeden Methodenaufruf zu erstellen. Es gibt viele Varianten für solche Berechnungen, wenn Sie eine Eingabe von einer Webseite lesen. Vielleicht in Ordnung, aber wenn Sie eine Protokolldatei mit Hunderttausenden von Zeilen verarbeiten, ist dies nur sehr langsam
JBoy

1
Java optimiert die Objektzuordnung, wenn Ihre Schleife heiß ist. Siehe docs.oracle.com/javase/7/docs/technotes/guides/vm/…
Alice Purcell

1

java.time.Period

Verwenden Sie die java.time.PeriodKlasse, um Tage zu zählen .

Da die Berechnung der Differenz in Java 8 intuitiver LocalDateist LocalDateTime, werden die beiden Daten dargestellt

    LocalDate now = LocalDate.now();
    LocalDate inputDate = LocalDate.of(2018, 11, 28);

    Period period = Period.between( inputDate, now);
    int diff = period.getDays();
    System.out.println("diff = " + diff);

0
DateTime  dt  = new DateTime(laterDate);        

DateTime newDate = dt.minus( new  DateTime ( previousDate ).getMillis());

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    

-11
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}

1
die frage sucht speziell nach joda-zeit.
Kapad
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.