Home > Back-end >  How can I get the Duration between two dates / times in YMDHMS (ISO 8601)
How can I get the Duration between two dates / times in YMDHMS (ISO 8601)

Time:05-21

Given 2 dates, how can I calculate the difference between them in Year Month Day, Hour Minute Second format, as per ISO 8601 Durations?

I've only found Java libraries that can give the difference in days or smaller.

Given that months and years have irregular numbers of days, I'm not sure how to figure out the difference in months and years.

Even Duration.between is close but it gives the result in hours minutes seconds:

ZonedDateTime event1 = ZonedDateTime.of(2022, 2, 2, 2, 2, 2, 0, ZoneId.of("UTC-7"));
ZonedDateTime event2 = ZonedDateTime.of(2025, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC-7"));
//ZonedDateTime now = ZonedDateTime.now(ZoneId.of("UTC-7"));
Duration duration = Duration.between(event1, event2);
Log.d("Duration ISO-8601: ", duration.toString());


Output:    PT25533H57M58S

which is 25533 hours, 57 minutes, 58 seconds

I'd like to see something like:

__ years, __ months, __ days, 9 hours, 57 minutes, 58 seconds

CodePudding user response:

I believe I have solved it:

ZonedDateTime event1 = ZonedDateTime.of(2022, 2, 2, 2, 2, 2, 0, ZoneId.of("UTC-7"));
ZonedDateTime event2 = ZonedDateTime.of(2025, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC-7"));
Duration duration = Duration.between(event1, event2);
Period period = Period.between(event1.toLocalDate(), event2.toLocalDate());
Log.d("Duration ISO-8601: ", period.toString()   duration.toString());

This prints:

P2Y10M30DPT25533H57M58S

Which is 2 years, 10 months, 30 days, 25533 hours, 57 minutes, 58 seconds.

When extracting the individual values (which is what I need), using modulus fixes the problem with hours:

 String.valueOf(duration.toHours() % 24)

And if there's any problem with minutes and seconds, % 60 will fix it.

CodePudding user response:

You can create a custom class that will maintain Period and Duration.

class DateTimeSlot {
    private Period period;
    private Duration duration;
    
    private DateTimeSlot(Period period, Duration duration) {
        this.period = period;
        this.duration = duration;
    }
    
    public static DateTimeSlot of(LocalDateTime from, LocalDateTime to) {
        if (from.isAfter(to) || from.equals(to)) {
            throw new IllegalArgumentException();
        }
    
        Duration duration;
        Period period;
        
        if (from.toLocalTime().isBefore(to.toLocalTime())) {
            duration = Duration.between(from.toLocalTime(), to.toLocalTime());
            period = Period.between(from.toLocalDate(), to.toLocalDate());
        } else {
            duration = Duration.between(to.withHour(from.getHour())
                .withMinute(from.getMinute())
                .minusDays(1), to);
            period = Period.between(from.toLocalDate(), to.toLocalDate());
        }
        return new DateTimeSlot(period, duration);
    }
    
    @Override
    public String toString() {
        return "DateTimeSlot{"   period   duration  
            '}';
    }
}

The toString() implementation above is very primitive, its purpose only to demonstrate the idea.

main() - demo

public static void main(String[] args) {
    LocalDateTime now = LocalDateTime.of(2022, 5, 20, 18, 0);
    LocalDateTime withTimeBefore = LocalDateTime.of(2020, 12, 31, 15,9);
    LocalDateTime withTimeAfter = LocalDateTime.of(2020, 12, 31, 22,50);
    
    DateTimeSlot slot1 = DateTimeSlot.of(withTimeBefore, now);
    DateTimeSlot slot2 = DateTimeSlot.of(withTimeAfter, now);

    System.out.println(slot1);
    System.out.println(slot2);
}

Output

DateTimeSlot{P1Y4M20DPT2H51M}
DateTimeSlot{P1Y4M20DPT19H10M}
  • Related