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     * @see StopWatch
054     */
055    @SuppressWarnings("boxing") // boxing unavoidable
056    public static <T extends Throwable> void accept(final FailableBiConsumer<Long, Integer, T> consumer, final Duration duration)
057            throws T {
058        if (consumer != null && duration != null) {
059            consumer.accept(duration.toMillis(), getNanosOfMilli(duration));
060        }
061    }
062
063    /**
064     * Gets the nanosecond part of a Duration converted to milliseconds.
065     * <p>
066     * Handy when calling an API that takes a long of milliseconds and an int of nanoseconds. For example,
067     * {@link Object#wait(long, int)} and {@link Thread#sleep(long, int)}.
068     * </p>
069     * <p>
070     * Note that is this different from {@link Duration#getNano()} because a duration are seconds and nanoseconds.
071     * </p>
072     *
073     * @param duration The duration to query.
074     * @return nanoseconds between 0 and 999,999.
075     * @deprecated Use {@link #getNanosOfMilli(Duration)}.
076     */
077    @Deprecated
078    public static int getNanosOfMiili(final Duration duration) {
079        return getNanosOfMilli(duration);
080    }
081
082    /**
083     * Gets the nanosecond part of a Duration converted to milliseconds.
084     * <p>
085     * Handy when calling an API that takes a long of milliseconds and an int of nanoseconds. For example,
086     * {@link Object#wait(long, int)} and {@link Thread#sleep(long, int)}.
087     * </p>
088     * <p>
089     * Note that is this different from {@link Duration#getNano()} because a duration are seconds and nanoseconds.
090     * </p>
091     *
092     * @param duration The duration to query.
093     * @return nanoseconds between 0 and 999,999.
094     * @since 3.13.0
095     */
096    public static int getNanosOfMilli(final Duration duration) {
097        return zeroIfNull(duration).getNano() % 1_000_000;
098    }
099
100    /**
101     * Tests whether the given Duration is positive (&gt;0).
102     *
103     * @param duration the value to test
104     * @return whether the given Duration is positive (&gt;0).
105     */
106    public static boolean isPositive(final Duration duration) {
107        return !duration.isNegative() && !duration.isZero();
108    }
109
110    private static <E extends Throwable> Instant now(final FailableConsumer<Instant, E> nowConsumer) throws E {
111        final Instant start = Instant.now();
112        nowConsumer.accept(start);
113        return start;
114    }
115
116    /**
117     * Runs the lambda and returns the duration of its execution.
118     *
119     * @param <E> The type of exception throw by the lambda.
120     * @param consumer What to execute.
121     * @return The Duration of execution.
122     * @throws E thrown by the lambda.
123     * @see StopWatch
124     * @since 3.13.0
125     */
126    public static <E extends Throwable> Duration of(final FailableConsumer<Instant, E> consumer) throws E {
127        return since(now(consumer::accept));
128    }
129
130    /**
131     * Runs the lambda and returns the duration of its execution.
132     *
133     * @param <E> The type of exception throw by the lambda.
134     * @param runnable What to execute.
135     * @return The Duration of execution.
136     * @throws E thrown by the lambda.
137     * @see StopWatch
138     * @since 3.13.0
139     */
140    public static <E extends Throwable> Duration of(final FailableRunnable<E> runnable) throws E {
141        return of(start -> runnable.run());
142    }
143
144    /**
145     * Computes the Duration between a start instant and now.
146     *
147     * @param startInclusive the start instant, inclusive, not null.
148     * @return a {@link Duration}, not null.
149     * @since 3.13.0
150     */
151    public static Duration since(final Temporal startInclusive) {
152        return Duration.between(startInclusive, Instant.now());
153    }
154
155    /**
156     * Converts a {@link TimeUnit} to a {@link ChronoUnit}.
157     *
158     * @param timeUnit A non-null TimeUnit.
159     * @return The corresponding ChronoUnit.
160     */
161    static ChronoUnit toChronoUnit(final TimeUnit timeUnit) {
162        // TODO when using Java >= 9: Use TimeUnit.toChronoUnit().
163        switch (Objects.requireNonNull(timeUnit)) {
164        case NANOSECONDS:
165            return ChronoUnit.NANOS;
166        case MICROSECONDS:
167            return ChronoUnit.MICROS;
168        case MILLISECONDS:
169            return ChronoUnit.MILLIS;
170        case SECONDS:
171            return ChronoUnit.SECONDS;
172        case MINUTES:
173            return ChronoUnit.MINUTES;
174        case HOURS:
175            return ChronoUnit.HOURS;
176        case DAYS:
177            return ChronoUnit.DAYS;
178        default:
179            throw new IllegalArgumentException(timeUnit.toString());
180        }
181    }
182
183    /**
184     * Converts an amount and TimeUnit into a Duration.
185     *
186     * @param amount   the amount of the duration, measured in terms of the unit, positive or negative
187     * @param timeUnit the unit that the duration is measured in, must have an exact duration, not null
188     * @return a Duration.
189     */
190    public static Duration toDuration(final long amount, final TimeUnit timeUnit) {
191        return Duration.of(amount, toChronoUnit(timeUnit));
192    }
193
194    /**
195     * Converts a Duration to milliseconds bound to an int (instead of a long).
196     * <p>
197     * Handy for low-level APIs that take millisecond timeouts in ints rather than longs.
198     * </p>
199     * <ul>
200     * <li>If the duration milliseconds are greater than {@link Integer#MAX_VALUE}, then return
201     * {@link Integer#MAX_VALUE}.</li>
202     * <li>If the duration milliseconds are lesser than {@link Integer#MIN_VALUE}, then return
203     * {@link Integer#MIN_VALUE}.</li>
204     * </ul>
205     *
206     * @param duration The duration to convert, not null.
207     * @return int milliseconds.
208     */
209    public static int toMillisInt(final Duration duration) {
210        Objects.requireNonNull(duration, "duration");
211        // intValue() does not do a narrowing conversion here
212        return LONG_TO_INT_RANGE.fit(Long.valueOf(duration.toMillis())).intValue();
213    }
214
215    /**
216     * Returns the given non-null value or {@link Duration#ZERO} if null.
217     *
218     * @param duration The duration to test.
219     * @return The given duration or {@link Duration#ZERO}.
220     */
221    public static Duration zeroIfNull(final Duration duration) {
222        return ObjectUtils.defaultIfNull(duration, Duration.ZERO);
223    }
224
225    /**
226     * Make private in 4.0.
227     *
228     * @deprecated TODO Make private in 4.0.
229     */
230    @Deprecated
231    public DurationUtils() {
232        // empty
233    }
234}