Java 8: Unterschied zwischen zwei LocalDateTime in mehreren Einheiten

266

Ich versuche die Differenz zwischen zwei zu berechnen LocalDateTime .

Die Ausgabe muss das Format haben y years m months d days h hours m minutes s seconds. Folgendes habe ich geschrieben:

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;

public class Main {

    static final int MINUTES_PER_HOUR = 60;
    static final int SECONDS_PER_MINUTE = 60;
    static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;

    public static void main(String[] args) {
        LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 9, 19, 46, 45);
        LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);

        Period period = getPeriod(fromDateTime, toDateTime);
        long time[] = getTime(fromDateTime, toDateTime);

        System.out.println(period.getYears() + " years " + 
                period.getMonths() + " months " + 
                period.getDays() + " days " +
                time[0] + " hours " +
                time[1] + " minutes " +
                time[2] + " seconds.");


    }

    private static Period getPeriod(LocalDateTime dob, LocalDateTime now) {
        return Period.between(dob.toLocalDate(), now.toLocalDate());
    }

    private static long[] getTime(LocalDateTime dob, LocalDateTime now) {
        LocalDateTime today = LocalDateTime.of(now.getYear(),
                now.getMonthValue(), now.getDayOfMonth(), dob.getHour(), dob.getMinute(), dob.getSecond());
        Duration duration = Duration.between(today, now);

        long seconds = duration.getSeconds();

        long hours = seconds / SECONDS_PER_HOUR;
        long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
        long secs = (seconds % SECONDS_PER_MINUTE);

        return new long[]{hours, minutes, secs};
    }
}

Die Ausgabe, die ich bekomme, ist 29 years 8 months 24 days 12 hours 0 minutes 50 seconds. Ich habe mein Ergebnis von dieser Website überprüft (mit Werten 12/16/1984 07:45:55und 09/09/2014 19:46:45). Der folgende Screenshot zeigt die Ausgabe:

Epochen-Konverter

Ich bin mir ziemlich sicher, dass die Felder nach dem Monatswert in meinem Code falsch sind. Jeder Vorschlag wäre sehr hilfreich.

Aktualisieren

Ich habe mein Ergebnis von einer anderen Website getestet und das Ergebnis ist anders. Hier ist es: Berechnen Sie die Dauer zwischen zwei Daten (Ergebnis: 29 Jahre, 8 Monate, 24 Tage, 12 Stunden, 0 Minuten und 50 Sekunden).

Aktualisieren

Da ich zwei unterschiedliche Ergebnisse von zwei verschiedenen Standorten erhalten habe, frage ich mich, ob der Algorithmus meiner Berechnung legitim ist oder nicht. Wenn ich folgende zwei LocalDateTimeObjekte verwende:

LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);

Dann kommt die Ausgabe: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.

Von diesem Link sollte es sein 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds. Der Algorithmus muss also auch die negativen Zahlen verarbeiten.

Beachten Sie, dass es bei der Frage nicht darum geht, welche Site mir welches Ergebnis gebracht hat. Ich muss den richtigen Algorithmus kennen und die richtigen Ergebnisse erzielen.

Tapas Bose
quelle
Nur eine Vermutung, könnte aber Period.between()eine Rundung anwenden?
Thomas
3
Ich habe mir den Code noch einmal angesehen und es scheint, dass die Website falsch ist (versuchen Sie, sich selbst zu berechnen). Wenn Sie das Datum weglassen, dh Unterschiede in Jahr, Monat und Tag, erhalten Sie die Startzeit 7:45:55und die Endzeit 19:46:45(oder 7:46:45PM). Der Unterschied zwischen diesen beiden Zeiten beträgt also 12 Stunden, 0 Minuten und 50 Sekunden und niemals 23 Stunden, 34 Minuten und 12 Sekunden. Ihre tatsächliche Berechnung scheint also zumindest zeitlich korrekt zu sein.
Thomas
1
Interessantes Phänomen auf dieser Website: Fügen Sie dem Startdatum 10 Jahre hinzu, und die Differenz der Stunden ändert sich von 23 auf 8 - sicherlich ein Zeichen für einen Fehler.
Thomas
8
Beachten Sie, dass LocalDateTimees möglicherweise keine eindeutige Antwort gibt , da es keine Zeitzone gibt. Selbst wenn Sie davon ausgehen, dass die Start- und Endzeitzonen gleich sind, werden in bestimmten Zonen Daten wie der 09.09.2014 in der Sommerzeit oder in der Sommerzeit angezeigt, in anderen nicht. Dies könnte die Dinge um eine Stunde abwerfen. Die Berechnung der Differenz zur Sekunde ist also bedeutungslos, es sei denn, dies wird behoben.
Stuart Marks
2
Verstehen Sie, dass die Verwendung LocalDateTimeunrealistische Ergebnisse liefert, da dieser Klasse absichtlich ein Konzept der Zeitzone oder des Versatzes von UTC fehlt? Weisen Sie für realistische Werte eine ZoneIdzu verwendende Zeitzone über zu ZonedDateTime.
Basil Bourque

