in endorsed/src/org.apache.sis.metadata/main/org/apache/sis/temporal/GeneralDuration.java [169:263]
static TemporalAmount distance(final Instant self, final Instant other, final boolean negate, final boolean absolute) {
/*
* Get the temporal value, or null if "now". Other indeterminate values cause an exception to be thrown.
* We use null for "now" instead of fetching the current time for two reasons: avoid mismatch by a few
* nanoseconds when comparing `t1` with `t2`, and for choosing a type compatible with the other instant.
*/
Temporal t1 = getDeterminatePosition(self);
Temporal t2 = getDeterminatePosition(other);
/*
* Ensures that the given objects both have a date part, or that none of them have a date part.
* Note that the "epoch day" field is supported by `LocalDate` as well as the dates with zone ID.
*/
final boolean hasDate = isSupportedByBoth(ChronoField.EPOCH_DAY, t1, t2);
/*
* If at least one date has a timezone, then we require that both dates have a timezone.
* It allows an unambiguous duration in number of days, without time-varying months or years.
* If one date has a timezone and the other does not, a `DateTimeException` will be thrown.
*
* Note 1: we could be lenient and handle the two dates as if they were local, ignoring the timezone.
* But we avoid false sense of accuracy for now. We may revisit this policy later if there is a need.
*/
if (t1 != null && t1.isSupported(ChronoField.OFFSET_SECONDS)) {
if (t2 == null) t2 = ZonedDateTime.now();
final Duration p = Duration.between(t1, t2);
return (absolute || p.isNegative() == negate) ? p.abs() : null;
}
if (t2 != null && (!hasDate || t2.isSupported(ChronoField.OFFSET_SECONDS))) {
if (t1 == null) t1 = ZonedDateTime.now();
final Duration p = Duration.between(t2, t1); // Negative of the result.
return (absolute || p.isNegative() != negate) ? p.abs() : null;
}
/*
* Ensure that the given temporal objects both have a time part, or none of them have a time part.
* If only one of them has a time part, we do not interpret the other one as an instant at midnight
* in order to avoid false sense of accuracy.
*/
final boolean hasTime = isSupportedByBoth(ChronoField.SECOND_OF_DAY, t1, t2);
if (t1 == null) t1 = LocalDateTime.now();
if (t2 == null) t2 = LocalDateTime.now();
ChronoLocalDate d1 = null, d2 = null;
if (hasDate) {
d1 = ChronoLocalDate.from(t1);
d2 = ChronoLocalDate.from(t2);
if (!absolute && (negate ? d1.isBefore(d2) : d1.isAfter(d2))) {
return null; // Stop early if we can.
}
}
/*
* Compute the duration in the time part. If negative (after negation if `negate` is true),
* then we add the necessary number of days to make it positive and remove the same number
* of days from the date. We adjust the date instead of the period computed by `d1.until(d2)`
* in order to have the correct adjustment for the variable number of days in each month.
*/
Duration time = Duration.ZERO;
if (hasTime) {
time = Duration.between(LocalTime.from(t1), LocalTime.from(t2));
if (hasDate) {
final boolean isPositive = d1.isBefore(d2);
if (isPositive || d1.isAfter(d2)) { // Require the period to be non-zero.
if (isPositive ? time.isNegative() : JDK18.isPositive(time)) {
long n = time.toDays(); // Truncated toward 0.
if (isPositive) {
d2 = d2.plus (--n, ChronoUnit.DAYS); // `n` is negative. Reduces period by decreasing the ending.
} else {
d1 = d1.minus(++n, ChronoUnit.DAYS); // `n` is positive. Reduces period by increasing the beginning.
}
time = time.minusDays(n); // If negative, make positive. If positive, make negative.
}
}
}
}
/*
* Get the period for the date part, then combine with the time part if non-zero.
* The result shall be either positive or null.
*/
if (hasDate) {
ChronoPeriod period = d1.until(d2);
if (period.isZero()) {
if (time.isZero()) {
return period;
}
} else {
if (period.isNegative()) {
if (!(negate | absolute)) { // Equivalent to (!negate && !absolute).
return null;
}
period = period.negated();
} else if (negate & !absolute) { // Equivalent to (negate && !absolute).
return null;
}
return time.isZero() ? period : new GeneralDuration(Period.from(period), time.abs());
}
}
return (absolute || time.isNegative() == negate) ? time.abs() : null;
}