Duration.between vs ChronoUnit.between
Asked Answered
D

3

8

Are the both methods equivalent?

version 1:

var diff = Duration.between(begin, end).toHours();

version 2;

var diff = ChronoUnit.HOURS.between(begin, end);

Are there any implicit differences? If yes, which one should I prefer?

Doubleripper answered 17/2, 2021 at 18:31 Comment(5)
Very interesting question :) Reading the javadocs, nothing seems to say there is a differenceKaylenekayley
Doesn't the first one return a Duration and the second one a long (the amount of time between temporal1Inclusive and temporal2Exclusive in terms of this unit; positive if temporal2Exclusive is later than temporal1Inclusive, negative if earlier)?Commend
for the love of God, I hope that there is no difference between the two, specifically about hours.Biron
@Biron cried laughing with the commentNurmi
@Boug yeah... the question is awesome, imo. But I surely hope the answer is as straight forward as it should : NO, there isn't. I have been looking at the documentation and implementation and found no meaningful difference.Biron
N
6

Analyzed implementation on open JDK 15.


A) Using Duration.between(begin, end).toHours()

Duration.between(begin, end) first calls

// called with unit of NANOS or SECONDS if first one fails
long until(Temporal endExclusive, TemporalUnit unit);

and then parses the difference to create a Duration based on nanos that have been calculated (or seconds if calculation of nanos failed):

public static Duration between(Temporal startInclusive, Temporal endExclusive) {
    try {
        return ofNanos(startInclusive.until(endExclusive, NANOS));
    } catch (DateTimeException | ArithmeticException ex) {
        long secs = startInclusive.until(endExclusive, SECONDS);
        long nanos;
        try {
            nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND);
            if (secs > 0 && nanos < 0) {
                secs++;
            } else if (secs < 0 && nanos > 0) {
                secs--;
            }
        } catch (DateTimeException ex2) {
            nanos = 0;
        }

        return ofSeconds(secs, nanos);
    }

Then you have to call toHours() which then parses the created Duration object to return the hours as long.

B) Using ChronoUnit.HOURS.between(begin, end)

Directly calls

// in this case it is called with unit of HOURS directly
long until(Temporal endExclusive, TemporalUnit unit);

which directly returns the hours as long.


Comparison of implementations

  • Both work the same way (for Hours at least) and give the same result.
  • But for A) it seems we have some back and forth conversions which are not necessary.
  • Solution B) seems straightforward without any conversions that we don't need.

Answer

I would chose the solution B) as more efficient.

Nurmi answered 17/2, 2021 at 19:9 Comment(3)
which part of the question was not clear, though? Duration.between(begin, end).toHours() returns a long; and ChronoUnit.HOURS.between(begin, end) returns a long. This is what the OP asked, as such your answer does not answer anythingBiron
@Biron updated previous answer to bring some insight to the questionNurmi
Interesting use of the word "parses".Peterson
K
1

It seems that both methods finally call Temporal#until

var diff = Duration.between(begin, end).toHours();

Duration#between
\
 Temporal#until (used twice but some branching go for another implementation)

var diff = ChronoUnit.HOURS.between(begin, end);

ChronoUnit.HOURS#between
\
 Temporal#until (it is the only method underlying)
Kaylenekayley answered 17/2, 2021 at 18:47 Comment(1)
That's a bit vague. I looked in the source and it seems that version 1 does a lot of stuff in compare to version 2.Doubleripper
A
1

Beware: ChronoUnit.DAYS.between(...) and Duration.between(...).toDays() work differently!

... when you use timezones that have to respect daylight saving time:

  • Calculation of hours seems to be identical and correct
  • Calculation of days does not work correctly with Duration
    • ChronoUnits retuns the accurate amount of days
    • Duration is using its hour-calculation and calculates a wrong amount of days in the case of spring-forward DST (with a 'missing' hour)

Here is a simple Spock-Test:

  def "different behaviour of duration calculation in respect to DST"() {
        when: "Interval with spring-forward DST between (March 2024)"

        // Start of month
        def startLocalTime = LocalDateTime.of(2024, month.value, 1, 0, 0);
        def start = ZonedDateTime.of(startLocalTime, ZoneId.of("Europe/Berlin"))

        // end of month (exclusive)
        def endLocalTime = LocalDateTime.of(2024, month.value + 1, 1, 0, 0);
        def end = ZonedDateTime.of(endLocalTime, ZoneId.of("Europe/Berlin"))

        then: "creation of that interval was successful"

        ChronoUnit.HOURS.between(start, end) == accurateHours
        Duration.between(start, end).toHours() == accurateHours

        ChronoUnit.DAYS.between(start, end) == accurateDays // ChronoUnits calculation DAYS-duration with respect to DST
        Duration.between(start, end).toDays() == accurateDays + dayDifference // Duraction calculates DAYS-duraction based on HOUR-duration

        where: "three months"

        month         || accurateDays | dayDifference | accurateHours
        Month.MARCH   || 31           | -1            | 31 * 24 - 1 // DST (spring-forward)
        Month.JULY    || 31           | 0             | 31 * 24
        Month.OCTOBER || 31           | 0             | 31 * 24 + 1 // DST (fall-back)
    }
Acroterion answered 21/8 at 8:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.