Antworten:

163

Leider scheint es keine Zeitklasse zu geben, die sich auch über die Zeit erstreckt. Daher müssen Sie die Berechnungen möglicherweise selbst durchführen.

Glücklicherweise verfügen die Datums- und Zeitklassen über viele nützliche Methoden, die dies bis zu einem gewissen Grad vereinfachen. Hier ist eine Möglichkeit, die Differenz zu berechnen, obwohl dies nicht unbedingt die schnellste ist:

LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);

LocalDateTime tempDateTime = LocalDateTime.from( fromDateTime );

long years = tempDateTime.until( toDateTime, ChronoUnit.YEARS );
tempDateTime = tempDateTime.plusYears( years );

long months = tempDateTime.until( toDateTime, ChronoUnit.MONTHS );
tempDateTime = tempDateTime.plusMonths( months );

long days = tempDateTime.until( toDateTime, ChronoUnit.DAYS );
tempDateTime = tempDateTime.plusDays( days );


long hours = tempDateTime.until( toDateTime, ChronoUnit.HOURS );
tempDateTime = tempDateTime.plusHours( hours );

long minutes = tempDateTime.until( toDateTime, ChronoUnit.MINUTES );
tempDateTime = tempDateTime.plusMinutes( minutes );

long seconds = tempDateTime.until( toDateTime, ChronoUnit.SECONDS );

System.out.println( years + " years " + 
        months + " months " + 
        days + " days " +
        hours + " hours " +
        minutes + " minutes " +
        seconds + " seconds.");

//prints: 29 years 8 months 24 days 22 hours 54 minutes 50 seconds.

Die Grundidee lautet: Erstellen Sie ein temporäres Startdatum und erhalten Sie die vollen Jahre bis zum Ende. Passen Sie dieses Datum dann um die Anzahl der Jahre an, sodass das Startdatum weniger als ein Jahr nach dem Ende liegt. Wiederholen Sie dies für jede Zeiteinheit in absteigender Reihenfolge.

Zum Schluss noch ein Haftungsausschluss : Ich habe keine unterschiedlichen Zeitzonen berücksichtigt (beide Daten sollten in derselben Zeitzone liegen) und ich habe auch nicht getestet / überprüft, wie sich die Sommerzeit oder andere Änderungen in einem Kalender ändern (wie die Zeitzonen in Samoa). beeinflussen diese Berechnung. Also vorsichtig verwenden.

Thomas
quelle
1
Sie haben die gleiche Grundidee, daher meine Gegenstimme, aber bitte versuchen Sie, die gleiche Eingabe zu verwenden, da sonst das unterschiedliche Ergebnis verwirrend ist.
Meno Hochschild
@MenoHochschild es ist die gleiche Eingabe, die gerade aus dem Update stammt, bei dem das OP Probleme mit negativen Zeiten hat. ;)
Thomas
1
Guter Algorithmus, aber falscher Typ (siehe Thomas Haftungsausschluss). Bevor Sie Ihre Berechnung durchführen, sollten Sie Ihre LocalDateTime-Variablen unter Verwendung der Standardzeitzone (oder einer beliebigen Zeitzone, die Sie benötigen) in ZonedDateTime konvertieren: ZonedDateTime fromZonedDateTime = fromDateTime.atZone (ZoneId.systemDefault ());
Tristan
2
@Thomas Ihr Beispiel verwendet ebenfalls Java 8 - und die Frage ist mit Java-8 gekennzeichnet. In der Tat verwendet Ihr Beispiel sogar ChronoUnit:), erledigt aber die Arbeit "von Hand".
Eugene Beresovsky
3
@ EugeneBeresovsky oh ja, du hast recht. Ich habe das total verpasst;) - Außerdem long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);müssten Sie immer noch die größeren Einheiten vom Intervall abziehen, da beispielsweise für ein Intervall von mindestens 1 Stunde eine Zahl größer als 59 zurückgegeben würde - und das ist nicht das, was das OP will. Wenn Sie dies berücksichtigen, können Sie nicht nur 6 Anrufe tätigen ChronoUnit#between()und fertig sein.
Thomas
502

