How to convert from long+TimeUnit to Duration?

2.9k views Asked by At

I'm migrating some signatures from f(long dur, TimeUnit timeUnit) to f(Duration duration), and would like to implement the former with the latter.

Being on Java 8, I can't find any API to easily convert the long+TU to a Duration, the only idea that comes to me is to do something ugly like:

static Duration convert(long dur, TimeUnit timeUnit) {
  switch (timeUnit) {
    case DAYS:
      return Duration.ofDays(dur);
    case HOURS:
      /* alternative, but (again) I don't have an easy conversion from TimeUnit -> ChronoUnit */
      return Duration.of(dur, ChronoUnit.HOURS);
    case ..... /* and so on */
  }
}

Or did I miss some API?

3

There are 3 answers

5
Adam Kotwasinski On BEST ANSWER

For Java 8 there is a roundabout solution :\ (we unnecessarily convert from our TU -> millis (or whatever unit) -> Duration) like this:

long dur = ...
TimeUnit unit = ...
Duration result = Duration.ofMillis(unit.toMillis(dur));

Caveat emptor with extremely large values though, Long.MAX_VALUE days cannot be correctly converted into long millis (compared to Duration's ctor that does throw when trying to init with such a value):

final long millis = TimeUnit.DAYS.toMillis(Long.MAX_VALUE); // ouch
final Duration dur = Duration.ofMillis(dur);
System.err.println(dur.toDays() == Long.MAX_VALUE); // returns 'false'
4
Alexander Ivanchenko On

Java 9+

You can use static method Duration.of(long, TemporalUnit).

It expects an amount as long, and a TemporalUnit, so you need to convert the TimeUnit into ChronoUnit.

static Duration convert(long dur, TimeUnit timeUnit) {
    return Duration.of(dur, timeUnit.toChronoUnit());
}

Method toChronoUnit() was introduced in JDK version 9.

Java 8

With Java 8 you can translate TimeUnit into ChronoUnit using ThreeTen library's utility method Temporals.chronoUnit​(TimeUnit).

If you don't want to introduce a dependency on this library in your project, you can make use of the utility method provided in the answer by Paul.

1
Paul On

You're on the right track. Since you think the right track is ugly the solution is to hide the ugliness! Here's the implementation of TimeUnit.toChronoUnit() from OpenJDK:

/**
 * Converts this {@code TimeUnit} to the equivalent {@code ChronoUnit}.
 *
 * @return the converted equivalent ChronoUnit
 * @since 9
 */
public ChronoUnit toChronoUnit() {
    switch (this) {
    case NANOSECONDS:  return ChronoUnit.NANOS;
    case MICROSECONDS: return ChronoUnit.MICROS;
    case MILLISECONDS: return ChronoUnit.MILLIS;
    case SECONDS:      return ChronoUnit.SECONDS;
    case MINUTES:      return ChronoUnit.MINUTES;
    case HOURS:        return ChronoUnit.HOURS;
    case DAYS:         return ChronoUnit.DAYS;
    default: throw new AssertionError();
    }
}

To keep your code cleaner I would implement a static utility method based on above (i.e. pass in a TimeUnit parameter) and get the ChronoUnit without doing a conversion in each case. You'll end up with 1 call to Duration.of(long amount, TemporalUnit unit) and your code will be as beautiful as if you were using Java 9+!