wake-up-neo.net

Java 8: Differenz zwischen zwei LocalDateTime berechnen

Ich versuche, die Differenz zwischen zwei LocalDateTime zu berechnen.

Die Ausgabe muss das Format y years m months d days h hours m minutes s seconds haben. 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 erhalte, ist 29 years 8 months 24 days 12 hours 0 minutes 50 seconds. Ich habe mein Ergebnis von dieser Website (mit den Werten 12/16/1984 07:45:55 und 09/09/2014 19:46:45) überprüft. Der folgende Screenshot zeigt die Ausgabe:

Epoch Converter

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

Aktualisieren

Ich habe mein Ergebnis von einer anderen Website getestet und das Ergebnis ist unterschiedlich. Hier ist es: Dauer zwischen zwei Daten berechnen (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 LocalDateTime Objekte 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 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds sein. Der Algorithmus muss also auch die negativen Zahlen verarbeiten.

Beachten Sie, dass es bei der Frage nicht darum geht, welche Site zu welchem ​​Ergebnis geführt hat. Ich muss den richtigen Algorithmus kennen und die richtigen Ergebnisse erzielen.

218
Tapas Bose

Leider scheint es keinen Zeitraum zu geben, der sich über die Zeit erstreckt, so dass Sie die Berechnungen möglicherweise selbst durchführen müssen.

Zum Glück gibt es in den Datums- und Zeitklassen viele Dienstprogrammmethoden, die dies in gewissem Maße 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 vorübergehendes Startdatum und bringen Sie die vollen Jahre zum Ende. Passen Sie dieses Datum dann um die Anzahl der Jahre an, sodass das Startdatum weniger als ein Jahr vom Ende entfernt ist. Wiederholen Sie dies für jede Zeiteinheit in absteigender Reihenfolge.

Zum Schluss noch ein Hinweis: Ich habe verschiedene Zeitzonen nicht berücksichtigt (beide Daten sollten in derselben Zeitzone liegen) und ich habe auch nicht geprüft, wie sich die Sommerzeit oder andere Änderungen in einem Kalender (wie die Zeitzonenänderungen in Samoa) ändern. beeinflussen diese Berechnung. Also mit Vorsicht anwenden.

138
Thomas

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

421
satnam

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)));
34
Junior Batista

Es sollte einfacher sein!

Duration.between(startLocalDateTime, endLocalDateTime).toMillis();
20
Zon

Und die Version von @Thomas in Groovy mit nimmt die gewünschten Einheiten in eine Liste auf, anstatt die Werte fest zu codieren. Diese Implementierung (die leicht nach Java portiert werden kann - ich habe die Funktionsdeklaration explizit gemacht) macht den Ansatz von Thomas 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 gibt der obige Code 47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis zurück. Bei der Eingabe von @Gennady Kolomoets gibt der Code 23 Hours zurück.

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
5
ChrLipp

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

import Java.time.*;
import Java.util.*;
import Java.time.format.DateTimeFormatter;
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();

       int hr = cd.getHour() - bd.getHour();
       int mn = cd.getMinute() - bd.getMinute();

       Period time = Period.between(bd.toLocalDate(),cd.toLocalDate());

       System.out.print("Age is: "+time.getYears()+ " years,"+time.getMonths()+ " months, " +time.getDays()+ " days, "+hr+ " hours, " +mn+ " minutes old");
    }
}
4
SavedBeau

Es gibt ein Problem mit dem Tapas Bose-Code und dem Thomas-Code. Wenn der Zeitunterschied 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 auf LocalDate- und LocalTime-Instanzen zu trennen. Danach können wir die Java 8 Period and Duration-Instanzen erhalten. Die Duration-Instanz wird nach Anzahl der Tage und ganztägigem Zeitwert (<24h) mit anschließender Korrektur des Periodenwerts getrennt. Wenn der zweite LocalTime-Wert vor dem ersten LocalTime-Wert liegt, muss der Zeitraum um einen Tag verringert werden.

So kann ich die LocalDateTime-Differenz berechnen:

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 von lokalen Datums- und Zeitwerten zu berechnen. 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 obige Methode zu schreiben (beide sind PeriodDuration-Klassenmitglieder). 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 des ersten LocalDateTime vor und für einen LocalTime-Wert liegt.

3