395 Stimmen

Java 8: Unterschied zwischen zwei LocalDateTime in mehreren Einheiten

Ich versuche, den Unterschied zwischen zwei LocalDateTime zu berechnen.

Die Ausgabe muss im Format y Jahre m Monate d Tage h Stunden m Minuten s Sekunden sein. Hier ist, was ich geschrieben habe:

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() + " Jahre " + 
                period.getMonths() + " Monate " + 
                period.getDays() + " Tage " +
                time[0] + " Stunden " +
                time[1] + " Minuten " +
                time[2] + " Sekunden.");

    }

    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 erhalte, ist 29 Jahre 8 Monate 24 Tage 12 Stunden 0 Minuten 50 Sekunden. Ich habe mein Ergebnis von dieser Website überprüft (mit den Werten 16.12.1984 07:45:55 und 09.09.2014 19:46:45). Der folgende Screenshot zeigt die Ausgabe:

Epoch Converter

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

Aktualisierung

Ich habe mein Ergebnis von einer anderen Website getestet und das Ergebnis, das ich erhalten habe, 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).

Aktualisierung

Da ich von zwei verschiedenen Websites zwei verschiedene Ergebnisse erhalten habe, frage ich mich, ob der Algorithmus meiner Berechnung legitim ist oder nicht. Wenn ich die folgenden zwei LocalDateTime-Objekte verwende:

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

Dann lautet die Ausgabe: 29 Jahre 8 Monate 25 Tage -1 Stunden -5 Minuten -10 Sekunden.

Wie aus diesem Link hervorgeht, sollte es 29 Jahre 8 Monate 24 Tage 22 Stunden, 54 Minuten und 50 Sekunden sein. Daher muss der Algorithmus auch mit negativen Zahlen umgehen können.

Bitte beachten Sie, dass es nicht um die Frage geht, welche Site mir welches Ergebnis gegeben hat. Ich möchte den richtigen Algorithmus kennen und die richtigen Ergebnisse haben.

713voto

satnam Punkte 9520

Ich habe herausgefunden, dass der beste Weg, dies zu tun, mit ChronoUnit ist.

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

Zusätzliche Dokumentation finden Sie hier: Period und Duration

229voto

Thomas Punkte 84982

Es scheint Verwirrung darüber zu geben, wie der Unterschied zwischen 2 Zeitpunkten in Java berechnet wird und wie die Antworten auf die Frage des OP angewendet werden können.

Da viele Leute hierher geleitet zu werden scheinen, werde ich meine Antwort aktualisieren, um einen umfassenderen Überblick darüber zu bieten, was andere vorgeschlagen haben - also Lob an sie.

Da Java 8 das java.time API eingeführt hat, wird diese Antwort nur dieses API behandeln. Wenn Sie Java 7 oder früher verwenden, können Sie Joda Time verwenden, um etwas Ähnliches zu tun.

Bevor wir mit den verschiedenen Ansätzen beginnen, lassen Sie uns unsere Testdaten gemäß der Anfrage des OP festlegen:

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

Unterschied zwischen 2 Zeitstempeln in einer einzelnen Einheit

Wenn Sie den Unterschied in einer einzelnen Einheit erhalten möchten, sagen wir Tage oder Sekunden, können Sie direkt mehrere Optionen von java.time verwenden:

Dauer

Dauer d = Duration.between(fromDateTime, toDateTime);
long Tage = d.toDays();
long Sekunden = d.toSeconds();

Anmerkungen:

  • Das Erhalten von Einheiten größer als Tage ist nicht direkt.
  • Die toXxx()-Methoden geben keine Bruchteile von Einheiten zurück, daher würde das obige Beispiel 10859 Tage anstelle von ungefähr 10859,95 Tagen zurückgeben. Wenn Sie also Bruchteile von Einheiten benötigen, müssen Sie selbst berechnen, z.B. double Tage = d.toMillis() / (24 * 60 * 60 * 1000.0); (oder double Tage = d.toMillis() / (double)Duration.ofDays(1).toMillis();)

ChronoUnit

long Tage = ChronoUnit.DAYS.between(fromDateTime, toDateTime);
long Sekunden = ChronoUnit.SECONDS.between(fromDateTime, toDateTime);

Anmerkungen:

  • Dies kann einfacher zu lesen sein als Duration.between(...).toDays()
  • Einheiten bis zu Jahrhunderten werden unterstützt.
  • Wie bei Duration.toXxx() unterstützt dies keine Bruchteile von Einheiten, daher müssen Sie selbst berechnen.
  • Wenn Sie denselben Zeitraum in verschiedenen Einheiten ausdrücken müssen, kann die Verwendung von Dauer schneller sein.

Temporal.until()

long Tage = fromDateTime.until(toDateTime, ChronoUnit.DAYS);
long Tage = fromDateTime.until(toDateTime, ChronoUnit.SECONDS);

Anmerkungen:

  • ChronoUnit.between() verwendet dies tatsächlich im Hintergrund, also gelten die gleichen Einschränkungen. Es ist mehr eine Frage des Stils und der Lesbarkeit.
  • Temporal wird von mehreren Klassen implementiert, z.B. LocalDateTime, LocalTime, ZonedDateTime usw.

Unterschied zwischen 2 Zeitstempeln in mehreren Teilen mit verschiedenen Einheiten

Semi-manuelle Berechnungen