Ich fand den besten Weg, dies zu tun, mit ChronoUnit.

long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);
long hours = ChronoUnit.HOURS.between(fromDate, toDate);

Zusätzliche Dokumentation finden Sie hier: https://docs.oracle.com/javase/tutorial/datetime/iso/period.html

Satnam
quelle
8
Ich sehe keine wirkliche Verbesserung. Im Vergleich mit anerkannten Thomas' Antwort , die Sie ersetzen Sie einfach tempDateTime.until( toDateTime, ChronoUnit.YEARS)mit ChronoUnit.YEARS.between(fromDate, toDate). Die wichtigen zusätzlichen Zeilen wie tempDateTime.plusYears( years )usw. fehlen jedoch vollständig in Ihrer Antwort, sodass dies dem OP nicht hilft. Der komplette Algorithmus ist wichtig!
Meno Hochschild
9
Ich mag das besser, da es prägnanter und lesbarer ist. Dies ist der beste Weg, um den Unterschied zwischen zwei Daten zu ermitteln.
Somaiah Kumbera
7
@ SomaiahKumbera Nein, Sie verpassen den kritischen Punkt völlig. Das OP möchte keine Dauer in nur einer Einheit, sondern in mehreren Einheiten , und daher ist diese Antwort überhaupt keine echte Antwort. Das Anliegen des OP wird einfach nicht angesprochen. Bitte lesen Sie die Frage noch einmal. (Viele Aufsteiger scheinen die Frage falsch verstanden zu haben).
Meno Hochschild
125
@MenoHochschild, ich denke du bist tatsächlich derjenige, dem der kritische Punkt fehlt. Die OP hat ihre Antwort vor über einem Jahr erhalten. Die 36.000 Menschen, die diese Frage sehen, interessieren sich nicht für die spezifische Frage des OP. Sie wurden von Google hierher geführt und meine Antwort lieferte ihnen das, wonach sie suchten - eine saubere und einfache Lösung, um den Unterschied zwischen zwei Daten zu ermitteln.
Satnam
10
Ich brauchte den Sekundenunterschied zwischen zwei Datums- und Uhrzeitangaben und begann mit der Implementierung meiner eigenen Lösung. Kurz scheint es zu komplex und ich habe Google übernommen. Die Antwort von satnam beantwortet möglicherweise nicht die OP-Frage, hat mir aber sehr geholfen und ich wette, dass viele andere wie ich.
Julian
43

Hier ein einzelnes Beispiel mit Duration und TimeUnit, um das Format 'hh: mm: ss' zu erhalten.

Duration dur = Duration.between(localDateTimeIni, localDateTimeEnd);
long millis = dur.toMillis();

