ChatGPT解决这个技术问题 Extra ChatGPT

Java 8: Difference between two LocalDateTime in multiple units

I am trying to calculate the difference between two LocalDateTime.

The output needs to be of the format y years m months d days h hours m minutes s seconds. Here is what I have written:

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

The output that I am getting is 29 years 8 months 24 days 12 hours 0 minutes 50 seconds. I have checked my result from this website (with values 12/16/1984 07:45:55 and 09/09/2014 19:46:45). The following screenshot shows the output:

https://i.stack.imgur.com/SAJE4.png

I am pretty sure that the fields after the month value is coming wrong from my code. Any suggestion would be very helpful.

Update

I have tested my result from another website and the result I got is different. Here it is: Calculate duration between two dates (result: 29 years, 8 months, 24 days, 12 hours, 0 minutes and 50 seconds).

Update

Since I got two different results from two different sites, I am wondering if the algorithm of my calculation is legitimate or not. If I use following two LocalDateTime objects:

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

Then the output is coming: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.

From this link it should be 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds. So the algorithm needs to handle the negative numbers too.

Note the question is not about which site gave me what result, I need to know the right algorithm and need to have right results.

Just a guess, but might Period.between() apply some rounding?
I just looked at the code once more and it seems the website is wrong (Try calculating yourself). If you omit the date, i.e. differences in year, month and day, you'll get the start time 7:45:55 and the end time 19:46:45 (or 7:46:45 PM). So the difference between those two times is 12 hours, 0 minutes and 50 seconds and never 23 hours, 34 minutes and 12 seconds. So your calculation actualle seems to be correct, at least on the time part.
Interesting phenomenon on that website: add 10 years to starting date and the difference in hours changes from 23 to 8 - surely a sign of bug.
Note that since LocalDateTime has no time zone, there might not be a unique answer. Even if you assume the start and end time zones are the same, in certain zones dates like 2014-09-09 will be in Daylight Saving Time or Summer Time and in others it will not. This might throw things off by an hour. So computing the difference to the second is meaningless unless this is resolved.
Do you understand that using LocalDateTime yields unrealistic results, as that class purposely lacks any concept of time zone or offset-from-UTC? For realistic values, assign a time zone via ZoneId to use ZonedDateTime.

s
satnam

I found the best way to do this is with ChronoUnit.

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

Additional documentation is here: https://docs.oracle.com/javase/tutorial/datetime/iso/period.html


I don't see a real improvement. Compared with accepted Thomas' answer you just replace tempDateTime.until( toDateTime, ChronoUnit.YEARS) with ChronoUnit.YEARS.between(fromDate, toDate). But the important additional lines like tempDateTime.plusYears( years ) etc. are completely missing in your answer so it does not help the OP. The complete algorithm matters!
I like this better since it is more succinct, and more read readable. This is the best way to find the difference between two dates.
@SomaiahKumbera No you completely miss the critical point. The OP does not want a duration in only one unit but multiple units, and therefore this answer is not a real answer at all. The OP's concern is simply not addressed. Please be so kind to read again the question. (A lot of upvoters seem to have misunderstood the question).
@MenoHochschild, I think you are actually the one missing the critical point. The OP got their answer over a year ago. The 36 thousand people who are viewing this question do not care about the OP's specific question. They were lead here by google and my answer provided them with what they were looking for -- a clean and simple solution for getting the difference between two dates.
I needed the seconds difference between two date times and started implementing my own solution. Shortly it seems to complex and I took on google. satnam answer may not answer OP question but for sure helped me a lot and I bet many many others like me.
N
Nikolai Shevchenko

Unfortunately, there doesn't seem to be a period class that spans time as well, so you might have to do the calculations on your own.

Fortunately, the date and time classes have a lot of utility methods that simplify that to some degree. Here's a way to calculate the difference although not necessarily the fastest:

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.

The basic idea is this: create a temporary start date and get the full years to the end. Then adjust that date by the number of years so that the start date is less then a year from the end. Repeat that for each time unit in descending order.

Finally a disclaimer: I didn't take different timezones into account (both dates should be in the same timezone) and I also didn't test/check how daylight saving time or other changes in a calendar (like the timezone changes in Samoa) affect this calculation. So use with care.


You have the same basic idea, therefore my upvote, but please try to use the same input otherwise the different result is confusing.
@MenoHochschild it is the same input, just taken from the update where the OP has issues with negative times. ;)
Good algorithm, but wrong type (see Thomas disclaimer). Before doing your calculation, you should convert your LocalDateTime variables to ZonedDateTime using default time zone for example (or any time zone you need) : ZonedDateTime fromZonedDateTime = fromDateTime.atZone(ZoneId.systemDefault());
@Thomas Your example is using Java 8 as well - and the question is tagged as java-8. In fact your example is even using ChronoUnit :) but doing the job "by hand".
@EugeneBeresovsky oh yes, you're right. Totally missed that ;) - Besides that, you'd still have to subtract the larger units from the interval since for example long minutes = ChronoUnit.MINUTES.between(fromDate, toDate); would return a number greater than 59 for any interval of at least 1 hour - and that's not what the OP wants. Taking that into account you'd not be able to just make 6 calls to ChronoUnit#between() and be done.
Z
Zon

It should be simpler!

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

You can convert millis to whatever unit you like:

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

I believe this is actually the semantically correct solution starting from Java 8. And before that version, JodaTime does the same.
This does not answer the question at all. "Difference between two LocalDateTime in multiple units"
n
naXa stands with Ukraine

Here a single example using Duration and TimeUnit to get 'hh:mm:ss' format.

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

It's actually possible to do it like this: String.format("%02d:%02d:%02d",dur.toHoursPart(), dur.toMinutesPart(), dur.toSecondsPart());. The part methods give you the right numbers to build a string. I think it' more readable.
Side note to my previous comment: the part methods are only available from JDK 9 and up.
A
Anakhand

TL;DR

// get the calendar period between the times (years, months & days)
Period period = Period.between(start.toLocalDate(), end.toLocalDate());
// make sure to get the floor of the number of days
period = period.minusDays(end.toLocalTime().compareTo(start.toLocalTime()) >= 0 ? 0 : 1);

// get the remainder as a duration (hours, minutes, etc.)
Duration duration = Duration.between(start, end);
// remove days, already counted in the period
duration = duration.minusDays(duration.toDaysPart());

and then use the methods period.getYears(), period.getMonths(), period.getDays(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart().

Try it online!

Expanded answer

I'll answer the original question, i.e. how to get the time difference between two LocalDateTimes in years, months, days, hours & minutes, such that the "sum" (see note below) of all the values for the different units equals the total temporal difference, and such that the value in each unit is smaller than the next bigger unit—i.e. minutes < 60, hours < 24, and so on.

Given two LocalDateTimes start and end, e.g.

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

we can represent the absolute timespan between the two with a Duration—perhaps using Duration.between(start, end). But the biggest unit we can extract out of a Duration is days (as a temporal unit equivalent to 24h)—see the note below for an explanation. To use larger units (months, years) we can represent this Duration with a pair of (Period, Duration), where the Period measures the difference up to a precision of days and the Duration represents the remainder.

To get the Period:

Period period = Period.between(start.toLocalDate(), end.toLocalDate());

We need to be careful here, because a Period is really a date difference, not an amount of time, and all its calculations are based on calendar dates (see the section below). For example, from 1st January 2000 at 23:59 to 2nd January 2000 at 00:01, a Period would say there is a difference of 1 day, because that's the difference between the two dates, even though the time delta is less than 24h. So, if the time of day on the end datetime is earlier than the time of day on the start datetime, we need to subtract 1 from the day count, since it doesn't correspond to a complete 24h span (the corresponding remainder will be recorded by our Duration):

period = period.minusDays(end.toLocalTime().compareTo(start.toLocalTime()) >= 0 ? 0 : 1);

To get the remainder, as a Duration:

Duration duration = Duration.between(start, end);

Since the difference up to a precision of days is already taken care of by our period, we only need to keep smaller units (hours, minutes, etc.):

duration = duration.minusDays(duration.toDaysPart()); // essentially "duration (mod 1 day)"

Now we can simply use the methods defined on Period and Duration to extract the individual units:

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, 23 hours, 29 minutes, 0 seconds

or, using the default format:

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

Note on years, months & days

Note that, in java.time's conception, period "units" like "month" or "year" don't represent a fixed, absolute temporal value—they're date- and calendar-dependent, as the following example illustrates:

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

As another example, from 1st January 2000 at 23:59 to 2nd January 2000 at 00:01, a Period would say there is a difference of 1 day, because that's the difference between the two dates, even though the time delta is less than 24h.


T
Tinashe

Here is a very simple answer to your question. It works.

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

I should mention this is not accurate. try with this. it says 1 minute difference. LocalDateTime bd = LocalDateTime.of(2019, 11, 26, 15, 03, 55); LocalDateTime cd = LocalDateTime.of(2019, 11, 26, 15, 04, 45);
I entered a time last night and got Age is: 0 years,0 months, 1 days, -10 hours, -48 minutes old. I don’t think this is what was desired.
@OleV.V. Fixed that issue
C
ChrLipp

And the version of @Thomas in Groovy with takes the desired units in a list instead of hardcoding the values. This implementation (which can easily ported to Java - I made the function declaration explicit) makes Thomas approach more reuseable.

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(', ')
}