Das ist meine ursprüngliche Antwort, die sich mit der Frage befasst, wie eine Dauer in mehrere Brucheinheiten aufgeteilt wird, z.B. 29 Jahre + 8 Monate usw.

Es ist "semi-manuell", weil Sie einen Algorithmus schreiben müssen, um die Reihenfolge richtig zu bekommen (der manuelle Teil), aber trotzdem vorgefertigte Berechnungen wie plusXxx() und until() verwenden können (der automatische Teil).

LocalDateTime tempDateTime = LocalDateTime.from( fromDateTime );

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

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

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

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

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

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

System.out.println( Jahre + " Jahre " + 
        Monate + " Monate " + 
        Tage + " Tage " +
        Stunden + " Stunden " +
        Minuten + " Minuten " +
        Sekunden + " Sekunden.");

//gibt aus: 29 Jahre 8 Monate 24 Tage 22 Stunden 54 Minuten 50 Sekunden.

Die Grundidee ist folgende: Erstellen Sie ein vorübergehendes Startdatum und erhalten Sie die vollen Jahre bis zum Ende. Passen Sie dann dieses Datum um die Anzahl der Jahre an, so dass das Startdatum weniger als ein Jahr vom Ende entfernt ist. Wiederholen Sie das für jede Zeit-Einheit in absteigender Reihenfolge.

Zum Schluss ein Haftungsausschluss: Ich habe verschiedene Zeitzonen nicht berücksichtigt (beide Daten sollten in der gleichen Zeitzone sein) und habe auch nicht getestet/überprüft, wie die Umstellung auf die Sommerzeit oder andere Änderungen im Kalender (wie die Zeitzonenänderungen in Samoa) diese Berechnung beeinflussen. Verwenden Sie also vorsichtig.

Anmerkungen:

  • Dies ermöglicht es Ihnen, für alle benötigten Einheiten zu berechnen.
  • Vergewissern Sie sich, dass Sie die Berechnungen in der richtigen Reihenfolge durchführen, d.h. von der größten Einheit zur kleinsten.

[Dauer.toDaysPart() usw.](https://docs.oracle.com/en%2Fjava%2Fjavase%2F21%2Fdocs%2Fapi%2F%2F/java.base/java/time/Duration.html#toDaysPart())

Ab Java 9 (das 3 Jahre nach der Frage des OP veröffentlicht wurde) hat die Klasse Dauer jetzt einige toXxxPart()-Methoden, sodass Sie Folgendes tun könnten:

Dauer d = Duration.between(fromDateTime, toDateTime);
long Tage = d.toDaysPart();
long Sekunden = d.toSecondsPart();

Anmerkungen:

  • Einheiten über Tage werden ab Java 21 nicht unterstützt
  • Einheiten dazwischen werden vermisst, d.h. das obige Beispiel gibt 10859 Tage von toDaysPart() zurück, aber nur 50 Sekunden von toSecondsPart(), sodass Sie 22 Stunden und 54 Minuten dazwischen verpassen würden. Die Verwendung von Temporal.until() wie in meiner ursprünglichen Antwort ermöglicht es Ihnen, die Beispiel-Dauer als "10859 Tage und 82490 Sekunden" auszudrücken.

Manuelle Berechnungen

Eine letzte Option, die _möglicherweise_ schneller ist als die anderen (wenn auch wahrscheinlich sehr vernachlässigbar), wäre alles selbst zu berechnen.

Die Grundidee besteht darin, die Millisekunden zwischen den 2 Daten zu erhalten und dann Ihre Berechnungen durchzuführen. Der Einfachheit halber werde ich Dauer.toMillis() verwenden, aber es gibt auch andere Optionen, die möglicherweise schneller sind.

 long Millisekunden = Duration.between(fromDateTime, toDateTime).toMillis();
 long MillisekundenProTag = 24 * 60 * 60 * 1000; //24 Stunden * 60 Minuten * 60 Sekunden * 1000 Millisekunden
 long Tage = Millisekunden / MillisekundenProTag; //Beispiel ergibt 10859
 long Sekunden = Millisekunden / 1000; //Beispiel ergibt 938300090

Anmerkungen:

  • Dies könnte die schnellste Berechnung sein und für einfache Anwendungsfälle ausreichend sein
  • Wenn Sie größere Einheiten wie Monate oder Jahre verwenden, müssen Sie möglicherweise Längenunterschiede berücksichtigen (Monate reichen von 28 bis 31 Tagen, Jahre von 365 bis 366 Tagen usw.), was dies komplexer und besonders fehleranfällig machen würde.

130voto

Zon Punkte 15646

Es sollte einfacher sein!

Dauer.zwischen(startLocalDateTime, endLocalDateTime).zuMillis();

Sie können Millisekunden in die gewünschte Einheit umwandeln:

String.format("%d Minuten %d Sekunden", 
  TimeUnit.MILLISECONDS.toMinutes(millis),
  TimeUnit.MILLISECONDS.toSeconds(millis) - 
  TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));

58voto

Junior Batista Punkte 597

Hier ein einfaches Beispiel zur Verwendung von Duration und TimeUnit, um das Format 'hh:mm:ss' zu erhalten.

Dauer dur = Dauer.zwischen(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)));

7voto

Tinashe Punkte 992

Hier ist eine sehr einfache Antwort auf deine Frage. Es funktioniert.

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("Geburtsdatum eingeben[tt/mm/jjjj 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("Alter ist: "+hours+ " Stunden, oder " +minutes+ " Minuten alt");
}
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X