String.format("%02d:%02d:%02d", 
        TimeUnit.MILLISECONDS.toHours(millis),
        TimeUnit.MILLISECONDS.toMinutes(millis) - 
        TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
        TimeUnit.MILLISECONDS.toSeconds(millis) - 
        TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
Junior Batista
quelle
2
Es ist tatsächlich möglich, dies so zu tun : String.format("%02d:%02d:%02d",dur.toHoursPart(), dur.toMinutesPart(), dur.toSecondsPart());. Die Teilemethoden geben Ihnen die richtigen Zahlen, um eine Zeichenfolge zu erstellen. Ich denke, es ist besser lesbar.
Daantie
Randnotiz zu meinem vorherigen Kommentar: Die Teilemethoden sind nur ab JDK 9 verfügbar.
Daantie
40

Es sollte einfacher sein!

Duration.between(startLocalDateTime, endLocalDateTime).toMillis();
Zon
quelle
1
Ich glaube, dies ist tatsächlich die semantisch korrekte Lösung ab Java 8. Und vor dieser Version macht JodaTime dasselbe.
Vrakfall
9

TL; DR

Duration duration = Duration.between(start, end);
duration = duration.minusDays(duration.toDaysPart()); // essentially "duration (mod 1 day)"
Period period = Period.between(start.toLocalDate(), end.toLocalDate());

und dann die Methoden verwenden period.getYears(), period.getMonths(), period.getDays(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart().


Erweiterte Antwort

Ich beantworte die ursprüngliche Frage, dh wie man den Zeitunterschied zwischen zwei LocalDateTimesin Jahren, Monaten, Tagen, Stunden und Minuten erhält, so dass die "Summe" (siehe Hinweis unten) aller Werte für die verschiedenen Einheiten der Summe entspricht zeitliche Differenz, und so , dass der Wert in jeder Einheit kleiner ist als die nächst größere Einheit, dh minutes < 60,hours < 24 und so weiter.

Gegeben zwei LocalDateTimes startund endz

LocalDateTime start = LocalDateTime.of(2019, 11, 29, 17, 15);
LocalDateTime end = LocalDateTime.of(2020, 11, 30, 18, 44);

wir können die absolute Zeitspanne zwischen den beiden mit einem - Durationvielleicht mit darstellen Duration.between(start, end). Die größte Einheit, die wir aus a extrahieren können, Durationsind Tage (als zeitliche Einheit, die 24 Stunden entspricht). Eine Erklärung finden Sie in der folgenden Anmerkung. Um größere Einheiten (Monate, Jahre) zu verwenden, können wir dies Durationmit einem Paar von ( Period, Duration) darstellen, wobei Perioddie Differenz die Differenz bis zu einer Genauigkeit von Tagen misst und die Durationden Rest darstellt:

Duration duration = Duration.between(start, end);
duration = duration.minusDays(duration.toDaysPart()); // essentially "duration (mod 1 day)"
Period period = Period.between(start.toLocalDate(), end.toLocalDate());

Jetzt können wir einfach die auf Periodund definierten Methoden verwenden Duration, um die einzelnen Einheiten zu extrahieren:

System.out.printf("%d years, %d months, %d days, %d hours, %d minutes, %d seconds",
        period.getYears(), period.getMonths(), period.getDays(), duration.toHoursPart(),
        duration.toMinutesPart(), duration.toSecondsPart());
1 years, 0 months, 1 days, 1 hours, 29 minutes, 0 seconds

oder im Standardformat:

System.out.println(period + " + " + duration);
P1Y1D + PT1H29M

Hinweis zu Jahren, Monaten und Tagen

Beachten Sie, dass in java.timeder Konzeption "Einheiten" wie "Monat" oder "Jahr" keinen festen, absoluten Zeitwert darstellen - sie sind datums- und kalenderabhängig, wie das folgende Beispiel zeigt:

LocalDateTime
        start1 = LocalDateTime.of(2020, 1, 1, 0, 0),
        end1 = LocalDateTime.of(2021, 1, 1, 0, 0),
        start2 = LocalDateTime.of(2021, 1, 1, 0, 0),
        end2 = LocalDateTime.of(2022, 1, 1, 0, 0);
System.out.println(Period.between(start1.toLocalDate(), end1.toLocalDate()));
System.out.println(Duration.between(start1, end1).toDays());
System.out.println(Period.between(start2.toLocalDate(), end2.toLocalDate()));
System.out.println(Duration.between(start2, end2).toDays());
P1Y
366
P1Y
365
Anakhand
quelle
5

Es gibt ein Problem mit dem Tapas Bose-Code und dem Thomas-Code. Wenn die Zeitdifferenz negativ ist, erhält das Array die negativen Werte. Zum Beispiel wenn

LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 46, 45);
LocalDateTime fromDateTime = LocalDateTime.of(2014, 9, 9, 7, 46, 45);

es gibt 0 Jahre 0 Monate 1 Tage -1 Stunden 0 Minuten 0 Sekunden zurück.

Ich denke, die richtige Ausgabe ist: 0 Jahre 0 Monate 0 Tage 23 Stunden 0 Minuten 0 Sekunden.

Ich schlage vor, die LocalDateTime-Instanzen in LocalDate- und LocalTime-Instanzen zu trennen. Danach können wir die Java 8 Period- und Duration-Instanzen erhalten. Die Dauer-Instanz wird nach der Anzahl der Tage und dem Tageszeitwert (<24 Stunden) mit anschließender Korrektur des Periodenwerts getrennt. Wenn der zweite LocalTime-Wert vor dem ersten LocalTime-Wert liegt, muss der Zeitraum um einen Tag verkürzt werden.

Hier ist meine Methode zur Berechnung der LocalDateTime-Differenz:

private void getChronoUnitForSecondAfterFirst(LocalDateTime firstLocalDateTime, LocalDateTime secondLocalDateTime, long[] chronoUnits) {
    /*Separate LocaldateTime on LocalDate and LocalTime*/
    LocalDate firstLocalDate = firstLocalDateTime.toLocalDate();
    LocalTime firstLocalTime = firstLocalDateTime.toLocalTime();

    LocalDate secondLocalDate = secondLocalDateTime.toLocalDate();
    LocalTime secondLocalTime = secondLocalDateTime.toLocalTime();

    /*Calculate the time difference*/
    Duration duration = Duration.between(firstLocalDateTime, secondLocalDateTime);
    long durationDays = duration.toDays();
    Duration throughoutTheDayDuration = duration.minusDays(durationDays);
    Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
            "Duration is: " + duration + " this is " + durationDays
            + " days and " + throughoutTheDayDuration + " time.");

    Period period = Period.between(firstLocalDate, secondLocalDate);

    /*Correct the date difference*/
    if (secondLocalTime.isBefore(firstLocalTime)) {
        period = period.minusDays(1);
        Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
                "minus 1 day");
    }

    Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
            "Period between " + firstLocalDateTime + " and "
            + secondLocalDateTime + " is: " + period + " and duration is: "
            + throughoutTheDayDuration
            + "\n-----------------------------------------------------------------");

    /*Calculate chrono unit values and  write it in array*/
    chronoUnits[0] = period.getYears();
    chronoUnits[1] = period.getMonths();
    chronoUnits[2] = period.getDays();
    chronoUnits[3] = throughoutTheDayDuration.toHours();
    chronoUnits[4] = throughoutTheDayDuration.toMinutes() % 60;
    chronoUnits[5] = throughoutTheDayDuration.getSeconds() % 60;
}

