411

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.

13
  • Just a guess, but might Period.between() apply some rounding? Commented Sep 9, 2014 at 14:48
  • 3
    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. Commented Sep 9, 2014 at 15:30
  • 1
    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. Commented Sep 9, 2014 at 15:34
  • 10
    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. Commented Sep 9, 2014 at 19:39
  • 2
    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. Commented Nov 24, 2017 at 17:54

12 Answers 12

736

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

Sign up to request clarification or add additional context in comments.

10 Comments

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.
|
237

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

11 Comments

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.
|
130

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

2 Comments

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"
58

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

2 Comments

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.
30

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!

2 Comments

I like your answer, but it has a bug. In case when Period.between() returns 1, 2 etc. months without any days, the subsequent call to period.minusDay() will subtract 1 day from 0 days, relulting in period having -1 days. The months do not translate to days, because the period object does not have enough context to do this correctly (is 1 month = 30 days, or 31?). Thus, subtracting should happen before measuring period. Fo example, like this: Period period = Period.between(start.toLocalDate().plusDays( (end.toLocalTime() >= start.toLocalTime()) ? 0L : 1L), end.toLocalDate())
@AlexSemeniuk Thank you for noticing the bug and suggesting a solution! I have now fixed it; I used your idea but wrote it slightly differently, I think it also helped clarify/simplify the solution overall. PS: when I wrote the .minusDays code, it felt so hacky that I felt it should go wrong somewhere, and it indeed does, I guess I didn't test thoroughly at all!
7

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

3 Comments

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
6

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.

3 Comments

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).
6

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 

Comments

5

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.

1 Comment

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.
2

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.

Comments

1

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 

1 Comment

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.
0

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

4 Comments

Thanks for the heads-up. This is because between measures from start time inclusive to end time exclusive. If you want 120 minutes, use 8:00 and 10:00.
Yes, exactly. Thank you for the reasoning friend. And In case that the values are given on run-time by the user and you can't control them, just add 1 after they provide their input e.g. countMins+=1;
What is the exact import for MINUTES here? I cant seem to find it in LocalTime/LocalDateTime classes so not sure where its from
@cowley05 import static java.time.temporal.ChronoUnit.MINUTES;

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.