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 *      https://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.time.temporal.TemporalUnit;
025import java.util.Objects;
026import java.util.concurrent.TimeUnit;
027
028import org.apache.commons.lang3.LongRange;
029import org.apache.commons.lang3.ObjectUtils;
030import org.apache.commons.lang3.StringUtils;
031import org.apache.commons.lang3.function.FailableBiConsumer;
032import org.apache.commons.lang3.function.FailableConsumer;
033import org.apache.commons.lang3.function.FailableRunnable;
034import org.apache.commons.lang3.math.NumberUtils;
035
036/**
037 * Utilities for {@link Duration}.
038 *
039 * @since 3.12.0
040 */
041public class DurationUtils {
042
043    /**
044     * An Integer Range that accepts Longs.
045     */
046    static final LongRange LONG_TO_INT_RANGE = LongRange.of(NumberUtils.LONG_INT_MIN_VALUE, NumberUtils.LONG_INT_MAX_VALUE);
047
048    /**
049     * Accepts the function with the duration as a long milliseconds and int nanoseconds.
050     *
051     * @param <T> The function exception.
052     * @param consumer Accepting function.
053     * @param duration The duration to pick apart.
054     * @throws T See the function signature.
055     * @see StopWatch
056     */
057    @SuppressWarnings("boxing") // boxing unavoidable
058    public static <T extends Throwable> void accept(final FailableBiConsumer<Long, Integer, T> consumer, final Duration duration)
059            throws T {
060        if (consumer != null && duration != null) {
061            consumer.accept(duration.toMillis(), getNanosOfMilli(duration));
062        }
063    }
064
065    /**
066     * Gets a Duration of unit stored in the system property at the given key.
067     *
068     * @param key  The property name.
069     * @param unit The unit that the duration is measured in, not null.
070     * @param def  The default value in the given unit.
071     * @return a Duration of seconds.
072     * @since 3.19.0
073     */
074    public static Duration get(final String key, final TemporalUnit unit, final long def) {
075        return Duration.of(getLong(key, def), unit);
076    }
077
078    private static long getLong(final String key, final long def) {
079        return StringUtils.isEmpty(key) ? def : Long.getLong(key, def);
080    }
081
082    /**
083     * Gets a Duration of milliseconds stored in the system property at the given key.
084     *
085     * @param key The property name.
086     * @param def The default value in milliseconds.
087     * @return a Duration of milliseconds.
088     * @since 3.19.0
089     */
090    public static Duration getMillis(final String key, final long def) {
091        return Duration.ofMillis(getLong(key, def));
092    }
093
094    /**
095     * Gets the nanosecond part of a Duration converted to milliseconds.
096     * <p>
097     * Handy when calling an API that takes a long of milliseconds and an int of nanoseconds. For example,
098     * {@link Object#wait(long, int)} and {@link Thread#sleep(long, int)}.
099     * </p>
100     * <p>
101     * Note that is this different from {@link Duration#getNano()} because a duration are seconds and nanoseconds.
102     * </p>
103     *
104     * @param duration The duration to query.
105     * @return nanoseconds between 0 and 999,999.
106     * @deprecated Use {@link #getNanosOfMilli(Duration)}.
107     */
108    @Deprecated
109    public static int getNanosOfMiili(final Duration duration) {
110        return getNanosOfMilli(duration);
111    }
112
113    /**
114     * Gets the nanosecond part of a Duration converted to milliseconds.
115     * <p>
116     * Handy when calling an API that takes a long of milliseconds and an int of nanoseconds. For example,
117     * {@link Object#wait(long, int)} and {@link Thread#sleep(long, int)}.
118     * </p>
119     * <p>
120     * Note that is this different from {@link Duration#getNano()} because a duration are seconds and nanoseconds.
121     * </p>
122     *
123     * @param duration The duration to query.
124     * @return nanoseconds between 0 and 999,999.
125     * @since 3.13.0
126     */
127    public static int getNanosOfMilli(final Duration duration) {
128        return zeroIfNull(duration).getNano() % 1_000_000;
129    }
130
131    /**
132     * Gets a Duration of seconds stored in the system property at the given key.
133     *
134     * @param key The property name.
135     * @param def The default value in seconds.
136     * @return a Duration of seconds.
137     * @since 3.19.0
138     */
139    public static Duration getSeconds(final String key, final long def) {
140        return Duration.ofSeconds(getLong(key, def));
141    }
142
143    /**
144     * Tests whether the given Duration is positive (duration &gt; 0).
145     *
146     * @param duration the value to test
147     * @return whether the given Duration is positive (duration &gt; 0).
148     */
149    public static boolean isPositive(final Duration duration) {
150        return !duration.isNegative() && !duration.isZero();
151    }
152
153    private static <E extends Throwable> Instant now(final FailableConsumer<Instant, E> nowConsumer) throws E {
154        final Instant start = Instant.now();
155        nowConsumer.accept(start);
156        return start;
157    }
158
159    /**
160     * Runs the lambda and returns the duration of its execution.
161     *
162     * @param <E> The type of exception throw by the lambda.
163     * @param consumer What to execute.
164     * @return The Duration of execution.
165     * @throws E thrown by the lambda.
166     * @see StopWatch
167     * @since 3.13.0
168     */
169    public static <E extends Throwable> Duration of(final FailableConsumer<Instant, E> consumer) throws E {
170        return since(now(consumer::accept));
171    }
172
173    /**
174     * Runs the lambda and returns the duration of its execution.
175     *
176     * @param <E> The type of exception throw by the lambda.
177     * @param runnable What to execute.
178     * @return The Duration of execution.
179     * @throws E thrown by the lambda.
180     * @see StopWatch
181     * @since 3.13.0
182     */
183    public static <E extends Throwable> Duration of(final FailableRunnable<E> runnable) throws E {
184        return of(start -> runnable.run());
185    }
186
187    /**
188     * Computes the Duration between a start instant and now.
189     *
190     * @param startInclusive the start instant, inclusive, not null.
191     * @return a {@link Duration}, not null.
192     * @since 3.13.0
193     */
194    public static Duration since(final Temporal startInclusive) {
195        return Duration.between(startInclusive, Instant.now());
196    }
197
198    /**
199     * Converts a {@link TimeUnit} to a {@link ChronoUnit}.
200     *
201     * @param timeUnit A non-null TimeUnit.
202     * @return The corresponding ChronoUnit.
203     */
204    static ChronoUnit toChronoUnit(final TimeUnit timeUnit) {
205        // TODO when using Java >= 9: Use TimeUnit.toChronoUnit().
206        switch (Objects.requireNonNull(timeUnit)) {
207        case NANOSECONDS:
208            return ChronoUnit.NANOS;
209        case MICROSECONDS:
210            return ChronoUnit.MICROS;
211        case MILLISECONDS:
212            return ChronoUnit.MILLIS;
213        case SECONDS:
214            return ChronoUnit.SECONDS;
215        case MINUTES:
216            return ChronoUnit.MINUTES;
217        case HOURS:
218            return ChronoUnit.HOURS;
219        case DAYS:
220            return ChronoUnit.DAYS;
221        default:
222            throw new IllegalArgumentException(timeUnit.toString());
223        }
224    }
225
226    /**
227     * Converts an amount and TimeUnit into a Duration.
228     *
229     * @param amount   the amount of the duration, measured in terms of the unit, positive or negative
230     * @param timeUnit the unit that the duration is measured in, must have an exact duration, not null
231     * @return a Duration.
232     */
233    public static Duration toDuration(final long amount, final TimeUnit timeUnit) {
234        return Duration.of(amount, toChronoUnit(timeUnit));
235    }
236
237    /**
238     * Converts a Duration to milliseconds bound to an int (instead of a long).
239     * <p>
240     * Handy for low-level APIs that take millisecond timeouts in ints rather than longs.
241     * </p>
242     * <ul>
243     * <li>If the duration milliseconds are greater than {@link Integer#MAX_VALUE}, then return
244     * {@link Integer#MAX_VALUE}.</li>
245     * <li>If the duration milliseconds are lesser than {@link Integer#MIN_VALUE}, then return
246     * {@link Integer#MIN_VALUE}.</li>
247     * </ul>
248     *
249     * @param duration The duration to convert, not null.
250     * @return int milliseconds.
251     */
252    public static int toMillisInt(final Duration duration) {
253        Objects.requireNonNull(duration, "duration");
254        // intValue() does not do a narrowing conversion here
255        return LONG_TO_INT_RANGE.fit(Long.valueOf(duration.toMillis())).intValue();
256    }
257
258    /**
259     * Returns the given non-null value or {@link Duration#ZERO} if null.
260     *
261     * @param duration The duration to test.
262     * @return The given duration or {@link Duration#ZERO}.
263     */
264    public static Duration zeroIfNull(final Duration duration) {
265        return ObjectUtils.getIfNull(duration, Duration.ZERO);
266    }
267
268    /**
269     * Make private in 4.0.
270     *
271     * @deprecated TODO Make private in 4.0.
272     */
273    @Deprecated
274    public DurationUtils() {
275        // empty
276    }
277}