Die obige Methode kann verwendet werden, um die Differenz lokaler Datums- und Zeitwerte zu berechnen, zum Beispiel:

public long[] getChronoUnits(String firstLocalDateTimeString, String secondLocalDateTimeString) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    LocalDateTime firstLocalDateTime = LocalDateTime.parse(firstLocalDateTimeString, formatter);
    LocalDateTime secondLocalDateTime = LocalDateTime.parse(secondLocalDateTimeString, formatter);

    long[] chronoUnits = new long[6];
    if (secondLocalDateTime.isAfter(firstLocalDateTime)) {
        getChronoUnitForSecondAfterFirst(firstLocalDateTime, secondLocalDateTime, chronoUnits);
    } else {
        getChronoUnitForSecondAfterFirst(secondLocalDateTime, firstLocalDateTime, chronoUnits);
    }
    return chronoUnits;
}

Es ist praktisch, einen Komponententest für die oben beschriebene Methode zu schreiben (beide sind Mitglieder der PeriodDuration-Klasse). Hier ist der Code:

@RunWith(Parameterized.class)
public class PeriodDurationTest {

private final String firstLocalDateTimeString;
private final String secondLocalDateTimeString;
private final long[] chronoUnits;

public PeriodDurationTest(String firstLocalDateTimeString, String secondLocalDateTimeString, long[] chronoUnits) {
    this.firstLocalDateTimeString = firstLocalDateTimeString;
    this.secondLocalDateTimeString = secondLocalDateTimeString;
    this.chronoUnits = chronoUnits;
}

@Parameters
public static Collection<Object[]> periodValues() {
    long[] chronoUnits0 = {0, 0, 0, 0, 0, 0};
    long[] chronoUnits1 = {0, 0, 0, 1, 0, 0};
    long[] chronoUnits2 = {0, 0, 0, 23, 0, 0};
    long[] chronoUnits3 = {0, 0, 0, 1, 0, 0};
    long[] chronoUnits4 = {0, 0, 0, 23, 0, 0};
    long[] chronoUnits5 = {0, 0, 1, 23, 0, 0};
    long[] chronoUnits6 = {29, 8, 24, 12, 0, 50};
    long[] chronoUnits7 = {29, 8, 24, 12, 0, 50};
    return Arrays.asList(new Object[][]{
        {"2015-09-09 21:46:44", "2015-09-09 21:46:44", chronoUnits0},
        {"2015-09-09 21:46:44", "2015-09-09 22:46:44", chronoUnits1},
        {"2015-09-09 21:46:44", "2015-09-10 20:46:44", chronoUnits2},
        {"2015-09-09 21:46:44", "2015-09-09 20:46:44", chronoUnits3},
        {"2015-09-10 20:46:44", "2015-09-09 21:46:44", chronoUnits4},
        {"2015-09-11 20:46:44", "2015-09-09 21:46:44", chronoUnits5},
        {"1984-12-16 07:45:55", "2014-09-09 19:46:45", chronoUnits6},
        {"2014-09-09 19:46:45", "1984-12-16 07:45:55", chronoUnits6}
    });
}

@Test
public void testGetChronoUnits() {
    PeriodDuration instance = new PeriodDuration();
    long[] expResult = this.chronoUnits;
    long[] result = instance.getChronoUnits(this.firstLocalDateTimeString, this.secondLocalDateTimeString);
    assertArrayEquals(expResult, result);
}

}}

