ProjectAres/Util/core/src/main/java/tc/oc/commons/core/util/TimeUtils.java

236 lines
7.5 KiB
Java

package tc.oc.commons.core.util;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalUnit;
import java.util.Comparator;
import java.util.Date;
import java.util.Optional;
import tc.oc.time.Durations;
public class TimeUtils {
public static final Duration INF_POSITIVE = ChronoUnit.FOREVER.getDuration();
public static final Duration INF_NEGATIVE = INF_POSITIVE.negated();
public static final Instant INF_FUTURE = Instant.MAX;
public static final Instant INF_PAST = Instant.MIN;
public static boolean isFinite(Duration duration) {
return !isInfPositive(duration) && !isInfNegative(duration);
}
public static boolean isInfPositive(Duration duration) {
return INF_POSITIVE.equals(duration);
}
public static boolean isInfNegative(Duration duration) {
return INF_NEGATIVE.equals(duration);
}
public static boolean isInfFuture(Instant instant) {
return INF_FUTURE.equals(instant);
}
public static boolean isInfPast(Instant instant) {
return INF_PAST.equals(instant);
}
public static boolean isPrecise(TemporalUnit unit) {
return !unit.isDurationEstimated() || ChronoUnit.DAYS.equals(unit);
}
public static long toMicros(Duration duration) {
return Math.addExact(Math.multiplyExact(duration.getSeconds(), 1_000_000),
duration.getNano() / 1_000);
}
public static long toUnit(TemporalUnit unit, Duration duration) {
switch((ChronoUnit) unit) {
case NANOS: return duration.toNanos();
case MICROS: return toMicros(duration);
case MILLIS: return duration.toMillis();
case SECONDS: return duration.getSeconds();
}
if(unit.getDuration().getNano() == 0) {
return duration.getSeconds() / unit.getDuration().getSeconds();
}
throw new IllegalArgumentException("Unsupported sub-second unit " + unit);
}
public static long getUnitOrDefault(TemporalAmount period, TemporalUnit unit, long def) {
return period.getUnits().contains(unit) ? period.get(unit) : def;
}
public static long getUnitOrZero(TemporalAmount period, TemporalUnit unit) {
return getUnitOrDefault(period, unit, 0L);
}
public static Duration duration(Instant start, Instant end) {
if(isInfPast(start) || isInfFuture(end)) {
return INF_POSITIVE;
} else if(start.isBefore(end)) {
return Duration.between(start, end);
} else {
return Duration.ZERO;
}
}
public static Optional<Duration> positiveDuration(Instant start, Instant end) {
return Optionals.getIf(start.isBefore(end), () -> duration(start, end));
}
public static Duration durationUntil(Instant end) {
return duration(Instant.now(), end);
}
public static Duration durationSince(Instant start) {
return duration(start, Instant.now());
}
public static boolean isInfFuture(Date date) {
return date.getYear() > 8000; // Hacky, but needs to match Ruby's Time::INF_FUTURE
}
public static boolean isInfPast(Date date) {
return date.getYear() < -10000;
}
public static Instant toInstant(Date date) {
if(isInfFuture(date)) {
return INF_FUTURE;
} else if(isInfPast(date)) {
return INF_PAST;
} else {
return date.toInstant();
}
}
public static long daysRoundingUp(Duration duration) {
final long days = duration.toDays();
return duration.equals(Duration.ofDays(days)) ? days : days + 1;
}
public static long toTicks(Duration duration) {
return duration.toMillis() / 50;
}
public static Duration fromTicks(long ticks) {
return Duration.ofMillis(50 * ticks);
}
public static Duration min(Duration a, Duration b) {
return a.compareTo(b) <= 0 ? a : b;
}
public static Duration max(Duration a, Duration b) {
return a.compareTo(b) >= 0 ? a : b;
}
public static Instant min(Instant a, Instant b) {
return a.compareTo(b) <= 0 ? a : b;
}
public static Instant max(Instant a, Instant b) {
return a.compareTo(b) >= 0 ? a : b;
}
public static boolean isEqualOrBeforeNow(Instant now, Instant instant) {
return !instant.isAfter(now);
}
public static boolean isEqualOrBeforeNow(Instant instant) {
return isEqualOrBeforeNow(Instant.now(), instant);
}
public static boolean isEqualOrAfterNow(Instant now, Instant instant) {
return !instant.isBefore(now);
}
public static boolean isEqualOrAfterNow(Instant instant) {
return isEqualOrAfterNow(Instant.now(), instant);
}
public static Duration parseDuration(String text) throws DateTimeParseException {
if("oo".equals(text)) return INF_POSITIVE;
// If text looks like a plain number, try to parse it as seconds,
// but be fairly strict so we don't accidentally parse a time as
// a number.
if(text.matches("^\\s*-?[0-9]+(\\.[0-9]+)?\\s*$")) {
try {
return Duration.ofMillis((long) (1000 * Double.parseDouble(text)));
} catch(NumberFormatException ignored) {}
}
return Durations.parse(text);
}
public static Duration parseDuration(String text, Duration def) throws DateTimeParseException {
if(text == null || text.length() == 0) {
return def;
} else {
return parseDuration(text);
}
}
public static Instant plus(Instant instant, Duration add) {
if(isInfFuture(instant)) {
return INF_FUTURE;
} else if(isInfPast(instant)) {
return INF_PAST;
} else if(isInfPositive(add)) {
return INF_FUTURE;
} else if(isInfNegative(add)) {
return INF_PAST;
} else {
return instant.plus(add);
}
}
public static Instant minus(Instant instant, Duration sub) {
if(isInfFuture(instant)) {
return INF_FUTURE;
} else if(isInfPast(instant)) {
return INF_PAST;
} else if(isInfPositive(sub)) {
return INF_PAST;
} else if(isInfNegative(sub)) {
return INF_FUTURE;
} else {
return instant.minus(sub);
}
}
public static Duration multiply(Duration duration, double factor) {
final long nanosPerSecond = ChronoUnit.SECONDS.getDuration().toNanos();
final long nanos = (long) (duration.getNano() * factor);
return Duration.ofSeconds(Math.addExact((long) (duration.getSeconds() * factor), Math.floorDiv(nanos, nanosPerSecond)),
Math.floorMod(nanos, nanosPerSecond));
}
public static Duration ceilSeconds(Duration duration) {
return duration.getNano() == 0 ? duration : Duration.ofSeconds(duration.getSeconds());
}
public static int compareUnits(TemporalUnit a, TemporalUnit b) {
return a.getDuration().compareTo(b.getDuration());
}
private static final Comparator<TemporalUnit> ASC_UNITS = TimeUtils::compareUnits;
private static final Comparator<TemporalUnit> DESC_UNITS = ASC_UNITS.reversed();
public static Comparator<TemporalUnit> ascendingUnits() {
return ASC_UNITS;
}
public static Comparator<TemporalUnit> descendingUnits() {
return DESC_UNITS;
}
}