Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a equivalent to ChronoUnit.between that returns fraction instead of integer?

Methods such is ChronoUnit.HOURS.between(start, end) returns long so I can't get the fraction from there.

Is there an alternative method/approach that would return a fraction?

like image 952
jellyfication Avatar asked Jun 13 '16 09:06

jellyfication


2 Answers

The whole point of ChronoUnit.HOURS.between(start, end) is to get the number of HOURS between the two timepoints. For example: it's either 1 or 2 hours between, there is no such thing as 1.5658 hours*. If you need more precicion, use another ChronoUnit, i.e. Minutes or seconds.

The problem with decimal fractions is, that they are usually based on 10, while time units are fractions based on a circle (360°, 2pi, etc) which are better expressed in fractions of integers like 1/4, 1/8, 1/2, etc. and not in a floating point value. The keyword here is "floating point", you could express a value by moving the point or multiply a base-value with powers of 10 and use a prefix to the unit (i.e. 1.5 * 10^3 == 1500000 * 10^-3, where 10^3 is kilo and 10^-3 is milli), but you can't do that with days, hours, minutes and seconds. There is no such thing as kilo-hour or milli-days and you can't simply translate hours to minutes by multipling a power of 10*.

In real life nobody would say "it's 1 hour and 8.3%" or "6.5 hours later" - as this usually ends up in confusion (6.30 vs 6:30) - so it's more practical to say "it's 1hr and 5mins" or "5 past x" or "a quarter to x".

So there is no real practical use for having a decimal fraction calculation in a time API*. But in case you need it (i.e. for computational purposes), you may quite simply calculate it.

If you require a fraction - i.e. minutes or seconds - you have to use the corresponding ChronoUnit, i.e. ChronoUnit.MINUTES or ChronoUnit.SECONDS, and devide it by the amount of time units required for the whole you'd like to have a fraction of. For example

  • 60 minutes make an hour
  • 3600 seconds make an hour
  • 3600000 ms make an hour, etc.
  • 60 seconds make a minute
  • ...

You may calculate the fraction with minute-precision using simple math i.e.

double fracH = (double)ChronoUnit.MINUTES.between(start,end) / 60;

As alternative you could use Duration which offers a between method, too. A duration can be easily converted into Minutes, Hours etc.

Duration dur = Duration.between(start, end);
double fracH = (double)dur.toMinutes() / 60;

To get the highest precision, you have to calculate the fraction on the smallest timeunit, i.e. nanos or millis

double fracHns = (double)dur.toNanos() / 3_600_000_000_000;

You may even use Duration to get the base (the 100%) value:

//fraction of an hour
double fracHns = (double)dur.toNanos() / Duration.ofHours(1).toNanos();

//fraction of a minute
double fracMns = (double)dur.toNanos() / Duration.ofMinutes(1).toNanos();

Or more general, as David SN posted

TemporalUnit unit = ChronoUnit.HOURS;
double frac = (double)Duration.between(start, end).toNanos() 
              / Duration.of(1, unit).toNanos();

*) No rule without exception: To make things difficult, for time units the calculation base changes for units smaller than 1 second, because then it gets 10-based, i.e. milli-seconds (1/1000th), microseconds (1/1000000th),... So for seconds and smaller it is common to use decimal fractions, for example: 1.74 second, because it is simple to translate it to the next-smaller time unit by simply moving the decimal separator:

1.74 s <-> 1740.0 ms. 

But it's not that easy for hours:

1.74 hours <-> 104.40 minutes.
like image 161
Gerald Mücke Avatar answered Sep 23 '22 08:09

Gerald Mücke


There is no methods in ChronoUnit to get the decimal value of the time difference in a given time unit.

You could create a utility method to implement this functionality. The class Duration could be used for this purpose. This could be done getting the difference in the smallest unit and converting it back (with decimals) to the required unit:

public static double between(Temporal startInclusive, Temporal endExclusive, ChronoUnit unit) {
    Duration duration = Duration.between(startInclusive, endExclusive);
    long conversion = Duration.of(1, unit).toNanos();
    return (double) duration.toNanos() / conversion;
}
like image 34
David SN Avatar answered Sep 22 '22 08:09

David SN