Alle Tests sind erfolgreich, unabhängig davon, ob der Wert der ersten LocalDateTime vor und für alle LocalTime-Werte liegt.

Gennady Kolomoets
quelle
Ich kann Ihre Aussage nicht reproduzieren, dass Thomas-Code unter Verwendung Ihrer obigen Eingabe gemischte Vorzeichen erzeugt. Meine Ausgabe lautet: "0 Jahre 0 Monate 0 Tage 23 Stunden 0 Minuten 0 Sekunden." Und ich habe gerade getestet.
Meno Hochschild
Danke für den Kommentar. Ich habe den Thomas-Code gründlich getestet, tatsächlich funktioniert er korrekt! Die Magie wird von LocalDateTime ausgeführt, bis die Methode die Chronoeinheiten korrigiert. Negative Werte im Thomas-Code werden angezeigt, wenn die erste DateTime später als die zweite ist. Dies kann jedoch leicht korrigiert werden, wie ich es im obigen Code getan habe. Danke noch einmal.
Gennady Kolomoets
Nun, Thomas-Code und meine Bibliothek Time4J, die denselben Algorithmus zur Berechnung der Dauer verwenden, können negative Vorzeichen erzeugen, jedoch nur für die gesamte Dauer. Das ist der entscheidende Punkt! Das Vorzeichen bezieht sich auf die gesamte Dauer und beschreibt daher, ob der Start später als das Ende ist und bezieht sich niemals auf einzelne Dauer-Komponenten. Gemischte Zeichen sind hier nicht möglich und würden eine solche Interpretation von Anfang relativ zu Ende verhindern (Gegenbeispiel sind Joda-Zeitperiode oder java.time.Periodwo Benutzer solche gemischten Zeichen aufgrund unterschiedlichen internen Designs erzwingen / erzeugen können).
Meno Hochschild
5

Und die Version von @Thomas in Groovy mit nimmt die gewünschten Einheiten in eine Liste, anstatt die Werte fest zu codieren. Diese Implementierung (die leicht nach Java portiert werden kann - ich habe die Funktionsdeklaration explizit gemacht) macht den Thomas-Ansatz wiederverwendbarer.

def fromDateTime = LocalDateTime.of(1968, 6, 14, 0, 13, 0)
def toDateTime = LocalDateTime.now()
def listOfUnits = [
    ChronoUnit.YEARS, ChronoUnit.MONTHS, ChronoUnit.DAYS,
    ChronoUnit.HOURS, ChronoUnit.MINUTES, ChronoUnit.SECONDS,
    ChronoUnit.MILLIS]

println calcDurationInTextualForm(listOfUnits, fromDateTime, toDateTime)    

String calcDurationInTextualForm(List<ChronoUnit> listOfUnits, LocalDateTime ts, LocalDateTime to)
{
    def result = []

    listOfUnits.each { chronoUnit ->
        long amount = ts.until(to, chronoUnit)
        ts = ts.plus(amount, chronoUnit)

        if (amount) {
            result << "$amount ${chronoUnit.toString()}"
        }
    }

    result.join(', ')
}

Zum Zeitpunkt dieses Schreibens wird der obige Code zurückgegeben 47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis. Und für die Eingabe von @Gennady Kolomoets wird der Code zurückgegeben23 Hours .

Wenn Sie eine Liste von Einheiten angeben, muss diese nach der Größe der Einheiten sortiert sein (größte zuerst):

def listOfUnits = [ChronoUnit.WEEKS, ChronoUnit.DAYS, ChronoUnit.HOURS]
// returns 2495 Weeks, 3 Days, 8 Hours
ChrLipp
quelle
4

