package be.raildelays.delays;
import java.time.*;
import java.util.Date;
/**
* This utility class make usage of JSR-310 from JDK 8 and allow comparison between {@link java.util.Date} and
* {@link TimeDelay}.
* <br>
* Example of comparisons:
* <p>
* <code>
* 15:15 == 15:00+15"
* 15:00 < 15:00+15"
* </code>
* </p>
*
* @author Almex
* @since 2.0
*/
public abstract class Delays {
/**
* We keep an arbitrary reference to an instance of {@link LocalDate} in case of conversion from
* {@link java.sql.Time} into {@link LocalDateTime} to be sure that it's the same date between two
* call to {@link #toLocalTime(Date)}.
*/
public static final LocalDate DATE = LocalDate.now();
/**
* This method is based on {@link Comparable#compareTo(Object)} contract except that it can handle {@code null}
* reference on left parameter and that it computes the opposite of a {@link Duration} between those two
* {@link TimeDelay} parameters (taking into account the {@linkplain TimeDelay#getEffectiveTime() effectiveTime}).
*
* @param startInclusive start {@link TimeDelay}, inclusive and can be null
* @param endExclusive end {@link TimeDelay}, exclusive and can be null
* @return number of milliseconds between {@code endExclusive} and {@code startInclusive}, 0 if either
* {@code endExclusive} or {@code startInclusive} is null
* @throws DateTimeException if the seconds between the temporals cannot be obtained
* @throws ArithmeticException if the calculation exceeds the capacity of {@code Duration}
*/
public static long compareTimeAndDelay(TimeDelay startInclusive, TimeDelay endExclusive) {
long result;
if (startInclusive == endExclusive) {
result = 0;
} else if (startInclusive != null && endExclusive != null) {
LocalTime start = startInclusive.getEffectiveTime();
LocalTime end = endExclusive.getEffectiveTime();
Duration duration = Duration.between(start, end);
result = -duration.toMillis(); // The difference is the opposite of a duration
} else if (startInclusive != null) {
result = 1;
} else {
result = -1;
}
return result;
}
/**
* Compare two {@link TimeDelay} and compute duration between those two
* (taking into account delay only from the <code>timeDelay</code>).
*
* @param time a {@link LocalTime}
* @param timeDelay a {@link TimeDelay}
* @return number of milliseconds between <code>time</code> and <code>timeDelay</code>
*/
public static long compareTimeAndDelay(LocalTime time, TimeDelay timeDelay) {
return compareTimeAndDelay(TimeDelay.of(time), timeDelay);
}
/**
* Compare two {@link TimeDelay} and compute duration between those two
* (taking into account delay only from the <code>timeDelay</code>).
*
* @param timeDelay a {@link TimeDelay}
* @param time a {@link LocalTime}
* @return number of milliseconds between <code>timeDelay</code> and <code>time</code>
*/
public static long compareTimeAndDelay(TimeDelay timeDelay, LocalTime time) {
return compareTimeAndDelay(timeDelay, TimeDelay.of(time));
}
/**
* Compare two {@link TimeDelay} and compute duration between those two
* without taking into account delays of both parameters.
*
* @param timeDelayA first {@link TimeDelay}
* @param timeDelayB second {@link TimeDelay}
* @return number of milliseconds between <code>timeDelayA</code> and <code>timeDelayB</code>
*/
public static long compareTime(TimeDelay timeDelayA, TimeDelay timeDelayB) {
return compareTimeAndDelay(timeDelayA != null ? TimeDelay.of(timeDelayA.getExpectedTime()) : null, timeDelayB != null ? TimeDelay.of(timeDelayB.getExpectedTime()) : null);
}
/**
* Compare two {@link TimeDelay} and compute duration between those two
* without taking into account delay from <code>timeDelay</code>.
*
* @param timeDelay a {@link TimeDelay}
* @param time a {@link LocalTime}
* @return number of milliseconds between <code>timeDelay</code> and <code>time</code>
*/
public static long compareTime(TimeDelay timeDelay, LocalTime time) {
return compareTimeAndDelay(timeDelay != null ? TimeDelay.of(timeDelay.getExpectedTime()) : null, TimeDelay.of(time));
}
/**
* Compare two {@link TimeDelay} and compute duration between those two
* without taking into account delay from <code>timeDelay</code>.
*
* @param time a {@link LocalTime}
* @param timeDelay a {@link TimeDelay}
* @return number of milliseconds between <code>time</code> and <code>timeDelay</code>
*/
public static long compareTime(LocalTime time, TimeDelay timeDelay) {
return compareTimeAndDelay(TimeDelay.of(time), timeDelay != null ? TimeDelay.of(timeDelay.getExpectedTime()) : null);
}
/**
* Compare two {@link LocalTime} and compute duration between those two.
*
* @param timeA a {@link LocalTime}
* @param timeB a {@link LocalTime}
* @return number of milliseconds between <code>timeA</code> and <code>timeB</code>
*/
public static long compareTime(LocalTime timeA, LocalTime timeB) {
return compareTimeAndDelay(TimeDelay.of(timeA), TimeDelay.of(timeB));
}
/**
* Compute a delay between two {@link LocalTime}.
* This method give a way to translate from {@link java.time} API into our Domain-specific language based on
* {@link TimeDelay}.
*
* @param expectedTime the expected time
* @param effectiveTime the effective time
* @return the number of milliseconds between the {@code expectedTime} and the {@code effectiveTime},
* 0 if the {@code expectedTime} or the {@code expectedTime} is {@code null}.
*/
public static long computeDelay(LocalTime expectedTime, LocalTime effectiveTime) {
long result = 0;
if (expectedTime != null && effectiveTime != null) {
Duration duration = Duration.between(expectedTime, effectiveTime);
result = duration.toMillis();
}
return result;
}
/**
* Assumes translation of {@link Date} into {@link LocalTime}.
* This implementation takes into account 4 possible types :
* <ul>
* <li><code>java.sql.Time</code></li>
* <li><code>java.sql.Date</code></li>
* <li><code>java.sql.Timestamp</code></li>
* <li><code>java.util.Date</code></li>
* </ul>
*
* @param date the date to translate
* @return the time part of this date-time, not null
* @throws UnsupportedOperationException if the {@code date} is an instance of {@link java.sql.Date}
*/
public static LocalTime toLocalTime(Date date) {
LocalTime result;
if (date instanceof java.sql.Time) {
result = ((java.sql.Time) date).toLocalTime();
} else if (date instanceof java.sql.Date) {
throw new UnsupportedOperationException("Cannot handle java.sql.Date because we expect only a time");
} else if (date instanceof java.sql.Timestamp) {
result = ((java.sql.Timestamp) date)
.toLocalDateTime()
.toLocalTime();
} else {
result = LocalDateTime
.ofInstant(date.toInstant(), ZoneId.systemDefault())
.toLocalTime();
}
return result;
}
/**
* Convert a delay in milliseconds into minutes.
*
* @param delay in number of milliseconds
* @return the same delay in number of minutes
*/
public static Long toMinutes(Long delay) {
return delay != null ? Duration.ofMillis(delay).toMinutes() : null;
}
/**
* Convert a delay in minutes into milliseconds.
*
* @param delay in number of minutes
* @return the same delay in number of milliseconds
*/
public static Long toMillis(Long delay) {
return delay != null ? Duration.ofMinutes(delay).toMillis() : null;
}
}