
com.github.dm.jrt.util.TimeDuration Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.dm.jrt.util;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Utility class for handling a time duration in different time units.
*
* Created by davide-maestroni on 09/09/2014.
*/
public class TimeDuration extends Time {
/**
* Time duration instance representing the infinity.
*/
public static final TimeDuration INFINITY = seconds(Long.MAX_VALUE);
/**
* Time duration instance representing the zero.
*/
public static final TimeDuration ZERO = seconds(0);
private static final long MILLI_DAYS_OVERFLOW = 106751991167L;
private static final long NANO_DAYS_OVERFLOW = 106750L;
private static final long ONE_MILLI_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
/**
* Constructor.
*
* @param duration the time value.
* @param unit the time unit.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
protected TimeDuration(final long duration, @NotNull final TimeUnit unit) {
super(duration, unit);
if (duration < 0) {
throw new IllegalArgumentException("the time duration cannot be negative: " + duration);
}
}
/**
* Creates and returns an object representing the specified number of days.
*
* @param days the number of days.
* @return the time instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
public static TimeDuration days(final long days) {
if ((days > MAX_DAYS) || (days < -MAX_DAYS)) {
throw new IllegalArgumentException("time value overflow: " + days + " days");
}
return new TimeDuration(days * SECONDS_IN_DAY, TimeUnit.SECONDS);
}
/**
* Creates and returns an object representing the specified time value in the specified time
* unit.
*
* @param time the time value.
* @param unit the time unit.
* @return the time instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
@SuppressWarnings("ConstantConditions")
public static TimeDuration fromUnit(final long time, @NotNull final TimeUnit unit) {
if (unit == null) {
throw new NullPointerException("the time unit must not be null");
}
return new TimeDuration(time, unit);
}
/**
* Creates and returns an object representing the specified number of hours.
*
* @param hours the number of hours
* @return the time instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
public static TimeDuration hours(final long hours) {
if ((hours > MAX_HOURS) || (hours < -MAX_HOURS)) {
throw new IllegalArgumentException("time value overflow: " + hours + " hours");
}
return new TimeDuration(hours * SECONDS_IN_HOUR, TimeUnit.SECONDS);
}
/**
* Creates and returns an object representing the specified number of microseconds.
*
* @param micros the number of microseconds.
* @return the time duration instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
public static TimeDuration micros(final long micros) {
return new TimeDuration(micros, TimeUnit.MICROSECONDS);
}
/**
* Creates and returns an object representing the specified number of milliseconds.
*
* @param millis the number of milliseconds.
* @return the time duration instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
public static TimeDuration millis(final long millis) {
return new TimeDuration(millis, TimeUnit.MILLISECONDS);
}
/**
* Creates and returns an object representing the specified number of minutes.
*
* @param minutes the number of minutes.
* @return the time duration instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
public static TimeDuration minutes(final long minutes) {
if ((minutes > MAX_MINUTES) || (minutes < -MAX_MINUTES)) {
throw new IllegalArgumentException("time value overflow: " + minutes + " minutes");
}
return new TimeDuration(minutes * SECONDS_IN_MINUTE, TimeUnit.SECONDS);
}
/**
* Creates and returns an object representing the specified number of nanoseconds.
*
* @param nanos the number of nanoseconds.
* @return the time duration instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
public static TimeDuration nanos(final long nanos) {
return new TimeDuration(nanos, TimeUnit.NANOSECONDS);
}
/**
* Creates and returns an object representing the specified number of seconds.
*
* @param seconds the number of seconds.
* @return the time duration instance.
* @throws java.lang.IllegalArgumentException if the specified duration is negative.
*/
@NotNull
public static TimeDuration seconds(final long seconds) {
return new TimeDuration(seconds, TimeUnit.SECONDS);
}
/**
* Creates and returns an object representing the time range between now and a time in the past.
*
* If the specified is in the future, a {@code ZERO} duration will be returned.
*
* @param milliTime the system time in milliseconds.
* @return the time duration instance.
* @see java.lang.System#currentTimeMillis()
*/
@NotNull
public static TimeDuration timeSinceMillis(final long milliTime) {
final long millis = System.currentTimeMillis() - milliTime;
return (millis > 0) ? millis(millis) : ZERO;
}
/**
* Creates and returns an object representing the time range between now and a time in the past.
*
* If the specified is in the future, a {@code ZERO} duration will be returned.
*
* @param nanoTime the high precision system time in nanoseconds.
* @return the time duration instance.
* @see java.lang.System#nanoTime()
*/
@NotNull
public static TimeDuration timeSinceNanos(final long nanoTime) {
final long nanos = System.nanoTime() - nanoTime;
return (nanos > 0) ? nanos(nanos) : ZERO;
}
/**
* Creates and returns an object representing the time range between now and a time in the
* future.
* If the specified is in the past, a {@code ZERO} duration will be returned.
*
* @param milliTime the system time in milliseconds.
* @return the time duration instance.
* @see java.lang.System#currentTimeMillis()
*/
@NotNull
public static TimeDuration timeUntilMillis(final long milliTime) {
final long millis = milliTime - System.currentTimeMillis();
return (millis > 0) ? millis(millis) : ZERO;
}
/**
* Creates and returns an object representing the time range between now and a time in the
* future.
* If the specified is in the past, a {@code ZERO} duration will be returned.
*
* @param nanoTime the high precision system time in nanoseconds.
* @return the time duration instance.
* @see java.lang.System#nanoTime()
*/
@NotNull
public static TimeDuration timeUntilNanos(final long nanoTime) {
final long nanos = nanoTime - System.nanoTime();
return (nanos > 0) ? nanos(nanos) : ZERO;
}
/**
* Converts this duration in days.
*
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration daysTime() {
return days(toDays());
}
/**
* Converts this duration in hours.
*
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration hoursTime() {
return hours(toHours());
}
/**
* Converts this duration in microseconds.
*
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration microsTime() {
return micros(toMicros());
}
/**
* Converts this duration in milliseconds.
*
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration millisTime() {
return millis(toMillis());
}
/**
* Returns a new instance whose time value is decremented by the specified one.
* Note that the unit of the returned time will match the one with the highest precision.
* Note also that, if the resulting time is negative, the value will be clipped to 0.
*
* @param time the time to subtract.
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration minus(@NotNull final Time time) {
if (unit.compareTo(time.unit) > 0) {
return fromUnit(Math.max(0, time.unit.convert(this.time, unit) - time.time), time.unit);
}
return fromUnit(Math.max(0, this.time - unit.convert(time.time, time.unit)), unit);
}
/**
* Converts this duration in minutes.
*
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration minutesTime() {
return minutes(toMinutes());
}
/**
* Converts this duration in nanoseconds.
*
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration nanosTime() {
return nanos(toNanos());
}
/**
* Returns a new instance whose time value is incremented by the specified one.
* Note that the unit of the returned time will match the one with the highest precision.
* Note also that, if the resulting time is negative, the value will be clipped to 0.
*
* @param time the time to add.
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration plus(@NotNull final Time time) {
if (unit.compareTo(time.unit) > 0) {
return fromUnit(Math.max(0, time.unit.convert(this.time, unit) + time.time), time.unit);
}
return fromUnit(Math.max(0, this.time + unit.convert(time.time, time.unit)), unit);
}
/**
* Converts this duration in seconds.
*
* @return the time duration instance.
*/
@NotNull
@Override
public TimeDuration secondsTime() {
return seconds(toSeconds());
}
/**
* Checks if this duration represents the infinity.
*
* @return whether this duration is infinite.
*/
public boolean isInfinity() {
return equals(INFINITY);
}
/**
* Performs a {@link java.lang.Thread#join()} using this duration as timeout.
*
* @param target the target thread.
* @throws java.lang.InterruptedException if the current thread is interrupted.
*/
public void join(@NotNull final Thread target) throws InterruptedException {
unit.timedJoin(target, time);
}
/**
* Performs a {@link java.lang.Thread#sleep(long, int)} using this duration as timeout.
*
* @throws java.lang.InterruptedException if the current thread is interrupted.
*/
public void sleep() throws InterruptedException {
unit.sleep(time);
}
/**
* Performs a {@link java.lang.Thread#sleep(long, int)} using this duration as timeout, ensuring
* that the sleep time is respected even if spurious wake ups happen in the while.
*
* @throws java.lang.InterruptedException if the current thread is interrupted.
*/
public void sleepAtLeast() throws InterruptedException {
if (isZero()) {
return;
}
if (((toNanos() % ONE_MILLI_NANOS) == 0) || (toDays() > NANO_DAYS_OVERFLOW)) {
final long startMillis = System.currentTimeMillis();
while (true) {
if (!sleepSinceMillis(startMillis)) {
return;
}
}
}
final long startNanos = System.nanoTime();
while (true) {
if (!sleepSinceNanos(startNanos)) {
return;
}
}
}
/**
* Performs a {@link java.lang.Thread#sleep(long, int)} as if started from the specified system
* time in milliseconds, by using this duration as timeout.
*
* @param milliTime the starting system time in milliseconds.
* @return whether the sleep happened at all.
* @throws java.lang.IllegalStateException if this duration overflows the maximum sleep time.
* @throws java.lang.InterruptedException if the current thread is interrupted.
* @see java.lang.System#currentTimeMillis()
*/
public boolean sleepSinceMillis(final long milliTime) throws InterruptedException {
if (isZero()) {
return false;
}
if (toDays() > MILLI_DAYS_OVERFLOW) {
throw new IllegalStateException("the duration overflows the maximum sleep time: " +
toDays() + " days");
}
final long millisToSleep = milliTime - System.currentTimeMillis() + toMillis();
if (millisToSleep <= 0) {
return false;
}
TimeUnit.MILLISECONDS.sleep(millisToSleep);
return true;
}
/**
* Performs a {@link java.lang.Thread#sleep(long, int)} as if started from the specified high
* precision system time in nanoseconds, by using this duration as timeout.
*
* @param nanoTime the starting system time in nanoseconds.
* @return whether the sleep happened at all.
* @throws java.lang.IllegalStateException if this duration overflows the maximum sleep time.
* @throws java.lang.InterruptedException if the current thread is interrupted.
* @see java.lang.System#nanoTime()
*/
public boolean sleepSinceNanos(final long nanoTime) throws InterruptedException {
if (isZero()) {
return false;
}
if (toDays() > NANO_DAYS_OVERFLOW) {
throw new IllegalStateException("the duration overflows the maximum sleep time: " +
toDays() + " days");
}
final long nanosToSleep = nanoTime - System.nanoTime() + toNanos();
if (nanosToSleep <= 0) {
return false;
}
TimeUnit.NANOSECONDS.sleep(nanosToSleep);
return true;
}
/**
* Performs an {@link java.lang.Object#wait()} using this duration as timeout.
*
* @param target the target object.
* @throws java.lang.InterruptedException if the current thread is interrupted.
*/
@SuppressFBWarnings(value = "WA_NOT_IN_LOOP",
justification = "this is just a wrapper on an Object.wait() call")
public void wait(@NotNull final Object target) throws InterruptedException {
if (isZero()) {
return;
}
if (isInfinity()) {
target.wait();
return;
}
unit.timedWait(target, time);
}
/**
* Performs an {@link java.lang.Object#wait()} as if started from the specified system time in
* milliseconds, by using this duration as timeout.
*
* @param target the target object.
* @param milliTime the starting system time in milliseconds.
* @return whether the wait happened at all.
* @throws java.lang.InterruptedException if the current thread is interrupted.
* @see java.lang.System#currentTimeMillis()
*/
@SuppressFBWarnings(value = "WA_NOT_IN_LOOP",
justification = "this is just a wrapper on an Object.wait() call")
public boolean waitSinceMillis(@NotNull final Object target, final long milliTime) throws
InterruptedException {
if (isZero()) {
return false;
}
if (isInfinity() || (toDays() > MILLI_DAYS_OVERFLOW)) {
target.wait();
return true;
}
final long millisToWait = milliTime - System.currentTimeMillis() + toMillis();
if (millisToWait <= 0) {
return false;
}
TimeUnit.MILLISECONDS.timedWait(target, millisToWait);
return true;
}
/**
* Performs an {@link java.lang.Object#wait()} as if started from the specified high precision
* system time in nanoseconds, by using this duration as timeout.
*
* @param target the target object.
* @param nanoTime the starting system time in nanoseconds.
* @return whether the wait happened at all.
* @throws java.lang.InterruptedException if the current thread is interrupted.
* @see java.lang.System#nanoTime()
*/
@SuppressFBWarnings(value = "WA_NOT_IN_LOOP",
justification = "this is just a wrapper on an Object.wait() call")
public boolean waitSinceNanos(@NotNull final Object target, final long nanoTime) throws
InterruptedException {
if (isZero()) {
return false;
}
if (isInfinity() || (toDays() > NANO_DAYS_OVERFLOW)) {
target.wait();
return true;
}
final long nanosToWait = nanoTime - System.nanoTime() + toNanos();
if (nanosToWait <= 0) {
return false;
}
TimeUnit.NANOSECONDS.timedWait(target, nanosToWait);
return true;
}
/**
* Waits for the specified check to be true by performing an {@link java.lang.Object#wait()} and
* using this duration as timeout.
*
* @param target the target object.
* @param check the check to verify.
* @return whether the check became true before the timeout elapsed.
* @throws java.lang.InterruptedException if the current thread is interrupted.
*/
public boolean waitTrue(@NotNull final Object target, @NotNull final Check check) throws
InterruptedException {
if (isZero()) {
return check.isTrue();
}
if (isInfinity()) {
while (!check.isTrue()) {
target.wait();
}
return true;
}
if ((toNanos() % ONE_MILLI_NANOS) == 0) {
final long startMillis = System.currentTimeMillis();
while (!check.isTrue()) {
if (!waitSinceMillis(target, startMillis)) {
return false;
}
}
} else {
final long startNanos = System.nanoTime();
while (!check.isTrue()) {
if (!waitSinceNanos(target, startNanos)) {
return false;
}
}
}
return true;
}
/**
* Interface defining a check to be performed.
*/
public interface Check {
/**
* Checks if true.
*
* @return whether the check is verified.
*/
boolean isTrue();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy