Tapas Bose
Tapas Bose

Reputation: 29806

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:

Epoch Converter

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.

Upvotes: 410

Views: 518895

Answers (12)

Thomas
Thomas

Reputation: 88707

There seems to be some confusion on how to calculate the difference between 2 points in time in Java and how to apply the answers to the OP's question.

Since many people seem to be directed here I'll update my answer to provide a more comprehensive view of what others suggested as well - so kudos to them.

Since Java 8 introduced the java.time API this answer will only deal with this API. If you're using Java 7 or earlier you could use Joda Time to do something similar.

Before we start with the various approaches, let's establish our test data as per the OP's request:

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

Difference between 2 timestamps in a single unit

If you want to get the difference in a single unit, let's say days or seconds you can directly use several options provided by java.time:

Duration

Duration d = Duration.between(fromDateTime, toDateTime);
long days = d.toDays();
long seconds = d.toSeconds();

Notes:

  • Getting units larger than days is not straight forward.
  • The toXxx() methods won't return fractional units so the example above would return 10859 days instead of about 10859.95 days. So if you need fractional units you need to calculate yourself, e.g. double days = d.toMillis() / (24 * 60 * 60 * 1000.0); (or double days = d.toMillis() / (double)Duration.ofDays(1).toMillis();)

ChronoUnit

long days = ChronoUnit.DAYS.between(fromDateTime, toDateTime);
long seconds = ChronoUnit.SECONDS.between(fromDateTime, toDateTime);

Notes:

  • This may be easier to read than Duration.between(...).toDays()
  • Units up to centuries are supported.
  • As with Duration.toXxx() this doesn't support fractional units so you'd need to calculate yourself.
  • If you need to express the same period in different units using Duration may be faster

Temporal.until()

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

Notes:

  • ChronoUnit.between() actually uses this behind the scenes so the same limitations apply. It's more a matter of style and readbility.
  • Temporal is implemented by several classes, e.g. LocalDateTime, LocalTime, ZonedDateTime etc.

Difference between 2 timestamps in multiple parts with different units

Semi-manual calculations

This is my original answer which deals with the question on how to split a duration into multiple fractional units, e.g. 29 years + 8 months etc.

It's "semi-manual" because you'll need to write some form of algorithm to get order right (the manual part) but can still use read-to-use calculations like plusXxx() and until() (the automatic part).

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.

Notes:

  • This lets you calculate for all the units you need.
  • Make sure you get the calculations into the right order, i.e. from largest unit to smallest

Duration.toDaysPart() etc.

