    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.lang3.time;
    019
    020import java.time.Duration;
    021import java.time.Instant;
    022import java.time.temporal.ChronoUnit;
    023import java.time.temporal.Temporal;
    024import java.util.Objects;
    025import java.util.concurrent.TimeUnit;
    026
    027import org.apache.commons.lang3.LongRange;
    028import org.apache.commons.lang3.ObjectUtils;
    029import org.apache.commons.lang3.function.FailableBiConsumer;
    030import org.apache.commons.lang3.function.FailableConsumer;
    031import org.apache.commons.lang3.function.FailableRunnable;
    032import org.apache.commons.lang3.math.NumberUtils;
    033
    034/**
    035 * Utilities for {@link Duration}.
    036 *
    037 * @since 3.12.0
    038 */
    039public class DurationUtils {
    040
    041    /**
    042     * An Integer Range that accepts Longs.
    043     */
    044    static final LongRange LONG_TO_INT_RANGE = LongRange.of(NumberUtils.LONG_INT_MIN_VALUE, NumberUtils.LONG_INT_MAX_VALUE);
    045
    046    /**
    047     * Accepts the function with the duration as a long milliseconds and int nanoseconds.
    048     *
    049     * @param <T> The function exception.
    050     * @param consumer Accepting function.
    051     * @param duration The duration to pick apart.
    052     * @throws T See the function signature.
    053     */
    054    @SuppressWarnings("boxing") // boxing unavoidable
    055    public static <T extends Throwable> void accept(final FailableBiConsumer<Long, Integer, T> consumer, final Duration duration)
    056            throws T {
    057        if (consumer != null && duration != null) {
    058            consumer.accept(duration.toMillis(), getNanosOfMilli(duration));
    059        }
    060    }
    061
    062    /**
    063     * Gets the nanosecond part of a Duration converted to milliseconds.
    064     * <p>
    065     * Handy when calling an API that takes a long of milliseconds and an int of nanoseconds. For example,
    066     * {@link Object#wait(long, int)} and {@link Thread#sleep(long, int)}.
    067     * </p>
    068     * <p>
    069     * Note that is this different from {@link Duration#getNano()} because a duration are seconds and nanoseconds.
    070     * </p>
    071     *
    072     * @param duration The duration to query.
    073     * @return nanoseconds between 0 and 999,999.
    074     * @deprecated Use {@link #getNanosOfMilli(Duration)}.
    075     */
    076    @Deprecated
    077    public static int getNanosOfMiili(final Duration duration) {
    078        return getNanosOfMilli(duration);
    079    }
    080
    081    /**
    082     * Gets the nanosecond part of a Duration converted to milliseconds.
    083     * <p>
    084     * Handy when calling an API that takes a long of milliseconds and an int of nanoseconds. For example,
    085     * {@link Object#wait(long, int)} and {@link Thread#sleep(long, int)}.
    086     * </p>
    087     * <p>
    088     * Note that is this different from {@link Duration#getNano()} because a duration are seconds and nanoseconds.
    089     * </p>
    090     *
    091     * @param duration The duration to query.
    092     * @return nanoseconds between 0 and 999,999.
    093     * @since 3.13.0
    094     */
    095    public static int getNanosOfMilli(final Duration duration) {
    096        return zeroIfNull(duration).getNano() % 1_000_000;
    097    }
    098
    099    /**
    100     * Tests whether the given Duration is positive (&gt;0).
    101     *
    102     * @param duration the value to test
    103     * @return whether the given Duration is positive (&gt;0).
    104     */
    105    public static boolean isPositive(final Duration duration) {
    106        return !duration.isNegative() && !duration.isZero();
    107    }
    108
    109    /**
    110     * Runs the lambda and returns the duration of its execution.
    111     *
    112     * @param <E> The type of exception throw by the lambda.
    113     * @param consumer What to execute.
    114     * @return The Duration of execution.
    115     * @throws E thrown by the lambda.
    116     * @since 3.13.0
    117     */
    118    public static <E extends Throwable> Duration of(final FailableConsumer<Instant, E> consumer) throws E {
    119        return since(now(consumer::accept));
    120    }
    121
    122    /**
    123     * Runs the lambda and returns the duration of its execution.
    124     *
    125     * @param <E> The type of exception throw by the lambda.
    126     * @param runnable What to execute.
    127     * @return The Duration of execution.
    128     * @throws E thrown by the lambda.
    129     * @since 3.13.0
    130     */
    131    public static <E extends Throwable> Duration of(final FailableRunnable<E> runnable) throws E {
    132        return of(start -> runnable.run());
    133    }
    134
    135    private static <E extends Throwable> Instant now(final FailableConsumer<Instant, E> nowConsumer) throws E {
    136        final Instant start = Instant.now();
    137        nowConsumer.accept(start);
    138        return start;
    139    }
    140
    141    /**
    142     * Computes the Duration between a start instant and now.
    143     *
    144     * @param startInclusive the start instant, inclusive, not null.
    145     * @return a {@link Duration}, not null.
    146     * @since 3.13.0
    147     */
    148    public static Duration since(final Temporal startInclusive) {
    149        return Duration.between(startInclusive, Instant.now());
    150    }
    151
    152    /**
    153     * Converts a {@link TimeUnit} to a {@link ChronoUnit}.
    154     *
    155     * @param timeUnit A non-null TimeUnit.
    156     * @return The corresponding ChronoUnit.
    157     */
    158    static ChronoUnit toChronoUnit(final TimeUnit timeUnit) {
    159        // TODO when using Java >= 9: Use TimeUnit.toChronoUnit().
    160        switch (Objects.requireNonNull(timeUnit)) {
    161        case NANOSECONDS:
    162            return ChronoUnit.NANOS;
    163        case MICROSECONDS:
    164            return ChronoUnit.MICROS;
    165        case MILLISECONDS:
    166            return ChronoUnit.MILLIS;
    167        case SECONDS:
    168            return ChronoUnit.SECONDS;
    169        case MINUTES:
    170            return ChronoUnit.MINUTES;
    171        case HOURS:
    172            return ChronoUnit.HOURS;
    173        case DAYS:
    174            return ChronoUnit.DAYS;
    175        default:
    176            throw new IllegalArgumentException(timeUnit.toString());
    177        }
    178    }
    179
    180    /**
    181     * Converts an amount and TimeUnit into a Duration.
    182     *
    183     * @param amount   the amount of the duration, measured in terms of the unit, positive or negative
    184     * @param timeUnit the unit that the duration is measured in, must have an exact duration, not null
    185     * @return a Duration.
    186     */
    187    public static Duration toDuration(final long amount, final TimeUnit timeUnit) {
    188        return Duration.of(amount, toChronoUnit(timeUnit));
    189    }
    190
    191    /**
    192     * Converts a Duration to milliseconds bound to an int (instead of a long).
    193     * <p>
    194     * Handy for low-level APIs that take millisecond timeouts in ints rather than longs.
    195     * </p>
    196     * <ul>
    197     * <li>If the duration milliseconds are greater than {@link Integer#MAX_VALUE}, then return
    198     * {@link Integer#MAX_VALUE}.</li>
    199     * <li>If the duration milliseconds are lesser than {@link Integer#MIN_VALUE}, then return
    200     * {@link Integer#MIN_VALUE}.</li>
    201     * </ul>
    202     *
    203     * @param duration The duration to convert, not null.
    204     * @return int milliseconds.
    205     */
    206    public static int toMillisInt(final Duration duration) {
    207        Objects.requireNonNull(duration, "duration");
    208        // intValue() does not do a narrowing conversion here
    209        return LONG_TO_INT_RANGE.fit(Long.valueOf(duration.toMillis())).intValue();
    210    }
    211
    212    /**
    213     * Returns the given non-null value or {@link Duration#ZERO} if null.
    214     *
    215     * @param duration The duration to test.
    216     * @return The given duration or {@link Duration#ZERO}.
    217     */
    218    public static Duration zeroIfNull(final Duration duration) {
    219        return ObjectUtils.defaultIfNull(duration, Duration.ZERO);
    220    }
    221
    222}