Hier ist eine sehr einfache Antwort auf Ihre Frage. Es klappt.

import java.time.*;
import java.util.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class MyClass {
public static void main(String args[]) {
    DateTimeFormatter T = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
    Scanner h = new Scanner(System.in);

    System.out.print("Enter date of birth[dd/mm/yyyy hh:mm]: ");
    String b = h.nextLine();

    LocalDateTime bd = LocalDateTime.parse(b,T);
    LocalDateTime cd = LocalDateTime.now();

    long minutes = ChronoUnit.MINUTES.between(bd, cd);
    long hours = ChronoUnit.HOURS.between(bd, cd);

    System.out.print("Age is: "+hours+ " hours, or " +minutes+ " minutes old");
}
}
SavedBeau
quelle
1
Ich sollte erwähnen, dass dies nicht korrekt ist. versuche es damit. es heißt 1 Minute Unterschied. LocalDateTime bd = LocalDateTime.of (2019, 11, 26, 15, 03, 55); LocalDateTime cd = LocalDateTime.of (2019, 11, 26, 15, 04, 45);
Mark Amabile
Ich habe letzte Nacht eine Zeit eingegeben und bekommen Age is: 0 years,0 months, 1 days, -10 hours, -48 minutes old. Ich glaube nicht, dass dies erwünscht war.
Ole VV
@ OleV.V. Das Problem wurde
behoben
1

Joda-Zeit

Da viele der Antworten API 26- Unterstützung erforderten und meine min-API 23 war, löste ich sie mit dem folgenden Code:

import org.joda.time.Days

LocalDate startDate = Something
LocalDate endDate = Something
// The difference would be exclusive of both dates, 
// so in most of use cases we may need to increment it by 1
Days.daysBetween(startDate, endDate).days
Kushal
quelle
1
Das Joda-Time- Projekt befindet sich im Wartungsmodus, und sein Ersteller Stephen Colebourne hat die in JSR 310 definierten java.time- Klassen erstellt. Der größte Teil der java.time- Funktionalität wird im ThreeTen- auf Java 6 & 7 zurückportiert. Backport- Projekt. Weiter angepasst für frühes Android im ThreeTenABP- Projekt.
Basil Bourque
1

Nach mehr als fünf Jahren beantworte ich meine Frage. Ich denke, dass das Problem mit einer negativen Dauer durch eine einfache Korrektur gelöst werden kann:

LocalDateTime fromDateTime = LocalDateTime.of(2014, 9, 9, 7, 46, 45);
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 46, 45);

Period period = Period.between(fromDateTime.toLocalDate(), toDateTime.toLocalDate());
Duration duration = Duration.between(fromDateTime.toLocalTime(), toDateTime.toLocalTime());

if (duration.isNegative()) {
    period = period.minusDays(1);
    duration = duration.plusDays(1);
}
long seconds = duration.getSeconds();
long hours = seconds / SECONDS_PER_HOUR;
long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
long secs = (seconds % SECONDS_PER_MINUTE);
long time[] = {hours, minutes, secs};
System.out.println(period.getYears() + " years "
            + period.getMonths() + " months "
            + period.getDays() + " days "
            + time[0] + " hours "
            + time[1] + " minutes "
            + time[2] + " seconds.");

Hinweis: Die Website https://www.epochconverter.com/date-difference berechnet den Zeitunterschied jetzt korrekt.

Vielen Dank für Ihre Diskussion und Vorschläge.

Gennady Kolomoets
quelle
Danke für die Antwort. Für die Stunden und kleiner können Sie von dem profitieren toHours, toMinutesund toSecondsMethoden Duration. Seit Java 9 noch mehr von toMinutesPart()und toSecondsPart(). Und ich sehe keinen Sinn darin, die Stunden, Minuten und Sekunden in ein Array zu packen, nur um sie wieder herauszunehmen. Im Allgemeinen jedoch eine gute Antwort.
Ole VV
Nach mehr als fünf Jahren beantworte ich meine Frage. Die Frage wurde von einem Konto mit einem anderen Namen, Titel und Ort gestellt. Es scheint nicht viel Ähnlichkeit zu geben. Nicht, dass es wichtig wäre, ich frage mich nur.
Ole VV
"Nach mehr als fünf Jahren beantworte ich meine Frage" Ich war wohl der OP :)
Tapas Bose