As of Java 9 (which got published 3 years after the OP's question) the Duration class now has a couple of toXxxPart() methods so you could do the following:

Duration d = Duration.between(fromDateTime, toDateTime);
long days = d.toDaysPart();
long seconds = d.toSecondsPart();

Notes:

  • Units above days are not supported as of Java 21
  • Units in between will be missed, i.e. the example above will return 10859 days from toDaysPart() but only 50 seconds from toSecondsPart() so you'd miss 22 hours and 54 minutes in between. Using Temporal.until() as in my original answer gives you the ability to express the example duration as "10859 days and 82490 seconds"

Manual calculations

A final option that _may_ be faster than the others (albeit probably very negligible) would be to calculate everything yourself.

The basic idea is to get the milliseconds between the 2 dates and then do your calculations. For sake of simplicity I'll use Duration.toMillis() but there are other options as well which might be faster.

 long millis = Duration.between(fromDateTime, toDateTime).toMillis();
 long millisPerDay = 24 * 60 * 60 * 1000; //24 hours * 60 minutes * 60 seconds * 1000 millis
 long days = millis / millisPerDay; //example yields 10859
 long seconds = millis / 1000; //example yields 938300090

Notes:

  • This may be the fastest calculation and be sufficient for simple use cases
  • If you're using larger units like months or years you may need to take length differences into account (months range from 28 to 31 days, years from 365 to 366 days, etc.) which would make this more complex and especially error prone

Upvotes: 236

Robert Niestroj
Robert Niestroj

Reputation: 16131

If you don't need years and months you can use DurationFormatUtils from Apache Commons Lang 3. It uses miliseconds as the duration. You can get the miliseconds from java.time Duration class.

LocalDateTime start = LocalDateTime.of(2023,10,1,7,14,3);
LocalDateTime end = LocalDateTime.of(2023,10,1,8,55,22);
Duration d = Duration.between(start, end);
DurationFormatUtils.formatDuration(d.toMillis(), "d 'days' H 'hours' m 'minutes' s 'seconds'", true);

 // "0 days 1 hour 41 minutes 19 seconds"

For other possiblities check out the JavaDoc.

Upvotes: 2

satnam
satnam

Reputation: 11464

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: Period and Duration

Upvotes: 733

Anakhand
Anakhand

Reputation: 2988

Keep in mind: calendar months and years do not represent a fixed temporal length. For example, if this code shows a difference of 1 month, it could mean anything from 28 days to 31 days (where 1 day = 24h). If that's not ok for your use case, I strongly recommend ditching months and years entirely (so you can just use the Duration class), or at most setting a fixed temporal value for months and years (e.g. 30 days for a month and 365 days for a year).

If you still want to go ahead (for example because your users already expect months and years to mean calendar months and years), you will notice the code is not as straightforward as one would initially think it should be (this also means it's more error-prone; thanks to @Alex for noticing a bug, which has now been fixed), and that is precisely because Java library designers were smart enough not to mix calendar periods (Period class) and exact durations (Duration class) together.


TL;DR

Precondition: start <= end.

// Closest start datetime with same time-of-day as the end datetime.
// This is to make sure to only count full 24h days in the period.
LocalDateTime closestFullDaysStart = LocalDateTime.of(
    start.toLocalDate()
         .plusDays(end.toLocalTime().compareTo(start.toLocalTime()) < 0 ? 1 : 0),
    end.toLocalTime()
);

// Get the calendar period between the dates (full years, months & days).
Period period = Period.between(closestFullDaysStart.toLocalDate(), end.toLocalDate());

// Get the remainder as a duration (hours, minutes, etc.).
Duration duration = Duration.between(start, closestFullDaysStart);

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.

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 2 minutes, much less than 24h. So, we need to start counting the calendar period at the next closest point in time which has the same time of day as the end point, so that any calendar days that we count actually correspond to full 24h durations:

LocalDateTime closestFullDaysStart = LocalDateTime.of(
    start.toLocalDate()
         // if the end time-of-day is earlier than the start time-of-day,
         // the next point in time with that time-of-day is one calendar day ahead
         // (the clock "wraps around" at midnight while advancing to it)
         .plusDays(end.toLocalTime().compareTo(start.toLocalTime()) < 0 ? 1 : 0),
    end.toLocalTime()
);

Now we have effectively split the timespan between start and end into two parts: the span from start to the closestFullDaysStart, which by construction will be less than 24h, so we can measure it with a Duration object with no days part,

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

and the span from closestFullDaysStart and end, which we know we can now reliably* measure with a Period.

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

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.

Negative spans

If start > end, the code above produces an answer which is technically correct if we sum all the units but is presented in an unexpected way.

For example, for

LocalDateTime start = LocalDateTime.of(2020, 1, 1, 1, 00);
LocalDateTime end = LocalDateTime.of(2020, 1, 1, 0, 0);

we get:

0 years, 0 months, -1 days, 23 hours, 0 minutes, 0 seconds

-1 day plus 23 hours is -1 hour, which is correct. But we would expect the answer to be just -1 hour.

Currently durations shown are always positive, and this is because closestFullDaysStart is always in the future with respect to start.

If start > end, however, the direction from start to end is "back in time", and so we need to retrocede from start in order to find the closest datetime with the end time-of-day that lies between start and end. Also, the wrap-around condition changes: since we are winding the clock backwards, we wrap around the clock (and thus need to subtract one calendar day) if the start time-of-day is earlier than the end time.

Combining all of this yields:

int endStartComparison = end.compareTo(start);
int endStartTimeComparison = end.toLocalTime().compareTo(start.toLocalTime());
LocalDateTime closestFullDaysStart = LocalDateTime.of(
    start.toLocalDate()
         .plusDays(
             (
                 (endStartComparison > 0)
                 ? (endStartTimeComparison < 0)
                 : (endStartTimeComparison > 0)
             )
             ? (long) Math.signum(endStartComparison)  // advance one unit in the direction start->end 
             : 0
         ),
    end.toLocalTime()
);

We can rewrite that ugly nested conditional in a more compact but more hacky way:

(endStartComparison * endStartTimeComparison < 0)

Try it online!

Upvotes: 30

excitedWithin
excitedWithin

Reputation: 61

For anyone trying to count the minutes between two LocalTime objects, keep in mind that the result will be minus 1.

For example,

long countMins = MINUTES.between( LocalTime.of(8,00),LocalTime.of(9,59) );
System.out.println("countMins = " + countMins );

will give the Output: countMins = 119 instead of 120

Upvotes: 0

Zon
Zon

Reputation: 19880

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

Upvotes: 130

Tinashe
Tinashe

Reputation: 1151

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

Upvotes: 7

Hennadii Kolomoiets
Hennadii Kolomoiets

Reputation: 149

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.

Upvotes: 5

Kushal
Kushal

Reputation: 8478

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

Upvotes: 1

Junior Batista
Junior Batista

Reputation: 597

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

Upvotes: 58

ChrLipp
ChrLipp

Reputation: 15668

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

Upvotes: 6

Hennadii Kolomoiets
Hennadii Kolomoiets

Reputation: 149

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.

Upvotes: 6

Related Questions