|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package java.time.chrono; |
|
|
|
import static java.time.temporal.ChronoField.MONTH_OF_YEAR; |
|
import static java.time.temporal.ChronoUnit.DAYS; |
|
import static java.time.temporal.ChronoUnit.MONTHS; |
|
import static java.time.temporal.ChronoUnit.YEARS; |
|
|
|
import java.io.DataInput; |
|
import java.io.DataOutput; |
|
import java.io.IOException; |
|
import java.io.InvalidObjectException; |
|
import java.io.ObjectInputStream; |
|
import java.io.ObjectStreamException; |
|
import java.io.Serializable; |
|
import java.time.DateTimeException; |
|
import java.time.temporal.ChronoUnit; |
|
import java.time.temporal.Temporal; |
|
import java.time.temporal.TemporalAccessor; |
|
import java.time.temporal.TemporalAmount; |
|
import java.time.temporal.TemporalQueries; |
|
import java.time.temporal.TemporalUnit; |
|
import java.time.temporal.UnsupportedTemporalTypeException; |
|
import java.time.temporal.ValueRange; |
|
import java.util.Arrays; |
|
import java.util.Collections; |
|
import java.util.List; |
|
import java.util.Objects; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class ChronoPeriodImpl |
|
implements ChronoPeriod, Serializable { |
|
// this class is only used by JDK chronology implementations and makes assumptions based on that fact |
|
|
|
|
|
|
|
*/ |
|
private static final long serialVersionUID = 57387258289L; |
|
|
|
|
|
|
|
*/ |
|
private static final List<TemporalUnit> SUPPORTED_UNITS = |
|
Collections.unmodifiableList(Arrays.<TemporalUnit>asList(YEARS, MONTHS, DAYS)); |
|
|
|
|
|
|
|
*/ |
|
private final Chronology chrono; |
|
|
|
|
|
*/ |
|
final int years; |
|
|
|
|
|
*/ |
|
final int months; |
|
|
|
|
|
*/ |
|
final int days; |
|
|
|
|
|
|
|
*/ |
|
ChronoPeriodImpl(Chronology chrono, int years, int months, int days) { |
|
Objects.requireNonNull(chrono, "chrono"); |
|
this.chrono = chrono; |
|
this.years = years; |
|
this.months = months; |
|
this.days = days; |
|
} |
|
|
|
|
|
@Override |
|
public long get(TemporalUnit unit) { |
|
if (unit == ChronoUnit.YEARS) { |
|
return years; |
|
} else if (unit == ChronoUnit.MONTHS) { |
|
return months; |
|
} else if (unit == ChronoUnit.DAYS) { |
|
return days; |
|
} else { |
|
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); |
|
} |
|
} |
|
|
|
@Override |
|
public List<TemporalUnit> getUnits() { |
|
return ChronoPeriodImpl.SUPPORTED_UNITS; |
|
} |
|
|
|
@Override |
|
public Chronology getChronology() { |
|
return chrono; |
|
} |
|
|
|
|
|
@Override |
|
public boolean isZero() { |
|
return years == 0 && months == 0 && days == 0; |
|
} |
|
|
|
@Override |
|
public boolean isNegative() { |
|
return years < 0 || months < 0 || days < 0; |
|
} |
|
|
|
|
|
@Override |
|
public ChronoPeriod plus(TemporalAmount amountToAdd) { |
|
ChronoPeriodImpl amount = validateAmount(amountToAdd); |
|
return new ChronoPeriodImpl( |
|
chrono, |
|
Math.addExact(years, amount.years), |
|
Math.addExact(months, amount.months), |
|
Math.addExact(days, amount.days)); |
|
} |
|
|
|
@Override |
|
public ChronoPeriod minus(TemporalAmount amountToSubtract) { |
|
ChronoPeriodImpl amount = validateAmount(amountToSubtract); |
|
return new ChronoPeriodImpl( |
|
chrono, |
|
Math.subtractExact(years, amount.years), |
|
Math.subtractExact(months, amount.months), |
|
Math.subtractExact(days, amount.days)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private ChronoPeriodImpl validateAmount(TemporalAmount amount) { |
|
Objects.requireNonNull(amount, "amount"); |
|
if (amount instanceof ChronoPeriodImpl == false) { |
|
throw new DateTimeException("Unable to obtain ChronoPeriod from TemporalAmount: " + amount.getClass()); |
|
} |
|
ChronoPeriodImpl period = (ChronoPeriodImpl) amount; |
|
if (chrono.equals(period.getChronology()) == false) { |
|
throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + period.getChronology().getId()); |
|
} |
|
return period; |
|
} |
|
|
|
|
|
@Override |
|
public ChronoPeriod multipliedBy(int scalar) { |
|
if (this.isZero() || scalar == 1) { |
|
return this; |
|
} |
|
return new ChronoPeriodImpl( |
|
chrono, |
|
Math.multiplyExact(years, scalar), |
|
Math.multiplyExact(months, scalar), |
|
Math.multiplyExact(days, scalar)); |
|
} |
|
|
|
|
|
@Override |
|
public ChronoPeriod normalized() { |
|
long monthRange = monthRange(); |
|
if (monthRange > 0) { |
|
long totalMonths = years * monthRange + months; |
|
long splitYears = totalMonths / monthRange; |
|
int splitMonths = (int) (totalMonths % monthRange); |
|
if (splitYears == years && splitMonths == months) { |
|
return this; |
|
} |
|
return new ChronoPeriodImpl(chrono, Math.toIntExact(splitYears), splitMonths, days); |
|
|
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private long monthRange() { |
|
ValueRange startRange = chrono.range(MONTH_OF_YEAR); |
|
if (startRange.isFixed() && startRange.isIntValue()) { |
|
return startRange.getMaximum() - startRange.getMinimum() + 1; |
|
} |
|
return -1; |
|
} |
|
|
|
|
|
@Override |
|
public Temporal addTo(Temporal temporal) { |
|
validateChrono(temporal); |
|
if (months == 0) { |
|
if (years != 0) { |
|
temporal = temporal.plus(years, YEARS); |
|
} |
|
} else { |
|
long monthRange = monthRange(); |
|
if (monthRange > 0) { |
|
temporal = temporal.plus(years * monthRange + months, MONTHS); |
|
} else { |
|
if (years != 0) { |
|
temporal = temporal.plus(years, YEARS); |
|
} |
|
temporal = temporal.plus(months, MONTHS); |
|
} |
|
} |
|
if (days != 0) { |
|
temporal = temporal.plus(days, DAYS); |
|
} |
|
return temporal; |
|
} |
|
|
|
|
|
|
|
@Override |
|
public Temporal subtractFrom(Temporal temporal) { |
|
validateChrono(temporal); |
|
if (months == 0) { |
|
if (years != 0) { |
|
temporal = temporal.minus(years, YEARS); |
|
} |
|
} else { |
|
long monthRange = monthRange(); |
|
if (monthRange > 0) { |
|
temporal = temporal.minus(years * monthRange + months, MONTHS); |
|
} else { |
|
if (years != 0) { |
|
temporal = temporal.minus(years, YEARS); |
|
} |
|
temporal = temporal.minus(months, MONTHS); |
|
} |
|
} |
|
if (days != 0) { |
|
temporal = temporal.minus(days, DAYS); |
|
} |
|
return temporal; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void validateChrono(TemporalAccessor temporal) { |
|
Objects.requireNonNull(temporal, "temporal"); |
|
Chronology temporalChrono = temporal.query(TemporalQueries.chronology()); |
|
if (temporalChrono != null && chrono.equals(temporalChrono) == false) { |
|
throw new DateTimeException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + temporalChrono.getId()); |
|
} |
|
} |
|
|
|
|
|
@Override |
|
public boolean equals(Object obj) { |
|
if (this == obj) { |
|
return true; |
|
} |
|
if (obj instanceof ChronoPeriodImpl) { |
|
ChronoPeriodImpl other = (ChronoPeriodImpl) obj; |
|
return years == other.years && months == other.months && |
|
days == other.days && chrono.equals(other.chrono); |
|
} |
|
return false; |
|
} |
|
|
|
@Override |
|
public int hashCode() { |
|
return (years + Integer.rotateLeft(months, 8) + Integer.rotateLeft(days, 16)) ^ chrono.hashCode(); |
|
} |
|
|
|
|
|
@Override |
|
public String toString() { |
|
if (isZero()) { |
|
return getChronology().toString() + " P0D"; |
|
} else { |
|
StringBuilder buf = new StringBuilder(); |
|
buf.append(getChronology().toString()).append(' ').append('P'); |
|
if (years != 0) { |
|
buf.append(years).append('Y'); |
|
} |
|
if (months != 0) { |
|
buf.append(months).append('M'); |
|
} |
|
if (days != 0) { |
|
buf.append(days).append('D'); |
|
} |
|
return buf.toString(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected Object writeReplace() { |
|
return new Ser(Ser.CHRONO_PERIOD_TYPE, this); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void readObject(ObjectInputStream s) throws ObjectStreamException { |
|
throw new InvalidObjectException("Deserialization via serialization delegate"); |
|
} |
|
|
|
void writeExternal(DataOutput out) throws IOException { |
|
out.writeUTF(chrono.getId()); |
|
out.writeInt(years); |
|
out.writeInt(months); |
|
out.writeInt(days); |
|
} |
|
|
|
static ChronoPeriodImpl readExternal(DataInput in) throws IOException { |
|
Chronology chrono = Chronology.of(in.readUTF()); |
|
int years = in.readInt(); |
|
int months = in.readInt(); |
|
int days = in.readInt(); |
|
return new ChronoPeriodImpl(chrono, years, months, days); |
|
} |
|
|
|
} |