At the time of this writing,the code above returns 47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis. And, for @Gennady Kolomoets input, the code returns 23 Hours.

When you provide a list of units it must be sorted by size of the units (biggest first):

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

P
Patrick

There is some problem for Tapas Bose code and Thomas code. If time differenсe is negative, array gets the negative values. For example if

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

it returns 0 years 0 months 1 days -1 hours 0 minutes 0 seconds.

I think the right output is: 0 years 0 months 0 days 23 hours 0 minutes 0 seconds.

I propose to separate the LocalDateTime instances on LocalDate and LocalTime instances. After that we can obtain the Java 8 Period and Duration instances. The Duration instance is separated on the number of days and throughout-the-day time value (< 24h) with subsequent correction of the period value. When the second LocalTime value is before the firstLocalTime value, it is necessary to reduce the period for one day.

Here's my way to calculate the LocalDateTime difference:

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

The above method can be used to calculate the difference of any local date and time values, for example:

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

It is convenient to write a unit test for the above method (both of them are PeriodDuration class members). Here's the 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);
}

}

All tests are successful whether or not the value of the first LocalDateTime is before and for any LocalTime values.


I cannot reproduce your statement that Thomas code using your input above produces mixed signs. My output is: "0 years 0 months 0 days 23 hours 0 minutes 0 seconds.". And I have tested just now.
Thank you for the comment. I thoroughly tested the Thomas code, indeed, it is working correctly! The magic is done by LocalDateTime until method correcting chrono units. Negative values in the Thomas code appear if the first DateTime is later than the second. But this can be easily corrected, for example, as I did in the above code. Thanks again.
Well, Thomas code and my library Time4J using the same algorithm for calculation of durations can produce negative signs but only for the whole duration. That is the crucial point! The sign relates to the whole duration and hence describes if the start is later than the end and is never related to single duration components. Mixed signs are not possible here and would prevent such an interpretation of start relative to end (counter example are Joda-Time-period or java.time.Period where users can enforce/produce such mixed signs due to different internal design).
G
Gennady Kolomoets

After more than five years I answer my question. I think that the problem with a negative duration can be solved by a simple correction:

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.");

Note: The site https://www.epochconverter.com/date-difference now correctly calculates the time difference.

Thank you all for your discussion and suggestions.


Thanks for the answer. For the hours and smaller you can benefit from the toHours, toMinutesand toSeconds methods of Duration. Since Java 9 even more from toMinutesPart() and toSecondsPart(). And I don’t see thepoint in wrapping the hours, mins and secs into an array only to take them out again. Generally a good answer, though.
B
Basil Bourque

Joda-Time

Since many of the answers required API 26 support and my min API was 23, I solved it by below 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

The Joda-Time project is in maintenance-mode, its creator Stephen Colebourne having gone on to create the java.time classes defined in JSR 310. Most of the java.time functionality is back-ported to Java 6 & 7 in the ThreeTen-Backport project. Further adapted for early Android in the ThreeTenABP project.