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    private static <E extends Throwable> Instant now(final FailableConsumer<Instant, E> nowConsumer) throws E {
110        final Instant start = Instant.now();
111        nowConsumer.accept(start);
112        return start;
113    }
114
115    /**
116     * Runs the lambda and returns the duration of its execution.
117     *
118     * @param <E> The type of exception throw by the lambda.
119     * @param consumer What to execute.
120     * @return The Duration of execution.
121     * @throws E thrown by the lambda.
122     * @since 3.13.0
123     */
124    public static <E extends Throwable> Duration of(final FailableConsumer<Instant, E> consumer) throws E {
125        return since(now(consumer::accept));
126    }
127
128    /**
129     * Runs the lambda and returns the duration of its execution.
130     *
131     * @param <E> The type of exception throw by the lambda.
132     * @param runnable What to execute.
133     * @return The Duration of execution.
134     * @throws E thrown by the lambda.
135     * @since 3.13.0
136     */
137    public static <E extends Throwable> Duration of(final FailableRunnable<E> runnable) throws E {
138        return of(start -> runnable.run());
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}