提交 a1a03ca4 编写于 作者: S sherman

8016025: JSR 310 DateTime API Updates IV

8020418: Cleanup of -Xlint warnings in java.time
8016623: test/java/time/format/TestDateTimeTextProvider.java failing
Summary: Integration of JSR310 Date/Time API update IV
Reviewed-by: sherman
Contributed-by: scolebourne@joda.org, roger.riggs@oracle.com, masayoshi.okutsu@oracle.com, patrick.zhang@oracle.com, chand.basha@oracle.com
上级 9adbdcb0
...@@ -61,7 +61,6 @@ ...@@ -61,7 +61,6 @@
*/ */
package java.time; package java.time;
import java.time.temporal.UnsupportedTemporalTypeException;
import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_WEEK;
import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DAYS;
...@@ -73,6 +72,7 @@ import java.time.temporal.TemporalAccessor; ...@@ -73,6 +72,7 @@ import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField; import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalQuery;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
import java.time.temporal.WeekFields; import java.time.temporal.WeekFields;
import java.util.Locale; import java.util.Locale;
...@@ -339,7 +339,7 @@ public enum DayOfWeek implements TemporalAccessor, TemporalAdjuster { ...@@ -339,7 +339,7 @@ public enum DayOfWeek implements TemporalAccessor, TemporalAdjuster {
if (field == DAY_OF_WEEK) { if (field == DAY_OF_WEEK) {
return getValue(); return getValue();
} else if (field instanceof ChronoField) { } else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
......
...@@ -459,9 +459,9 @@ public final class Duration ...@@ -459,9 +459,9 @@ public final class Duration
*/ */
public static Duration between(Temporal startInclusive, Temporal endExclusive) { public static Duration between(Temporal startInclusive, Temporal endExclusive) {
try { try {
return ofNanos(startInclusive.periodUntil(endExclusive, NANOS)); return ofNanos(startInclusive.until(endExclusive, NANOS));
} catch (DateTimeException | ArithmeticException ex) { } catch (DateTimeException | ArithmeticException ex) {
long secs = startInclusive.periodUntil(endExclusive, SECONDS); long secs = startInclusive.until(endExclusive, SECONDS);
long nanos; long nanos;
try { try {
nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND); nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND);
...@@ -523,7 +523,7 @@ public final class Duration ...@@ -523,7 +523,7 @@ public final class Duration
} else if (unit == NANOS) { } else if (unit == NANOS) {
return nanos; return nanos;
} else { } else {
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
} }
......
...@@ -69,6 +69,7 @@ import static java.time.temporal.ChronoField.INSTANT_SECONDS; ...@@ -69,6 +69,7 @@ import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.NANO_OF_SECOND;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.NANOS;
import java.io.DataInput; import java.io.DataInput;
...@@ -418,8 +419,9 @@ public final class Instant ...@@ -418,8 +419,9 @@ public final class Instant
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this instant can be queried for the specified field. * This checks if this instant can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -447,6 +449,44 @@ public final class Instant ...@@ -447,6 +449,44 @@ public final class Instant
return field != null && field.isSupportedBy(this); return field != null && field.isSupportedBy(this);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code NANOS}
* <li>{@code MICROS}
* <li>{@code MILLIS}
* <li>{@code SECONDS}
* <li>{@code MINUTES}
* <li>{@code HOURS}
* <li>{@code HALF_DAYS}
* <li>{@code DAYS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
public boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit.isTimeBased() || unit == DAYS;
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -511,7 +551,7 @@ public final class Instant ...@@ -511,7 +551,7 @@ public final class Instant
case MILLI_OF_SECOND: return nanos / 1000_000; case MILLI_OF_SECOND: return nanos / 1000_000;
case INSTANT_SECONDS: INSTANT_SECONDS.checkValidIntValue(seconds); case INSTANT_SECONDS: INSTANT_SECONDS.checkValidIntValue(seconds);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return range(field).checkValidIntValue(field.getFrom(this), field); return range(field).checkValidIntValue(field.getFrom(this), field);
} }
...@@ -548,7 +588,7 @@ public final class Instant ...@@ -548,7 +588,7 @@ public final class Instant
case MILLI_OF_SECOND: return nanos / 1000_000; case MILLI_OF_SECOND: return nanos / 1000_000;
case INSTANT_SECONDS: return seconds; case INSTANT_SECONDS: return seconds;
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
...@@ -665,7 +705,7 @@ public final class Instant ...@@ -665,7 +705,7 @@ public final class Instant
case NANO_OF_SECOND: return (newValue != nanos ? create(seconds, (int) newValue) : this); case NANO_OF_SECOND: return (newValue != nanos ? create(seconds, (int) newValue) : this);
case INSTANT_SECONDS: return (newValue != seconds ? create(newValue, nanos) : this); case INSTANT_SECONDS: return (newValue != seconds ? create(newValue, nanos) : this);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.adjustInto(this, newValue); return field.adjustInto(this, newValue);
} }
...@@ -807,7 +847,7 @@ public final class Instant ...@@ -807,7 +847,7 @@ public final class Instant
case HALF_DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY / 2)); case HALF_DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY / 2));
case DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY)); case DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY));
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.addTo(this, amountToAdd); return unit.addTo(this, amountToAdd);
} }
...@@ -1053,14 +1093,14 @@ public final class Instant ...@@ -1053,14 +1093,14 @@ public final class Instant
* complete units between the two instants. * complete units between the two instants.
* The {@code Temporal} passed to this method must be an {@code Instant}. * The {@code Temporal} passed to this method must be an {@code Instant}.
* For example, the amount in days between two dates can be calculated * For example, the amount in days between two dates can be calculated
* using {@code startInstant.periodUntil(endInstant, SECONDS)}. * using {@code startInstant.until(endInstant, SECONDS)}.
* <p> * <p>
* There are two equivalent ways of using this method. * There are two equivalent ways of using this method.
* The first is to invoke this method. * The first is to invoke this method.
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, SECONDS); * amount = start.until(end, SECONDS);
* amount = SECONDS.between(start, end); * amount = SECONDS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -1085,7 +1125,7 @@ public final class Instant ...@@ -1085,7 +1125,7 @@ public final class Instant
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endInstant, TemporalUnit unit) { public long until(Temporal endInstant, TemporalUnit unit) {
if (endInstant instanceof Instant == false) { if (endInstant instanceof Instant == false) {
Objects.requireNonNull(endInstant, "endInstant"); Objects.requireNonNull(endInstant, "endInstant");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
...@@ -1103,7 +1143,7 @@ public final class Instant ...@@ -1103,7 +1143,7 @@ public final class Instant
case HALF_DAYS: return secondsUntil(end) / (12 * SECONDS_PER_HOUR); case HALF_DAYS: return secondsUntil(end) / (12 * SECONDS_PER_HOUR);
case DAYS: return secondsUntil(end) / (SECONDS_PER_DAY); case DAYS: return secondsUntil(end) / (SECONDS_PER_DAY);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.between(this, endInstant); return unit.between(this, endInstant);
} }
......
...@@ -127,7 +127,7 @@ import java.util.Objects; ...@@ -127,7 +127,7 @@ import java.util.Objects;
* @since 1.8 * @since 1.8
*/ */
public final class LocalDate public final class LocalDate
implements Temporal, TemporalAdjuster, ChronoLocalDate<LocalDate>, Serializable { implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
/** /**
* The minimum supported {@code LocalDate}, '-999999999-01-01'. * The minimum supported {@code LocalDate}, '-999999999-01-01'.
...@@ -466,8 +466,9 @@ public final class LocalDate ...@@ -466,8 +466,9 @@ public final class LocalDate
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this date can be queried for the specified field. * This checks if this date can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -501,6 +502,41 @@ public final class LocalDate ...@@ -501,6 +502,41 @@ public final class LocalDate
return ChronoLocalDate.super.isSupported(field); return ChronoLocalDate.super.isSupported(field);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code DAYS}
* <li>{@code WEEKS}
* <li>{@code MONTHS}
* <li>{@code YEARS}
* <li>{@code DECADES}
* <li>{@code CENTURIES}
* <li>{@code MILLENNIA}
* <li>{@code ERAS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override // override for Javadoc
public boolean isSupported(TemporalUnit unit) {
return ChronoLocalDate.super.isSupported(unit);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -538,7 +574,7 @@ public final class LocalDate ...@@ -538,7 +574,7 @@ public final class LocalDate
} }
return field.range(); return field.range();
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.rangeRefinedBy(this); return field.rangeRefinedBy(this);
} }
...@@ -631,7 +667,7 @@ public final class LocalDate ...@@ -631,7 +667,7 @@ public final class LocalDate
case YEAR: return year; case YEAR: return year;
case ERA: return (year >= 1 ? 1 : 0); case ERA: return (year >= 1 ? 1 : 0);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
private long getProlepticMonth() { private long getProlepticMonth() {
...@@ -988,7 +1024,7 @@ public final class LocalDate ...@@ -988,7 +1024,7 @@ public final class LocalDate
case YEAR: return withYear((int) newValue); case YEAR: return withYear((int) newValue);
case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year)); case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year));
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.adjustInto(this, newValue); return field.adjustInto(this, newValue);
} }
...@@ -1187,7 +1223,7 @@ public final class LocalDate ...@@ -1187,7 +1223,7 @@ public final class LocalDate
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.addTo(this, amountToAdd); return unit.addTo(this, amountToAdd);
} }
...@@ -1497,7 +1533,7 @@ public final class LocalDate ...@@ -1497,7 +1533,7 @@ public final class LocalDate
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code LocalDate}. * The {@code Temporal} passed to this method must be a {@code LocalDate}.
* For example, the amount in days between two dates can be calculated * For example, the amount in days between two dates can be calculated
* using {@code startDate.periodUntil(endDate, DAYS)}. * using {@code startDate.until(endDate, DAYS)}.
* <p> * <p>
* The calculation returns a whole number, representing the number of * The calculation returns a whole number, representing the number of
* complete units between the two dates. * complete units between the two dates.
...@@ -1509,7 +1545,7 @@ public final class LocalDate ...@@ -1509,7 +1545,7 @@ public final class LocalDate
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MONTHS); * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end); * amount = MONTHS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -1534,7 +1570,7 @@ public final class LocalDate ...@@ -1534,7 +1570,7 @@ public final class LocalDate
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endDate, TemporalUnit unit) { public long until(Temporal endDate, TemporalUnit unit) {
Objects.requireNonNull(unit, "unit"); Objects.requireNonNull(unit, "unit");
if (endDate instanceof LocalDate == false) { if (endDate instanceof LocalDate == false) {
Objects.requireNonNull(endDate, "endDate"); Objects.requireNonNull(endDate, "endDate");
...@@ -1552,7 +1588,7 @@ public final class LocalDate ...@@ -1552,7 +1588,7 @@ public final class LocalDate
case MILLENNIA: return monthsUntil(end) / 12000; case MILLENNIA: return monthsUntil(end) / 12000;
case ERAS: return end.getLong(ERA) - getLong(ERA); case ERAS: return end.getLong(ERA) - getLong(ERA);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.between(this, endDate); return unit.between(this, endDate);
} }
...@@ -1591,7 +1627,7 @@ public final class LocalDate ...@@ -1591,7 +1627,7 @@ public final class LocalDate
* The second is to use {@link Period#between(LocalDate, LocalDate)}: * The second is to use {@link Period#between(LocalDate, LocalDate)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* period = start.periodUntil(end); * period = start.until(end);
* period = Period.between(start, end); * period = Period.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -1600,7 +1636,7 @@ public final class LocalDate ...@@ -1600,7 +1636,7 @@ public final class LocalDate
* @return the period between this date and the end date, not null * @return the period between this date and the end date, not null
*/ */
@Override @Override
public Period periodUntil(ChronoLocalDate<?> endDate) { public Period until(ChronoLocalDate endDate) {
LocalDate end = LocalDate.from(endDate); LocalDate end = LocalDate.from(endDate);
long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe
int days = end.day - this.day; int days = end.day - this.day;
...@@ -1803,7 +1839,7 @@ public final class LocalDate ...@@ -1803,7 +1839,7 @@ public final class LocalDate
* @return the comparator value, negative if less, positive if greater * @return the comparator value, negative if less, positive if greater
*/ */
@Override // override for Javadoc and performance @Override // override for Javadoc and performance
public int compareTo(ChronoLocalDate<?> other) { public int compareTo(ChronoLocalDate other) {
if (other instanceof LocalDate) { if (other instanceof LocalDate) {
return compareTo0((LocalDate) other); return compareTo0((LocalDate) other);
} }
...@@ -1843,7 +1879,7 @@ public final class LocalDate ...@@ -1843,7 +1879,7 @@ public final class LocalDate
* @return true if this date is after the specified date * @return true if this date is after the specified date
*/ */
@Override // override for Javadoc and performance @Override // override for Javadoc and performance
public boolean isAfter(ChronoLocalDate<?> other) { public boolean isAfter(ChronoLocalDate other) {
if (other instanceof LocalDate) { if (other instanceof LocalDate) {
return compareTo0((LocalDate) other) > 0; return compareTo0((LocalDate) other) > 0;
} }
...@@ -1872,7 +1908,7 @@ public final class LocalDate ...@@ -1872,7 +1908,7 @@ public final class LocalDate
* @return true if this date is before the specified date * @return true if this date is before the specified date
*/ */
@Override // override for Javadoc and performance @Override // override for Javadoc and performance
public boolean isBefore(ChronoLocalDate<?> other) { public boolean isBefore(ChronoLocalDate other) {
if (other instanceof LocalDate) { if (other instanceof LocalDate) {
return compareTo0((LocalDate) other) < 0; return compareTo0((LocalDate) other) < 0;
} }
...@@ -1901,7 +1937,7 @@ public final class LocalDate ...@@ -1901,7 +1937,7 @@ public final class LocalDate
* @return true if this date is equal to the specified date * @return true if this date is equal to the specified date
*/ */
@Override // override for Javadoc and performance @Override // override for Javadoc and performance
public boolean isEqual(ChronoLocalDate<?> other) { public boolean isEqual(ChronoLocalDate other) {
if (other instanceof LocalDate) { if (other instanceof LocalDate) {
return compareTo0((LocalDate) other) == 0; return compareTo0((LocalDate) other) == 0;
} }
......
...@@ -515,8 +515,9 @@ public final class LocalDateTime ...@@ -515,8 +515,9 @@ public final class LocalDateTime
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this date-time can be queried for the specified field. * This checks if this date-time can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -569,6 +570,48 @@ public final class LocalDateTime ...@@ -569,6 +570,48 @@ public final class LocalDateTime
return field != null && field.isSupportedBy(this); return field != null && field.isSupportedBy(this);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code NANOS}
* <li>{@code MICROS}
* <li>{@code MILLIS}
* <li>{@code SECONDS}
* <li>{@code MINUTES}
* <li>{@code HOURS}
* <li>{@code HALF_DAYS}
* <li>{@code DAYS}
* <li>{@code WEEKS}
* <li>{@code MONTHS}
* <li>{@code YEARS}
* <li>{@code DECADES}
* <li>{@code CENTURIES}
* <li>{@code MILLENNIA}
* <li>{@code ERAS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override // override for Javadoc
public boolean isSupported(TemporalUnit unit) {
return ChronoLocalDateTime.super.isSupported(unit);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -1570,7 +1613,7 @@ public final class LocalDateTime ...@@ -1570,7 +1613,7 @@ public final class LocalDateTime
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code LocalDateTime}. * The {@code Temporal} passed to this method must be a {@code LocalDateTime}.
* For example, the amount in days between two date-times can be calculated * For example, the amount in days between two date-times can be calculated
* using {@code startDateTime.periodUntil(endDateTime, DAYS)}. * using {@code startDateTime.until(endDateTime, DAYS)}.
* <p> * <p>
* The calculation returns a whole number, representing the number of * The calculation returns a whole number, representing the number of
* complete units between the two date-times. * complete units between the two date-times.
...@@ -1582,7 +1625,7 @@ public final class LocalDateTime ...@@ -1582,7 +1625,7 @@ public final class LocalDateTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MONTHS); * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end); * amount = MONTHS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -1609,18 +1652,17 @@ public final class LocalDateTime ...@@ -1609,18 +1652,17 @@ public final class LocalDateTime
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endDateTime, TemporalUnit unit) { public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof LocalDateTime == false) { if (endDateTime instanceof LocalDateTime == false) {
Objects.requireNonNull(endDateTime, "endDateTime"); Objects.requireNonNull(endDateTime, "endDateTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
} }
LocalDateTime end = (LocalDateTime) endDateTime; LocalDateTime end = (LocalDateTime) endDateTime;
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit; if (unit.isTimeBased()) {
if (f.isTimeUnit()) {
long amount = date.daysUntil(end.date); long amount = date.daysUntil(end.date);
if (amount == 0) { if (amount == 0) {
return time.periodUntil(end.time, unit); return time.until(end.time, unit);
} }
long timePart = end.time.toNanoOfDay() - time.toNanoOfDay(); long timePart = end.time.toNanoOfDay() - time.toNanoOfDay();
if (amount > 0) { if (amount > 0) {
...@@ -1630,7 +1672,7 @@ public final class LocalDateTime ...@@ -1630,7 +1672,7 @@ public final class LocalDateTime
amount++; // safe amount++; // safe
timePart -= NANOS_PER_DAY; // safe timePart -= NANOS_PER_DAY; // safe
} }
switch (f) { switch ((ChronoUnit) unit) {
case NANOS: case NANOS:
amount = Math.multiplyExact(amount, NANOS_PER_DAY); amount = Math.multiplyExact(amount, NANOS_PER_DAY);
break; break;
...@@ -1667,7 +1709,7 @@ public final class LocalDateTime ...@@ -1667,7 +1709,7 @@ public final class LocalDateTime
} else if (endDate.isBefore(date) && end.time.isAfter(time)) { } else if (endDate.isBefore(date) && end.time.isAfter(time)) {
endDate = endDate.plusDays(1); endDate = endDate.plusDays(1);
} }
return date.periodUntil(endDate, unit); return date.until(endDate, unit);
} }
return unit.between(this, endDateTime); return unit.between(this, endDateTime);
} }
......
...@@ -470,8 +470,9 @@ public final class LocalTime ...@@ -470,8 +470,9 @@ public final class LocalTime
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this time can be queried for the specified field. * This checks if this time can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -510,6 +511,43 @@ public final class LocalTime ...@@ -510,6 +511,43 @@ public final class LocalTime
return field != null && field.isSupportedBy(this); return field != null && field.isSupportedBy(this);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code NANOS}
* <li>{@code MICROS}
* <li>{@code MILLIS}
* <li>{@code SECONDS}
* <li>{@code MINUTES}
* <li>{@code HOURS}
* <li>{@code HALF_DAYS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override // override for Javadoc
public boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit.isTimeBased();
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -628,7 +666,7 @@ public final class LocalTime ...@@ -628,7 +666,7 @@ public final class LocalTime
case CLOCK_HOUR_OF_DAY: return (hour == 0 ? 24 : hour); case CLOCK_HOUR_OF_DAY: return (hour == 0 ? 24 : hour);
case AMPM_OF_DAY: return hour / 12; case AMPM_OF_DAY: return hour / 12;
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -803,7 +841,7 @@ public final class LocalTime ...@@ -803,7 +841,7 @@ public final class LocalTime
case CLOCK_HOUR_OF_DAY: return withHour((int) (newValue == 24 ? 0 : newValue)); case CLOCK_HOUR_OF_DAY: return withHour((int) (newValue == 24 ? 0 : newValue));
case AMPM_OF_DAY: return plusHours((newValue - (hour / 12)) * 12); case AMPM_OF_DAY: return plusHours((newValue - (hour / 12)) * 12);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.adjustInto(this, newValue); return field.adjustInto(this, newValue);
} }
...@@ -995,8 +1033,7 @@ public final class LocalTime ...@@ -995,8 +1033,7 @@ public final class LocalTime
@Override @Override
public LocalTime plus(long amountToAdd, TemporalUnit unit) { public LocalTime plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit; switch ((ChronoUnit) unit) {
switch (f) {
case NANOS: return plusNanos(amountToAdd); case NANOS: return plusNanos(amountToAdd);
case MICROS: return plusNanos((amountToAdd % MICROS_PER_DAY) * 1000); case MICROS: return plusNanos((amountToAdd % MICROS_PER_DAY) * 1000);
case MILLIS: return plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000_000); case MILLIS: return plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000_000);
...@@ -1005,7 +1042,7 @@ public final class LocalTime ...@@ -1005,7 +1042,7 @@ public final class LocalTime
case HOURS: return plusHours(amountToAdd); case HOURS: return plusHours(amountToAdd);
case HALF_DAYS: return plusHours((amountToAdd % 2) * 12); case HALF_DAYS: return plusHours((amountToAdd % 2) * 12);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.addTo(this, amountToAdd); return unit.addTo(this, amountToAdd);
} }
...@@ -1295,7 +1332,7 @@ public final class LocalTime ...@@ -1295,7 +1332,7 @@ public final class LocalTime
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code LocalTime}. * The {@code Temporal} passed to this method must be a {@code LocalTime}.
* For example, the amount in hours between two times can be calculated * For example, the amount in hours between two times can be calculated
* using {@code startTime.periodUntil(endTime, HOURS)}. * using {@code startTime.until(endTime, HOURS)}.
* <p> * <p>
* The calculation returns a whole number, representing the number of * The calculation returns a whole number, representing the number of
* complete units between the two times. * complete units between the two times.
...@@ -1307,7 +1344,7 @@ public final class LocalTime ...@@ -1307,7 +1344,7 @@ public final class LocalTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MINUTES); * amount = start.until(end, MINUTES);
* amount = MINUTES.between(start, end); * amount = MINUTES.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -1332,7 +1369,7 @@ public final class LocalTime ...@@ -1332,7 +1369,7 @@ public final class LocalTime
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endTime, TemporalUnit unit) { public long until(Temporal endTime, TemporalUnit unit) {
if (endTime instanceof LocalTime == false) { if (endTime instanceof LocalTime == false) {
Objects.requireNonNull(endTime, "endTime"); Objects.requireNonNull(endTime, "endTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
...@@ -1349,7 +1386,7 @@ public final class LocalTime ...@@ -1349,7 +1386,7 @@ public final class LocalTime
case HOURS: return nanosUntil / NANOS_PER_HOUR; case HOURS: return nanosUntil / NANOS_PER_HOUR;
case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR); case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.between(this, endTime); return unit.between(this, endTime);
} }
......
...@@ -61,7 +61,6 @@ ...@@ -61,7 +61,6 @@
*/ */
package java.time; package java.time;
import java.time.temporal.UnsupportedTemporalTypeException;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.MONTHS;
...@@ -75,6 +74,7 @@ import java.time.temporal.TemporalAccessor; ...@@ -75,6 +74,7 @@ import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField; import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalQuery;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
import java.util.Locale; import java.util.Locale;
...@@ -370,7 +370,7 @@ public enum Month implements TemporalAccessor, TemporalAdjuster { ...@@ -370,7 +370,7 @@ public enum Month implements TemporalAccessor, TemporalAdjuster {
if (field == MONTH_OF_YEAR) { if (field == MONTH_OF_YEAR) {
return getValue(); return getValue();
} else if (field instanceof ChronoField) { } else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
......
...@@ -438,7 +438,7 @@ public final class MonthDay ...@@ -438,7 +438,7 @@ public final class MonthDay
case DAY_OF_MONTH: return day; case DAY_OF_MONTH: return day;
case MONTH_OF_YEAR: return month; case MONTH_OF_YEAR: return month;
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
......
...@@ -65,6 +65,7 @@ import static java.time.temporal.ChronoField.EPOCH_DAY; ...@@ -65,6 +65,7 @@ import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_DAY;
import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.OFFSET_SECONDS;
import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.NANOS;
import java.io.IOException; import java.io.IOException;
...@@ -137,25 +138,40 @@ public final class OffsetDateTime ...@@ -137,25 +138,40 @@ public final class OffsetDateTime
public static final OffsetDateTime MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN); public static final OffsetDateTime MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN);
/** /**
* Comparator for two {@code OffsetDateTime} instances based solely on the instant. * Gets a comparator that compares two {@code OffsetDateTime} instances
* based solely on the instant.
* <p> * <p>
* This method differs from the comparison in {@link #compareTo} in that it * This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying instant. * only compares the underlying instant.
* *
* @return a comparator that compares in time-line order
*
* @see #isAfter * @see #isAfter
* @see #isBefore * @see #isBefore
* @see #isEqual * @see #isEqual
*/ */
public static final Comparator<OffsetDateTime> INSTANT_COMPARATOR = new Comparator<OffsetDateTime>() { public static Comparator<OffsetDateTime> timeLineOrder() {
@Override return OffsetDateTime::compareInstant;
public int compare(OffsetDateTime datetime1, OffsetDateTime datetime2) { }
int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond());
if (cmp == 0) { /**
cmp = Long.compare(datetime1.toLocalTime().toNanoOfDay(), datetime2.toLocalTime().toNanoOfDay()); * Compares this {@code OffsetDateTime} to another date-time.
} * The comparison is based on the instant.
return cmp; *
* @param datetime1 the first date-time to compare, not null
* @param datetime2 the other date-time to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
private static int compareInstant(OffsetDateTime datetime1, OffsetDateTime datetime2) {
if (datetime1.getOffset().equals(datetime2.getOffset())) {
return datetime1.toLocalDateTime().compareTo(datetime2.toLocalDateTime());
}
int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond());
if (cmp == 0) {
cmp = datetime1.toLocalTime().getNano() - datetime2.toLocalTime().getNano();
} }
}; return cmp;
}
/** /**
* Serialization version. * Serialization version.
...@@ -406,8 +422,9 @@ public final class OffsetDateTime ...@@ -406,8 +422,9 @@ public final class OffsetDateTime
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this date-time can be queried for the specified field. * This checks if this date-time can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -458,6 +475,51 @@ public final class OffsetDateTime ...@@ -458,6 +475,51 @@ public final class OffsetDateTime
return field instanceof ChronoField || (field != null && field.isSupportedBy(this)); return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code NANOS}
* <li>{@code MICROS}
* <li>{@code MILLIS}
* <li>{@code SECONDS}
* <li>{@code MINUTES}
* <li>{@code HOURS}
* <li>{@code HALF_DAYS}
* <li>{@code DAYS}
* <li>{@code WEEKS}
* <li>{@code MONTHS}
* <li>{@code YEARS}
* <li>{@code DECADES}
* <li>{@code CENTURIES}
* <li>{@code MILLENNIA}
* <li>{@code ERAS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override // override for Javadoc
public boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit != FOREVER;
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -1528,7 +1590,7 @@ public final class OffsetDateTime ...@@ -1528,7 +1590,7 @@ public final class OffsetDateTime
* The start and end points are {@code this} and the specified date-time. * The start and end points are {@code this} and the specified date-time.
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* For example, the period in days between two date-times can be calculated * For example, the period in days between two date-times can be calculated
* using {@code startDateTime.periodUntil(endDateTime, DAYS)}. * using {@code startDateTime.until(endDateTime, DAYS)}.
* <p> * <p>
* The {@code Temporal} passed to this method must be an {@code OffsetDateTime}. * The {@code Temporal} passed to this method must be an {@code OffsetDateTime}.
* If the offset differs between the two date-times, the specified * If the offset differs between the two date-times, the specified
...@@ -1544,7 +1606,7 @@ public final class OffsetDateTime ...@@ -1544,7 +1606,7 @@ public final class OffsetDateTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MONTHS); * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end); * amount = MONTHS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -1571,7 +1633,7 @@ public final class OffsetDateTime ...@@ -1571,7 +1633,7 @@ public final class OffsetDateTime
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endDateTime, TemporalUnit unit) { public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof OffsetDateTime == false) { if (endDateTime instanceof OffsetDateTime == false) {
Objects.requireNonNull(endDateTime, "endDateTime"); Objects.requireNonNull(endDateTime, "endDateTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
...@@ -1579,7 +1641,7 @@ public final class OffsetDateTime ...@@ -1579,7 +1641,7 @@ public final class OffsetDateTime
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
OffsetDateTime end = (OffsetDateTime) endDateTime; OffsetDateTime end = (OffsetDateTime) endDateTime;
end = end.withOffsetSameInstant(offset); end = end.withOffsetSameInstant(offset);
return dateTime.periodUntil(end.dateTime, unit); return dateTime.until(end.dateTime, unit);
} }
return unit.between(this, endDateTime); return unit.between(this, endDateTime);
} }
...@@ -1724,15 +1786,9 @@ public final class OffsetDateTime ...@@ -1724,15 +1786,9 @@ public final class OffsetDateTime
*/ */
@Override @Override
public int compareTo(OffsetDateTime other) { public int compareTo(OffsetDateTime other) {
if (getOffset().equals(other.getOffset())) { int cmp = compareInstant(this, other);
return toLocalDateTime().compareTo(other.toLocalDateTime());
}
int cmp = Long.compare(toEpochSecond(), other.toEpochSecond());
if (cmp == 0) { if (cmp == 0) {
cmp = toLocalTime().getNano() - other.toLocalTime().getNano(); cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
if (cmp == 0) {
cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
}
} }
return cmp; return cmp;
} }
......
...@@ -348,8 +348,9 @@ public final class OffsetTime ...@@ -348,8 +348,9 @@ public final class OffsetTime
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this time can be queried for the specified field. * This checks if this time can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -389,6 +390,43 @@ public final class OffsetTime ...@@ -389,6 +390,43 @@ public final class OffsetTime
return field != null && field.isSupportedBy(this); return field != null && field.isSupportedBy(this);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code NANOS}
* <li>{@code MICROS}
* <li>{@code MILLIS}
* <li>{@code SECONDS}
* <li>{@code MINUTES}
* <li>{@code HOURS}
* <li>{@code HALF_DAYS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override // override for Javadoc
public boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit.isTimeBased();
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -1084,7 +1122,7 @@ public final class OffsetTime ...@@ -1084,7 +1122,7 @@ public final class OffsetTime
* The start and end points are {@code this} and the specified time. * The start and end points are {@code this} and the specified time.
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* For example, the period in hours between two times can be calculated * For example, the period in hours between two times can be calculated
* using {@code startTime.periodUntil(endTime, HOURS)}. * using {@code startTime.until(endTime, HOURS)}.
* <p> * <p>
* The {@code Temporal} passed to this method must be an {@code OffsetTime}. * The {@code Temporal} passed to this method must be an {@code OffsetTime}.
* If the offset differs between the two times, then the specified * If the offset differs between the two times, then the specified
...@@ -1100,7 +1138,7 @@ public final class OffsetTime ...@@ -1100,7 +1138,7 @@ public final class OffsetTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MINUTES); * amount = start.until(end, MINUTES);
* amount = MINUTES.between(start, end); * amount = MINUTES.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -1125,7 +1163,7 @@ public final class OffsetTime ...@@ -1125,7 +1163,7 @@ public final class OffsetTime
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endTime, TemporalUnit unit) { public long until(Temporal endTime, TemporalUnit unit) {
if (endTime instanceof OffsetTime == false) { if (endTime instanceof OffsetTime == false) {
Objects.requireNonNull(endTime, "endTime"); Objects.requireNonNull(endTime, "endTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
...@@ -1142,7 +1180,7 @@ public final class OffsetTime ...@@ -1142,7 +1180,7 @@ public final class OffsetTime
case HOURS: return nanosUntil / NANOS_PER_HOUR; case HOURS: return nanosUntil / NANOS_PER_HOUR;
case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR); case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.between(this, endTime); return unit.between(this, endTime);
} }
......
...@@ -139,7 +139,7 @@ public final class Period ...@@ -139,7 +139,7 @@ public final class Period
* The pattern for parsing. * The pattern for parsing.
*/ */
private final static Pattern PATTERN = private final static Pattern PATTERN =
Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE); Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)W)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE);
/** /**
* The set of supported units. * The set of supported units.
*/ */
...@@ -186,6 +186,20 @@ public final class Period ...@@ -186,6 +186,20 @@ public final class Period
return create(0, months, 0); return create(0, months, 0);
} }
/**
* Obtains a {@code Period} representing a number of weeks.
* <p>
* The resulting period will be day-based, with the amount of days
* equal to the number of weeks multiplied by 7.
* The years and months units will be zero.
*
* @param weeks the number of weeks, positive or negative
* @return the period, with the input weeks converted to days, not null
*/
public static Period ofWeeks(int weeks) {
return create(0, 0, Math.multiplyExact(weeks, 7));
}
/** /**
* Obtains a {@code Period} representing a number of days. * Obtains a {@code Period} representing a number of days.
* <p> * <p>
...@@ -257,22 +271,36 @@ public final class Period ...@@ -257,22 +271,36 @@ public final class Period
* Obtains a {@code Period} from a text string such as {@code PnYnMnD}. * Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
* <p> * <p>
* This will parse the string produced by {@code toString()} which is * This will parse the string produced by {@code toString()} which is
* based on the ISO-8601 period format {@code PnYnMnD}. * based on the ISO-8601 period formats {@code PnYnMnD} and {@code PnW}.
* <p> * <p>
* The string starts with an optional sign, denoted by the ASCII negative * The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole period is negated. * or positive symbol. If negative, the whole period is negated.
* The ASCII letter "P" is next in upper or lower case. * The ASCII letter "P" is next in upper or lower case.
* There are then three sections, each consisting of a number and a suffix. * There are then four sections, each consisting of a number and a suffix.
* At least one of the three sections must be present. * At least one of the four sections must be present.
* The sections have suffixes in ASCII of "Y", "M" and "D" for * The sections have suffixes in ASCII of "Y", "M", "W" and "D" for
* years, months and days, accepted in upper or lower case. * years, months, weeks and days, accepted in upper or lower case.
* The suffixes must occur in order. * The suffixes must occur in order.
* The number part of each section must consist of ASCII digits. * The number part of each section must consist of ASCII digits.
* The number may be prefixed by the ASCII negative or positive symbol. * The number may be prefixed by the ASCII negative or positive symbol.
* The number must parse to an {@code int}. * The number must parse to an {@code int}.
* <p> * <p>
* The leading plus/minus sign, and negative values for other units are * The leading plus/minus sign, and negative values for other units are
* not part of the ISO-8601 standard. * not part of the ISO-8601 standard. In addition, ISO-8601 does not
* permit mixing between the {@code PnYnMnD} and {@code PnW} formats.
* Any week-based input is multiplied by 7 and treated as a number of days.
* <p>
* For example, the following are valid inputs:
* <pre>
* "P2Y" -- Period.ofYears(2)
* "P3M" -- Period.ofMonths(3)
* "P4W" -- Period.ofWeeks(4)
* "P5D" -- Period.ofDays(5)
* "P1Y2M3D" -- Period.of(1, 2, 3)
* "P1Y2M3W4D" -- Period.of(1, 2, 25)
* "P-1Y2M" -- Period.of(-1, 2, 0)
* "-P1Y2M" -- Period.of(-1, -2, 0)
* </pre>
* *
* @param text the text to parse, not null * @param text the text to parse, not null
* @return the parsed period, not null * @return the parsed period, not null
...@@ -285,14 +313,18 @@ public final class Period ...@@ -285,14 +313,18 @@ public final class Period
int negate = ("-".equals(matcher.group(1)) ? -1 : 1); int negate = ("-".equals(matcher.group(1)) ? -1 : 1);
String yearMatch = matcher.group(2); String yearMatch = matcher.group(2);
String monthMatch = matcher.group(3); String monthMatch = matcher.group(3);
String dayMatch = matcher.group(4); String weekMatch = matcher.group(4);
if (yearMatch != null || monthMatch != null || dayMatch != null) { String dayMatch = matcher.group(5);
if (yearMatch != null || monthMatch != null || dayMatch != null || weekMatch != null) {
try { try {
return create(parseNumber(text, yearMatch, negate), int years = parseNumber(text, yearMatch, negate);
parseNumber(text, monthMatch, negate), int months = parseNumber(text, monthMatch, negate);
parseNumber(text, dayMatch, negate)); int weeks = parseNumber(text, weekMatch, negate);
int days = parseNumber(text, dayMatch, negate);
days = Math.addExact(days, Math.multiplyExact(weeks, 7));
return create(years, months, days);
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex); throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);
} }
} }
} }
...@@ -307,7 +339,7 @@ public final class Period ...@@ -307,7 +339,7 @@ public final class Period
try { try {
return Math.multiplyExact(val, negate); return Math.multiplyExact(val, negate);
} catch (ArithmeticException ex) { } catch (ArithmeticException ex) {
throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex); throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);
} }
} }
...@@ -329,10 +361,10 @@ public final class Period ...@@ -329,10 +361,10 @@ public final class Period
* @param startDate the start date, inclusive, not null * @param startDate the start date, inclusive, not null
* @param endDate the end date, exclusive, not null * @param endDate the end date, exclusive, not null
* @return the period between this date and the end date, not null * @return the period between this date and the end date, not null
* @see ChronoLocalDate#periodUntil(ChronoLocalDate) * @see ChronoLocalDate#until(ChronoLocalDate)
*/ */
public static Period between(LocalDate startDate, LocalDate endDate) { public static Period between(LocalDate startDate, LocalDate endDate) {
return startDate.periodUntil(endDate); return startDate.until(endDate);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -386,7 +418,7 @@ public final class Period ...@@ -386,7 +418,7 @@ public final class Period
} else if (unit == ChronoUnit.DAYS) { } else if (unit == ChronoUnit.DAYS) {
return getDays(); return getDays();
} else { } else {
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
} }
......
...@@ -64,6 +64,10 @@ package java.time; ...@@ -64,6 +64,10 @@ package java.time;
import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import static java.time.temporal.ChronoUnit.CENTURIES;
import static java.time.temporal.ChronoUnit.DECADES;
import static java.time.temporal.ChronoUnit.ERAS;
import static java.time.temporal.ChronoUnit.MILLENNIA;
import static java.time.temporal.ChronoUnit.YEARS; import static java.time.temporal.ChronoUnit.YEARS;
import java.io.DataInput; import java.io.DataInput;
...@@ -329,8 +333,9 @@ public final class Year ...@@ -329,8 +333,9 @@ public final class Year
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this year can be queried for the specified field. * This checks if this year can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -357,6 +362,41 @@ public final class Year ...@@ -357,6 +362,41 @@ public final class Year
return field != null && field.isSupportedBy(this); return field != null && field.isSupportedBy(this);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code YEARS}
* <li>{@code DECADES}
* <li>{@code CENTURIES}
* <li>{@code MILLENNIA}
* <li>{@code ERAS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
public boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS;
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -450,7 +490,7 @@ public final class Year ...@@ -450,7 +490,7 @@ public final class Year
case YEAR: return year; case YEAR: return year;
case ERA: return (year < 1 ? 0 : 1); case ERA: return (year < 1 ? 0 : 1);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
...@@ -575,7 +615,7 @@ public final class Year ...@@ -575,7 +615,7 @@ public final class Year
case YEAR: return Year.of((int) newValue); case YEAR: return Year.of((int) newValue);
case ERA: return (getLong(ERA) == newValue ? this : Year.of(1 - year)); case ERA: return (getLong(ERA) == newValue ? this : Year.of(1 - year));
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.adjustInto(this, newValue); return field.adjustInto(this, newValue);
} }
...@@ -664,7 +704,7 @@ public final class Year ...@@ -664,7 +704,7 @@ public final class Year
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.addTo(this, amountToAdd); return unit.addTo(this, amountToAdd);
} }
...@@ -821,7 +861,7 @@ public final class Year ...@@ -821,7 +861,7 @@ public final class Year
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code Year}. * The {@code Temporal} passed to this method must be a {@code Year}.
* For example, the period in decades between two year can be calculated * For example, the period in decades between two year can be calculated
* using {@code startYear.periodUntil(endYear, DECADES)}. * using {@code startYear.until(endYear, DECADES)}.
* <p> * <p>
* The calculation returns a whole number, representing the number of * The calculation returns a whole number, representing the number of
* complete units between the two years. * complete units between the two years.
...@@ -833,7 +873,7 @@ public final class Year ...@@ -833,7 +873,7 @@ public final class Year
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, YEARS); * amount = start.until(end, YEARS);
* amount = YEARS.between(start, end); * amount = YEARS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -858,7 +898,7 @@ public final class Year ...@@ -858,7 +898,7 @@ public final class Year
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endYear, TemporalUnit unit) { public long until(Temporal endYear, TemporalUnit unit) {
if (endYear instanceof Year == false) { if (endYear instanceof Year == false) {
Objects.requireNonNull(endYear, "endYear"); Objects.requireNonNull(endYear, "endYear");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
...@@ -873,7 +913,7 @@ public final class Year ...@@ -873,7 +913,7 @@ public final class Year
case MILLENNIA: return yearsUntil / 1000; case MILLENNIA: return yearsUntil / 1000;
case ERAS: return end.getLong(ERA) - getLong(ERA); case ERAS: return end.getLong(ERA) - getLong(ERA);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.between(this, endYear); return unit.between(this, endYear);
} }
......
...@@ -66,7 +66,12 @@ import static java.time.temporal.ChronoField.MONTH_OF_YEAR; ...@@ -66,7 +66,12 @@ import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import static java.time.temporal.ChronoUnit.CENTURIES;
import static java.time.temporal.ChronoUnit.DECADES;
import static java.time.temporal.ChronoUnit.ERAS;
import static java.time.temporal.ChronoUnit.MILLENNIA;
import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.YEARS;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
...@@ -313,8 +318,9 @@ public final class YearMonth ...@@ -313,8 +318,9 @@ public final class YearMonth
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this year-month can be queried for the specified field. * This checks if this year-month can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -344,6 +350,42 @@ public final class YearMonth ...@@ -344,6 +350,42 @@ public final class YearMonth
return field != null && field.isSupportedBy(this); return field != null && field.isSupportedBy(this);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code MONTHS}
* <li>{@code YEARS}
* <li>{@code DECADES}
* <li>{@code CENTURIES}
* <li>{@code MILLENNIA}
* <li>{@code ERAS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
public boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit == MONTHS || unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS;
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -440,7 +482,7 @@ public final class YearMonth ...@@ -440,7 +482,7 @@ public final class YearMonth
case YEAR: return year; case YEAR: return year;
case ERA: return (year < 1 ? 0 : 1); case ERA: return (year < 1 ? 0 : 1);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
...@@ -639,7 +681,7 @@ public final class YearMonth ...@@ -639,7 +681,7 @@ public final class YearMonth
case YEAR: return withYear((int) newValue); case YEAR: return withYear((int) newValue);
case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year)); case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year));
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.adjustInto(this, newValue); return field.adjustInto(this, newValue);
} }
...@@ -761,7 +803,7 @@ public final class YearMonth ...@@ -761,7 +803,7 @@ public final class YearMonth
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.addTo(this, amountToAdd); return unit.addTo(this, amountToAdd);
} }
...@@ -952,7 +994,7 @@ public final class YearMonth ...@@ -952,7 +994,7 @@ public final class YearMonth
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code YearMonth}. * The {@code Temporal} passed to this method must be a {@code YearMonth}.
* For example, the period in years between two year-months can be calculated * For example, the period in years between two year-months can be calculated
* using {@code startYearMonth.periodUntil(endYearMonth, YEARS)}. * using {@code startYearMonth.until(endYearMonth, YEARS)}.
* <p> * <p>
* The calculation returns a whole number, representing the number of * The calculation returns a whole number, representing the number of
* complete units between the two year-months. * complete units between the two year-months.
...@@ -964,7 +1006,7 @@ public final class YearMonth ...@@ -964,7 +1006,7 @@ public final class YearMonth
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MONTHS); * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end); * amount = MONTHS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -989,7 +1031,7 @@ public final class YearMonth ...@@ -989,7 +1031,7 @@ public final class YearMonth
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endYearMonth, TemporalUnit unit) { public long until(Temporal endYearMonth, TemporalUnit unit) {
if (endYearMonth instanceof YearMonth == false) { if (endYearMonth instanceof YearMonth == false) {
Objects.requireNonNull(endYearMonth, "endYearMonth"); Objects.requireNonNull(endYearMonth, "endYearMonth");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
...@@ -1005,7 +1047,7 @@ public final class YearMonth ...@@ -1005,7 +1047,7 @@ public final class YearMonth
case MILLENNIA: return monthsUntil / 12000; case MILLENNIA: return monthsUntil / 12000;
case ERAS: return end.getLong(ERA) - getLong(ERA); case ERAS: return end.getLong(ERA) - getLong(ERA);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.between(this, endYearMonth); return unit.between(this, endYearMonth);
} }
......
...@@ -400,6 +400,36 @@ public abstract class ZoneId implements Serializable { ...@@ -400,6 +400,36 @@ public abstract class ZoneId implements Serializable {
return of(zoneId, true); return of(zoneId, true);
} }
/**
* Obtains an instance of {@code ZoneId} wrapping an offset.
* <p>
* If the prefix is "GMT", "UTC", or "UT" a {@code ZoneId}
* with the prefix and the non-zero offset is returned.
* If the prefix is empty {@code ""} the {@code ZoneOffset} is returned.
*
* @param prefix the time-zone ID, not null
* @param offset the offset, not null
* @return the zone ID, not null
* @throws IllegalArgumentException if the prefix is not one of
* "GMT", "UTC", or "UT", or ""
*/
public static ZoneId ofOffset(String prefix, ZoneOffset offset) {
Objects.requireNonNull(prefix, "prefix");
Objects.requireNonNull(offset, "offset");
if (prefix.length() == 0) {
return offset;
}
if (!prefix.equals("GMT") && !prefix.equals("UTC") && !prefix.equals("UT")) {
throw new IllegalArgumentException("prefix should be GMT, UTC or UT, is: " + prefix);
}
if (offset.getTotalSeconds() != 0) {
prefix = prefix.concat(offset.getId());
}
return new ZoneRegion(prefix, offset.getRules());
}
/** /**
* Parses the ID, taking a flag to indicate whether {@code ZoneRulesException} * Parses the ID, taking a flag to indicate whether {@code ZoneRulesException}
* should be thrown or not, used in deserialization. * should be thrown or not, used in deserialization.
...@@ -433,7 +463,7 @@ public abstract class ZoneId implements Serializable { ...@@ -433,7 +463,7 @@ public abstract class ZoneId implements Serializable {
private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) { private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) {
String prefix = zoneId.substring(0, prefixLength); String prefix = zoneId.substring(0, prefixLength);
if (zoneId.length() == prefixLength) { if (zoneId.length() == prefixLength) {
return ZoneRegion.ofPrefixedOffset(prefix, ZoneOffset.UTC); return ofOffset(prefix, ZoneOffset.UTC);
} }
if (zoneId.charAt(prefixLength) != '+' && zoneId.charAt(prefixLength) != '-') { if (zoneId.charAt(prefixLength) != '+' && zoneId.charAt(prefixLength) != '-') {
return ZoneRegion.ofId(zoneId, checkAvailable); // drop through to ZoneRulesProvider return ZoneRegion.ofId(zoneId, checkAvailable); // drop through to ZoneRulesProvider
...@@ -441,9 +471,9 @@ public abstract class ZoneId implements Serializable { ...@@ -441,9 +471,9 @@ public abstract class ZoneId implements Serializable {
try { try {
ZoneOffset offset = ZoneOffset.of(zoneId.substring(prefixLength)); ZoneOffset offset = ZoneOffset.of(zoneId.substring(prefixLength));
if (offset == ZoneOffset.UTC) { if (offset == ZoneOffset.UTC) {
return ZoneRegion.ofPrefixedOffset(prefix, offset); return ofOffset(prefix, offset);
} }
return ZoneRegion.ofPrefixedOffset(prefix + offset.toString(), offset); return ofOffset(prefix, offset);
} catch (DateTimeException ex) { } catch (DateTimeException ex) {
throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex); throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex);
} }
......
...@@ -61,7 +61,6 @@ ...@@ -61,7 +61,6 @@
*/ */
package java.time; package java.time;
import java.time.temporal.UnsupportedTemporalTypeException;
import static java.time.LocalTime.MINUTES_PER_HOUR; import static java.time.LocalTime.MINUTES_PER_HOUR;
import static java.time.LocalTime.SECONDS_PER_HOUR; import static java.time.LocalTime.SECONDS_PER_HOUR;
import static java.time.LocalTime.SECONDS_PER_MINUTE; import static java.time.LocalTime.SECONDS_PER_MINUTE;
...@@ -79,6 +78,7 @@ import java.time.temporal.TemporalAccessor; ...@@ -79,6 +78,7 @@ import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField; import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalQuery;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
import java.time.zone.ZoneRules; import java.time.zone.ZoneRules;
import java.util.Objects; import java.util.Objects;
...@@ -581,7 +581,7 @@ public final class ZoneOffset ...@@ -581,7 +581,7 @@ public final class ZoneOffset
if (field == OFFSET_SECONDS) { if (field == OFFSET_SECONDS) {
return totalSeconds; return totalSeconds;
} else if (field instanceof ChronoField) { } else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return range(field).checkValidIntValue(getLong(field), field); return range(field).checkValidIntValue(getLong(field), field);
} }
...@@ -613,7 +613,7 @@ public final class ZoneOffset ...@@ -613,7 +613,7 @@ public final class ZoneOffset
if (field == OFFSET_SECONDS) { if (field == OFFSET_SECONDS) {
return totalSeconds; return totalSeconds;
} else if (field instanceof ChronoField) { } else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
......
...@@ -66,7 +66,6 @@ import java.time.zone.ZoneRules; ...@@ -66,7 +66,6 @@ import java.time.zone.ZoneRules;
import java.time.zone.ZoneRulesException; import java.time.zone.ZoneRulesException;
import java.time.zone.ZoneRulesProvider; import java.time.zone.ZoneRulesProvider;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern;
/** /**
* A geographical region where the same time-zone rules apply. * A geographical region where the same time-zone rules apply.
...@@ -153,19 +152,6 @@ final class ZoneRegion extends ZoneId implements Serializable { ...@@ -153,19 +152,6 @@ final class ZoneRegion extends ZoneId implements Serializable {
} }
} }
/**
* Obtains an instance of {@code ZoneId} wrapping an offset.
* <p>
* For example, zone IDs like 'UTC', 'GMT', 'UT' and 'UTC+01:30' will be setup here.
*
* @param zoneId the time-zone ID, not null
* @param offset the offset, not null
* @return the zone ID, not null
*/
static ZoneRegion ofPrefixedOffset(String zoneId, ZoneOffset offset) {
return new ZoneRegion(zoneId, offset.getRules());
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/** /**
* Constructor. * Constructor.
......
...@@ -642,8 +642,9 @@ public final class ZonedDateTime ...@@ -642,8 +642,9 @@ public final class ZonedDateTime
* Checks if the specified field is supported. * Checks if the specified field is supported.
* <p> * <p>
* This checks if this date-time can be queried for the specified field. * This checks if this date-time can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and * If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} methods will throw an exception. * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p> * <p>
* If the field is a {@link ChronoField} then the query is implemented here. * If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are: * The supported fields are:
...@@ -694,6 +695,48 @@ public final class ZonedDateTime ...@@ -694,6 +695,48 @@ public final class ZonedDateTime
return field instanceof ChronoField || (field != null && field.isSupportedBy(this)); return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
* <ul>
* <li>{@code NANOS}
* <li>{@code MICROS}
* <li>{@code MILLIS}
* <li>{@code SECONDS}
* <li>{@code MINUTES}
* <li>{@code HOURS}
* <li>{@code HALF_DAYS}
* <li>{@code DAYS}
* <li>{@code WEEKS}
* <li>{@code MONTHS}
* <li>{@code YEARS}
* <li>{@code DECADES}
* <li>{@code CENTURIES}
* <li>{@code MILLENNIA}
* <li>{@code ERAS}
* </ul>
* All other {@code ChronoUnit} instances will return false.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override // override for Javadoc
public boolean isSupported(TemporalUnit unit) {
return ChronoZonedDateTime.super.isSupported(unit);
}
//-----------------------------------------------------------------------
/** /**
* Gets the range of valid values for the specified field. * Gets the range of valid values for the specified field.
* <p> * <p>
...@@ -1540,8 +1583,7 @@ public final class ZonedDateTime ...@@ -1540,8 +1583,7 @@ public final class ZonedDateTime
@Override @Override
public ZonedDateTime plus(long amountToAdd, TemporalUnit unit) { public ZonedDateTime plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
ChronoUnit u = (ChronoUnit) unit; if (unit.isDateBased()) {
if (u.isDateUnit()) {
return resolveLocal(dateTime.plus(amountToAdd, unit)); return resolveLocal(dateTime.plus(amountToAdd, unit));
} else { } else {
return resolveInstant(dateTime.plus(amountToAdd, unit)); return resolveInstant(dateTime.plus(amountToAdd, unit));
...@@ -1990,7 +2032,7 @@ public final class ZonedDateTime ...@@ -1990,7 +2032,7 @@ public final class ZonedDateTime
* The start and end points are {@code this} and the specified date-time. * The start and end points are {@code this} and the specified date-time.
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* For example, the period in days between two date-times can be calculated * For example, the period in days between two date-times can be calculated
* using {@code startDateTime.periodUntil(endDateTime, DAYS)}. * using {@code startDateTime.until(endDateTime, DAYS)}.
* <p> * <p>
* The {@code Temporal} passed to this method must be a {@code ZonedDateTime}. * The {@code Temporal} passed to this method must be a {@code ZonedDateTime}.
* If the time-zone differs between the two zoned date-times, the specified * If the time-zone differs between the two zoned date-times, the specified
...@@ -2006,7 +2048,7 @@ public final class ZonedDateTime ...@@ -2006,7 +2048,7 @@ public final class ZonedDateTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MONTHS); * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end); * amount = MONTHS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -2047,7 +2089,7 @@ public final class ZonedDateTime ...@@ -2047,7 +2089,7 @@ public final class ZonedDateTime
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override @Override
public long periodUntil(Temporal endDateTime, TemporalUnit unit) { public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof ZonedDateTime == false) { if (endDateTime instanceof ZonedDateTime == false) {
Objects.requireNonNull(endDateTime, "endDateTime"); Objects.requireNonNull(endDateTime, "endDateTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
...@@ -2055,11 +2097,10 @@ public final class ZonedDateTime ...@@ -2055,11 +2097,10 @@ public final class ZonedDateTime
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
ZonedDateTime end = (ZonedDateTime) endDateTime; ZonedDateTime end = (ZonedDateTime) endDateTime;
end = end.withZoneSameInstant(zone); end = end.withZoneSameInstant(zone);
ChronoUnit u = (ChronoUnit) unit; if (unit.isDateBased()) {
if (u.isDateUnit()) { return dateTime.until(end.dateTime, unit);
return dateTime.periodUntil(end.dateTime, unit);
} else { } else {
return toOffsetDateTime().periodUntil(end.toOffsetDateTime(), unit); return toOffsetDateTime().until(end.toOffsetDateTime(), unit);
} }
} }
return unit.between(this, endDateTime); return unit.between(this, endDateTime);
......
...@@ -67,6 +67,8 @@ import java.time.DateTimeException; ...@@ -67,6 +67,8 @@ import java.time.DateTimeException;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal; import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit; import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
...@@ -96,12 +98,12 @@ import java.util.Objects; ...@@ -96,12 +98,12 @@ import java.util.Objects;
* // Enumerate the list of available calendars and print today for each * // Enumerate the list of available calendars and print today for each
* Set&lt;Chronology&gt; chronos = Chronology.getAvailableChronologies(); * Set&lt;Chronology&gt; chronos = Chronology.getAvailableChronologies();
* for (Chronology chrono : chronos) { * for (Chronology chrono : chronos) {
* ChronoLocalDate&lt;?&gt; date = chrono.dateNow(); * ChronoLocalDate date = chrono.dateNow();
* System.out.printf(" %20s: %s%n", chrono.getID(), date.toString()); * System.out.printf(" %20s: %s%n", chrono.getID(), date.toString());
* } * }
* *
* // Print the Hijrah date and calendar * // Print the Hijrah date and calendar
* ChronoLocalDate&lt;?&gt; date = Chronology.of("Hijrah").dateNow(); * ChronoLocalDate date = Chronology.of("Hijrah").dateNow();
* int day = date.get(ChronoField.DAY_OF_MONTH); * int day = date.get(ChronoField.DAY_OF_MONTH);
* int dow = date.get(ChronoField.DAY_OF_WEEK); * int dow = date.get(ChronoField.DAY_OF_WEEK);
* int month = date.get(ChronoField.MONTH_OF_YEAR); * int month = date.get(ChronoField.MONTH_OF_YEAR);
...@@ -110,10 +112,10 @@ import java.util.Objects; ...@@ -110,10 +112,10 @@ import java.util.Objects;
* dow, day, month, year); * dow, day, month, year);
* // Print today's date and the last day of the year * // Print today's date and the last day of the year
* ChronoLocalDate&lt;?&gt; now1 = Chronology.of("Hijrah").dateNow(); * ChronoLocalDate now1 = Chronology.of("Hijrah").dateNow();
* ChronoLocalDate&lt;?&gt; first = now1.with(ChronoField.DAY_OF_MONTH, 1) * ChronoLocalDate first = now1.with(ChronoField.DAY_OF_MONTH, 1)
* .with(ChronoField.MONTH_OF_YEAR, 1); * .with(ChronoField.MONTH_OF_YEAR, 1);
* ChronoLocalDate&lt;?&gt; last = first.plus(1, ChronoUnit.YEARS) * ChronoLocalDate last = first.plus(1, ChronoUnit.YEARS)
* .minus(1, ChronoUnit.DAYS); * .minus(1, ChronoUnit.DAYS);
* System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChronology().getID(), * System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChronology().getID(),
* first, last); * first, last);
...@@ -138,22 +140,61 @@ import java.util.Objects; ...@@ -138,22 +140,61 @@ import java.util.Objects;
* @param <D> the ChronoLocalDate of this date-time * @param <D> the ChronoLocalDate of this date-time
* @since 1.8 * @since 1.8
*/ */
abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> abstract class ChronoDateImpl<D extends ChronoLocalDate>
implements ChronoLocalDate<D>, Temporal, TemporalAdjuster, Serializable { implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable {
/** /**
* Serialization version. * Serialization version.
*/ */
private static final long serialVersionUID = 6282433883239719096L; private static final long serialVersionUID = 6282433883239719096L;
/**
* Casts the {@code Temporal} to {@code ChronoLocalDate} ensuring it bas the specified chronology.
*
* @param chrono the chronology to check for, not null
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoLocalDate}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
* or the chronology is not equal this Chronology
*/
static <D extends ChronoLocalDate> D ensureValid(Chronology chrono, Temporal temporal) {
@SuppressWarnings("unchecked")
D other = (D) temporal;
if (chrono.equals(other.getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + other.getChronology().getId());
}
return other;
}
//-----------------------------------------------------------------------
/** /**
* Creates an instance. * Creates an instance.
*/ */
ChronoDateImpl() { ChronoDateImpl() {
} }
@Override
@SuppressWarnings("unchecked")
public D with(TemporalAdjuster adjuster) {
return (D) ChronoLocalDate.super.with(adjuster);
}
@Override
@SuppressWarnings("unchecked")
public D with(TemporalField field, long value) {
return (D) ChronoLocalDate.super.with(field, value);
}
//-----------------------------------------------------------------------
@Override
@SuppressWarnings("unchecked")
public D plus(TemporalAmount amount) {
return (D) ChronoLocalDate.super.plus(amount);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
@SuppressWarnings("unchecked")
public D plus(long amountToAdd, TemporalUnit unit) { public D plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit; ChronoUnit f = (ChronoUnit) unit;
...@@ -167,9 +208,21 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -167,9 +208,21 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return ChronoLocalDate.super.plus(amountToAdd, unit); return (D) ChronoLocalDate.super.plus(amountToAdd, unit);
}
@Override
@SuppressWarnings("unchecked")
public D minus(TemporalAmount amount) {
return (D) ChronoLocalDate.super.minus(amount);
}
@Override
@SuppressWarnings("unchecked")
public D minus(long amountToSubtract, TemporalUnit unit) {
return (D) ChronoLocalDate.super.minus(amountToSubtract, unit);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -254,6 +307,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -254,6 +307,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
* @return a date based on this one with the years subtracted, not null * @return a date based on this one with the years subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range * @throws DateTimeException if the result exceeds the supported date range
*/ */
@SuppressWarnings("unchecked")
D minusYears(long yearsToSubtract) { D minusYears(long yearsToSubtract) {
return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract)); return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract));
} }
...@@ -274,6 +328,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -274,6 +328,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
* @return a date based on this one with the months subtracted, not null * @return a date based on this one with the months subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range * @throws DateTimeException if the result exceeds the supported date range
*/ */
@SuppressWarnings("unchecked")
D minusMonths(long monthsToSubtract) { D minusMonths(long monthsToSubtract) {
return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract)); return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract));
} }
...@@ -293,6 +348,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -293,6 +348,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
* @return a date based on this one with the weeks subtracted, not null * @return a date based on this one with the weeks subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range * @throws DateTimeException if the result exceeds the supported date range
*/ */
@SuppressWarnings("unchecked")
D minusWeeks(long weeksToSubtract) { D minusWeeks(long weeksToSubtract) {
return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract)); return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract));
} }
...@@ -310,6 +366,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -310,6 +366,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
* @return a date based on this one with the days subtracted, not null * @return a date based on this one with the days subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range * @throws DateTimeException if the result exceeds the supported date range
*/ */
@SuppressWarnings("unchecked")
D minusDays(long daysToSubtract) { D minusDays(long daysToSubtract) {
return (daysToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract)); return (daysToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl<D>)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract));
} }
...@@ -321,13 +378,13 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -321,13 +378,13 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
* @throws ArithmeticException {@inheritDoc} * @throws ArithmeticException {@inheritDoc}
*/ */
@Override @Override
public long periodUntil(Temporal endDateTime, TemporalUnit unit) { public long until(Temporal endDateTime, TemporalUnit unit) {
Objects.requireNonNull(endDateTime, "endDateTime"); Objects.requireNonNull(endDateTime, "endDateTime");
Objects.requireNonNull(unit, "unit"); Objects.requireNonNull(unit, "unit");
if (endDateTime instanceof ChronoLocalDate == false) { if (endDateTime instanceof ChronoLocalDate == false) {
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
} }
ChronoLocalDate<?> end = (ChronoLocalDate<?>) endDateTime; ChronoLocalDate end = (ChronoLocalDate) endDateTime;
if (getChronology().equals(end.getChronology()) == false) { if (getChronology().equals(end.getChronology()) == false) {
throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); throw new DateTimeException("Unable to calculate amount as objects have different chronologies");
} }
...@@ -342,16 +399,16 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -342,16 +399,16 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
case MILLENNIA: return monthsUntil(end) / 12000; case MILLENNIA: return monthsUntil(end) / 12000;
case ERAS: return end.getLong(ERA) - getLong(ERA); case ERAS: return end.getLong(ERA) - getLong(ERA);
} }
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return unit.between(this, endDateTime); return unit.between(this, endDateTime);
} }
private long daysUntil(ChronoLocalDate<?> end) { private long daysUntil(ChronoLocalDate end) {
return end.toEpochDay() - toEpochDay(); // no overflow return end.toEpochDay() - toEpochDay(); // no overflow
} }
private long monthsUntil(ChronoLocalDate<?> end) { private long monthsUntil(ChronoLocalDate end) {
ValueRange range = getChronology().range(MONTH_OF_YEAR); ValueRange range = getChronology().range(MONTH_OF_YEAR);
if (range.getMaximum() != 12) { if (range.getMaximum() != 12) {
throw new IllegalStateException("ChronoDateImpl only supports Chronologies with 12 months per year"); throw new IllegalStateException("ChronoDateImpl only supports Chronologies with 12 months per year");
...@@ -367,7 +424,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>> ...@@ -367,7 +424,7 @@ abstract class ChronoDateImpl<D extends ChronoLocalDate<D>>
return true; return true;
} }
if (obj instanceof ChronoLocalDate) { if (obj instanceof ChronoLocalDate) {
return compareTo((ChronoLocalDate<?>) obj) == 0; return compareTo((ChronoLocalDate) obj) == 0;
} }
return false; return false;
} }
......
...@@ -242,11 +242,10 @@ import java.util.Objects; ...@@ -242,11 +242,10 @@ import java.util.Objects;
* Additional calendar systems may be added to the system. * Additional calendar systems may be added to the system.
* See {@link Chronology} for more details. * See {@link Chronology} for more details.
* *
* @param <D> the concrete type for the date
* @since 1.8 * @since 1.8
*/ */
public interface ChronoLocalDate<D extends ChronoLocalDate<D>> public interface ChronoLocalDate
extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDate<?>> { extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDate> {
/** /**
* Gets a comparator that compares {@code ChronoLocalDate} in * Gets a comparator that compares {@code ChronoLocalDate} in
...@@ -263,7 +262,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -263,7 +262,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @see #isBefore * @see #isBefore
* @see #isEqual * @see #isEqual
*/ */
static Comparator<ChronoLocalDate<?>> timeLineOrder() { static Comparator<ChronoLocalDate> timeLineOrder() {
return Chronology.DATE_ORDER; return Chronology.DATE_ORDER;
} }
...@@ -289,9 +288,9 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -289,9 +288,9 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws DateTimeException if unable to convert to a {@code ChronoLocalDate} * @throws DateTimeException if unable to convert to a {@code ChronoLocalDate}
* @see Chronology#date(TemporalAccessor) * @see Chronology#date(TemporalAccessor)
*/ */
static ChronoLocalDate<?> from(TemporalAccessor temporal) { static ChronoLocalDate from(TemporalAccessor temporal) {
if (temporal instanceof ChronoLocalDate) { if (temporal instanceof ChronoLocalDate) {
return (ChronoLocalDate<?>) temporal; return (ChronoLocalDate) temporal;
} }
Chronology chrono = temporal.query(TemporalQuery.chronology()); Chronology chrono = temporal.query(TemporalQuery.chronology());
if (chrono == null) { if (chrono == null) {
...@@ -367,6 +366,25 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -367,6 +366,25 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
return (isLeapYear() ? 366 : 365); return (isLeapYear() ? 366 : 365);
} }
/**
* Checks if the specified field is supported.
* <p>
* This checks if the specified field can be queried on this date.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p>
* The set of supported fields is defined by the chronology and normally includes
* all {@code ChronoField} date fields.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field can be queried, false if not
*/
@Override @Override
default boolean isSupported(TemporalField field) { default boolean isSupported(TemporalField field) {
if (field instanceof ChronoField) { if (field instanceof ChronoField) {
...@@ -375,6 +393,32 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -375,6 +393,32 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
return field != null && field.isSupportedBy(this); return field != null && field.isSupportedBy(this);
} }
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to or subtracted from this date.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* The set of supported units is defined by the chronology and normally includes
* all {@code ChronoUnit} date units except {@code FOREVER}.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
default boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit.isDateBased();
}
return unit != null && unit.isSupportedBy(this);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// override for covariant return type // override for covariant return type
/** /**
...@@ -383,8 +427,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -383,8 +427,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws ArithmeticException {@inheritDoc} * @throws ArithmeticException {@inheritDoc}
*/ */
@Override @Override
default D with(TemporalAdjuster adjuster) { default ChronoLocalDate with(TemporalAdjuster adjuster) {
return (D) getChronology().ensureChronoLocalDate(Temporal.super.with(adjuster)); return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
} }
/** /**
...@@ -394,11 +438,11 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -394,11 +438,11 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws ArithmeticException {@inheritDoc} * @throws ArithmeticException {@inheritDoc}
*/ */
@Override @Override
default D with(TemporalField field, long newValue) { default ChronoLocalDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) { if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return (D) getChronology().ensureChronoLocalDate(field.adjustInto(this, newValue)); return ChronoDateImpl.ensureValid(getChronology(), field.adjustInto(this, newValue));
} }
/** /**
...@@ -407,8 +451,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -407,8 +451,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws ArithmeticException {@inheritDoc} * @throws ArithmeticException {@inheritDoc}
*/ */
@Override @Override
default D plus(TemporalAmount amount) { default ChronoLocalDate plus(TemporalAmount amount) {
return (D) getChronology().ensureChronoLocalDate(Temporal.super.plus(amount)); return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
} }
/** /**
...@@ -417,11 +461,11 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -417,11 +461,11 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws ArithmeticException {@inheritDoc} * @throws ArithmeticException {@inheritDoc}
*/ */
@Override @Override
default D plus(long amountToAdd, TemporalUnit unit) { default ChronoLocalDate plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
} }
return (D) getChronology().ensureChronoLocalDate(unit.addTo(this, amountToAdd)); return ChronoDateImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd));
} }
/** /**
...@@ -430,8 +474,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -430,8 +474,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws ArithmeticException {@inheritDoc} * @throws ArithmeticException {@inheritDoc}
*/ */
@Override @Override
default D minus(TemporalAmount amount) { default ChronoLocalDate minus(TemporalAmount amount) {
return (D) getChronology().ensureChronoLocalDate(Temporal.super.minus(amount)); return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
} }
/** /**
...@@ -441,8 +485,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -441,8 +485,8 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws ArithmeticException {@inheritDoc} * @throws ArithmeticException {@inheritDoc}
*/ */
@Override @Override
default D minus(long amountToSubtract, TemporalUnit unit) { default ChronoLocalDate minus(long amountToSubtract, TemporalUnit unit) {
return (D) getChronology().ensureChronoLocalDate(Temporal.super.minus(amountToSubtract, unit)); return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -522,14 +566,14 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -522,14 +566,14 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* The calculation returns a whole number, representing the number of * The calculation returns a whole number, representing the number of
* complete units between the two dates. * complete units between the two dates.
* For example, the amount in days between two dates can be calculated * For example, the amount in days between two dates can be calculated
* using {@code startDate.periodUntil(endDate, DAYS)}. * using {@code startDate.until(endDate, DAYS)}.
* <p> * <p>
* There are two equivalent ways of using this method. * There are two equivalent ways of using this method.
* The first is to invoke this method. * The first is to invoke this method.
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* amount = start.periodUntil(end, MONTHS); * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end); * amount = MONTHS.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -555,7 +599,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -555,7 +599,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
@Override // override for Javadoc @Override // override for Javadoc
long periodUntil(Temporal endDate, TemporalUnit unit); long until(Temporal endDate, TemporalUnit unit);
/** /**
* Calculates the period between this date and another date as a {@code Period}. * Calculates the period between this date and another date as a {@code Period}.
...@@ -575,7 +619,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -575,7 +619,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @throws DateTimeException if the period cannot be calculated * @throws DateTimeException if the period cannot be calculated
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
Period periodUntil(ChronoLocalDate<?> endDate); Period until(ChronoLocalDate endDate);
/** /**
* Formats this date using the specified formatter. * Formats this date using the specified formatter.
...@@ -606,8 +650,9 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -606,8 +650,9 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @param localTime the local time to use, not null * @param localTime the local time to use, not null
* @return the local date-time formed from this date and the specified time, not null * @return the local date-time formed from this date and the specified time, not null
*/ */
default ChronoLocalDateTime<D> atTime(LocalTime localTime) { @SuppressWarnings("unchecked")
return (ChronoLocalDateTime<D>)ChronoLocalDateTimeImpl.of(this, localTime); default ChronoLocalDateTime<?> atTime(LocalTime localTime) {
return ChronoLocalDateTimeImpl.of(this, localTime);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -656,7 +701,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -656,7 +701,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @return the comparator value, negative if less, positive if greater * @return the comparator value, negative if less, positive if greater
*/ */
@Override @Override
default int compareTo(ChronoLocalDate<?> other) { default int compareTo(ChronoLocalDate other) {
int cmp = Long.compare(toEpochDay(), other.toEpochDay()); int cmp = Long.compare(toEpochDay(), other.toEpochDay());
if (cmp == 0) { if (cmp == 0) {
cmp = getChronology().compareTo(other.getChronology()); cmp = getChronology().compareTo(other.getChronology());
...@@ -678,7 +723,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -678,7 +723,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @param other the other date to compare to, not null * @param other the other date to compare to, not null
* @return true if this is after the specified date * @return true if this is after the specified date
*/ */
default boolean isAfter(ChronoLocalDate<?> other) { default boolean isAfter(ChronoLocalDate other) {
return this.toEpochDay() > other.toEpochDay(); return this.toEpochDay() > other.toEpochDay();
} }
...@@ -696,7 +741,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -696,7 +741,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @param other the other date to compare to, not null * @param other the other date to compare to, not null
* @return true if this is before the specified date * @return true if this is before the specified date
*/ */
default boolean isBefore(ChronoLocalDate<?> other) { default boolean isBefore(ChronoLocalDate other) {
return this.toEpochDay() < other.toEpochDay(); return this.toEpochDay() < other.toEpochDay();
} }
...@@ -714,7 +759,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>> ...@@ -714,7 +759,7 @@ public interface ChronoLocalDate<D extends ChronoLocalDate<D>>
* @param other the other date to compare to, not null * @param other the other date to compare to, not null
* @return true if the underlying date is equal to the specified date * @return true if the underlying date is equal to the specified date
*/ */
default boolean isEqual(ChronoLocalDate<?> other) { default boolean isEqual(ChronoLocalDate other) {
return this.toEpochDay() == other.toEpochDay(); return this.toEpochDay() == other.toEpochDay();
} }
......
...@@ -63,6 +63,7 @@ package java.time.chrono; ...@@ -63,6 +63,7 @@ package java.time.chrono;
import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_DAY;
import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.NANOS;
import java.time.DateTimeException; import java.time.DateTimeException;
...@@ -73,6 +74,7 @@ import java.time.ZoneId; ...@@ -73,6 +74,7 @@ import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal; import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjuster;
...@@ -114,7 +116,7 @@ import java.util.Objects; ...@@ -114,7 +116,7 @@ import java.util.Objects;
* @param <D> the concrete type for the date of this date-time * @param <D> the concrete type for the date of this date-time
* @since 1.8 * @since 1.8
*/ */
public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>> public interface ChronoLocalDateTime<D extends ChronoLocalDate>
extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDateTime<?>> { extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDateTime<?>> {
/** /**
...@@ -191,9 +193,54 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>> ...@@ -191,9 +193,54 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>>
*/ */
LocalTime toLocalTime(); LocalTime toLocalTime();
@Override // Override to provide javadoc /**
* Checks if the specified field is supported.
* <p>
* This checks if the specified field can be queried on this date-time.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p>
* The set of supported fields is defined by the chronology and normally includes
* all {@code ChronoField} date and time fields.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field can be queried, false if not
*/
@Override
boolean isSupported(TemporalField field); boolean isSupported(TemporalField field);
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to or subtracted from this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* The set of supported units is defined by the chronology and normally includes
* all {@code ChronoUnit} units except {@code FOREVER}.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
default boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit != FOREVER;
}
return unit != null && unit.isSupportedBy(this);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// override for covariant return type // override for covariant return type
/** /**
...@@ -203,7 +250,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>> ...@@ -203,7 +250,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoLocalDateTime<D> with(TemporalAdjuster adjuster) { default ChronoLocalDateTime<D> with(TemporalAdjuster adjuster) {
return (ChronoLocalDateTime<D>)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.with(adjuster))); return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.with(adjuster));
} }
/** /**
...@@ -221,7 +268,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>> ...@@ -221,7 +268,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoLocalDateTime<D> plus(TemporalAmount amount) { default ChronoLocalDateTime<D> plus(TemporalAmount amount) {
return (ChronoLocalDateTime<D>)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.plus(amount))); return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.plus(amount));
} }
/** /**
...@@ -239,7 +286,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>> ...@@ -239,7 +286,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoLocalDateTime<D> minus(TemporalAmount amount) { default ChronoLocalDateTime<D> minus(TemporalAmount amount) {
return (ChronoLocalDateTime<D>)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.minus(amount))); return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amount));
} }
/** /**
...@@ -249,7 +296,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>> ...@@ -249,7 +296,7 @@ public interface ChronoLocalDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoLocalDateTime<D> minus(long amountToSubtract, TemporalUnit unit) { default ChronoLocalDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {
return (ChronoLocalDateTime<D>)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.minus(amountToSubtract, unit))); return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amountToSubtract, unit));
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
......
...@@ -98,7 +98,7 @@ import java.util.Objects; ...@@ -98,7 +98,7 @@ import java.util.Objects;
* @param <D> the concrete type for the date of this date-time * @param <D> the concrete type for the date of this date-time
* @since 1.8 * @since 1.8
*/ */
final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate>
implements ChronoLocalDateTime<D>, Temporal, TemporalAdjuster, Serializable { implements ChronoLocalDateTime<D>, Temporal, TemporalAdjuster, Serializable {
/** /**
...@@ -171,9 +171,27 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -171,9 +171,27 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
* @param time the local time, not null * @param time the local time, not null
* @return the local date-time, not null * @return the local date-time, not null
*/ */
@SuppressWarnings("rawtypes") static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> of(R date, LocalTime time) {
static ChronoLocalDateTimeImpl<?> of(ChronoLocalDate<?> date, LocalTime time) { return new ChronoLocalDateTimeImpl<>(date, time);
return new ChronoLocalDateTimeImpl(date, time); }
/**
* Casts the {@code Temporal} to {@code ChronoLocalDateTime} ensuring it bas the specified chronology.
*
* @param chrono the chronology to check for, not null
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl
* or the chronology is not equal this Chronology
*/
static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) {
@SuppressWarnings("unchecked")
ChronoLocalDateTimeImpl<R> other = (ChronoLocalDateTimeImpl<R>) temporal;
if (chrono.equals(other.toLocalDate().getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ ", actual: " + other.toLocalDate().getChronology().getId());
}
return other;
} }
/** /**
...@@ -202,7 +220,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -202,7 +220,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
return this; return this;
} }
// Validate that the new Temporal is a ChronoLocalDate (and not something else) // Validate that the new Temporal is a ChronoLocalDate (and not something else)
D cd = (D) date.getChronology().ensureChronoLocalDate(newDate); D cd = ChronoDateImpl.ensureValid(date.getChronology(), newDate);
return new ChronoLocalDateTimeImpl<>(cd, newTime); return new ChronoLocalDateTimeImpl<>(cd, newTime);
} }
...@@ -260,13 +278,13 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -260,13 +278,13 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
public ChronoLocalDateTimeImpl<D> with(TemporalAdjuster adjuster) { public ChronoLocalDateTimeImpl<D> with(TemporalAdjuster adjuster) {
if (adjuster instanceof ChronoLocalDate) { if (adjuster instanceof ChronoLocalDate) {
// The Chronology is checked in with(date,time) // The Chronology is checked in with(date,time)
return with((ChronoLocalDate<D>) adjuster, time); return with((ChronoLocalDate) adjuster, time);
} else if (adjuster instanceof LocalTime) { } else if (adjuster instanceof LocalTime) {
return with(date, (LocalTime) adjuster); return with(date, (LocalTime) adjuster);
} else if (adjuster instanceof ChronoLocalDateTimeImpl) { } else if (adjuster instanceof ChronoLocalDateTimeImpl) {
return (ChronoLocalDateTimeImpl<D>)(date.getChronology().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl<?>) adjuster)); return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster);
} }
return (ChronoLocalDateTimeImpl<D>)(date.getChronology().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl<?>) adjuster.adjustInto(this))); return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster.adjustInto(this));
} }
@Override @Override
...@@ -279,7 +297,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -279,7 +297,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
return with(date.with(field, newValue), time); return with(date.with(field, newValue), time);
} }
} }
return (ChronoLocalDateTimeImpl<D>)(date.getChronology().ensureChronoLocalDateTime(field.adjustInto(this, newValue))); return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), field.adjustInto(this, newValue));
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -298,7 +316,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -298,7 +316,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
} }
return with(date.plus(amountToAdd, unit), time); return with(date.plus(amountToAdd, unit), time);
} }
return (ChronoLocalDateTimeImpl<D>)(date.getChronology().ensureChronoLocalDateTime(unit.addTo(this, amountToAdd))); return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), unit.addTo(this, amountToAdd));
} }
private ChronoLocalDateTimeImpl<D> plusDays(long days) { private ChronoLocalDateTimeImpl<D> plusDays(long days) {
...@@ -322,7 +340,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -322,7 +340,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
private ChronoLocalDateTimeImpl<D> plusWithOverflow(ChronoLocalDate<?> newDate, long hours, long minutes, long seconds, long nanos) { private ChronoLocalDateTimeImpl<D> plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos) {
// 9223372036854775808 long, 2147483648 int // 9223372036854775808 long, 2147483648 int
if ((hours | minutes | seconds | nanos) == 0) { if ((hours | minutes | seconds | nanos) == 0) {
return with(newDate, time); return with(newDate, time);
...@@ -351,7 +369,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -351,7 +369,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public long periodUntil(Temporal endDateTime, TemporalUnit unit) { public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof ChronoLocalDateTime == false) { if (endDateTime instanceof ChronoLocalDateTime == false) {
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
} }
...@@ -361,10 +379,9 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -361,10 +379,9 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); throw new DateTimeException("Unable to calculate amount as objects have different chronologies");
} }
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit; if (unit.isTimeBased()) {
if (f.isTimeUnit()) {
long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY); long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY);
switch (f) { switch ((ChronoUnit) unit) {
case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break; case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break;
case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break; case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break;
case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break; case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break;
...@@ -373,13 +390,13 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -373,13 +390,13 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break; case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break;
case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break; case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break;
} }
return Math.addExact(amount, time.periodUntil(end.toLocalTime(), unit)); return Math.addExact(amount, time.until(end.toLocalTime(), unit));
} }
D endDate = end.toLocalDate(); ChronoLocalDate endDate = end.toLocalDate();
if (end.toLocalTime().isBefore(time)) { if (end.toLocalTime().isBefore(time)) {
endDate = endDate.minus(1, ChronoUnit.DAYS); endDate = endDate.minus(1, ChronoUnit.DAYS);
} }
return date.periodUntil(endDate, unit); return date.until(endDate, unit);
} }
return unit.between(this, endDateTime); return unit.between(this, endDateTime);
} }
...@@ -404,7 +421,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -404,7 +421,7 @@ final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate<D>>
} }
static ChronoLocalDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException { static ChronoLocalDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
ChronoLocalDate<?> date = (ChronoLocalDate<?>) in.readObject(); ChronoLocalDate date = (ChronoLocalDate) in.readObject();
LocalTime time = (LocalTime) in.readObject(); LocalTime time = (LocalTime) in.readObject();
return date.atTime(time); return date.atTime(time);
} }
......
...@@ -63,6 +63,7 @@ package java.time.chrono; ...@@ -63,6 +63,7 @@ package java.time.chrono;
import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.OFFSET_SECONDS;
import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.NANOS;
import java.time.DateTimeException; import java.time.DateTimeException;
...@@ -73,6 +74,7 @@ import java.time.ZoneOffset; ...@@ -73,6 +74,7 @@ import java.time.ZoneOffset;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal; import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjuster;
...@@ -115,7 +117,7 @@ import java.util.Objects; ...@@ -115,7 +117,7 @@ import java.util.Objects;
* @param <D> the concrete type for the date of this date-time * @param <D> the concrete type for the date of this date-time
* @since 1.8 * @since 1.8
*/ */
public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> public interface ChronoZonedDateTime<D extends ChronoLocalDate>
extends Temporal, Comparable<ChronoZonedDateTime<?>> { extends Temporal, Comparable<ChronoZonedDateTime<?>> {
/** /**
...@@ -338,9 +340,54 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> ...@@ -338,9 +340,54 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>>
*/ */
ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone); ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone);
@Override // Override to provide javadoc /**
* Checks if the specified field is supported.
* <p>
* This checks if the specified field can be queried on this date-time.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p>
* The set of supported fields is defined by the chronology and normally includes
* all {@code ChronoField} fields.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field can be queried, false if not
*/
@Override
boolean isSupported(TemporalField field); boolean isSupported(TemporalField field);
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to or subtracted from this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* The set of supported units is defined by the chronology and normally includes
* all {@code ChronoUnit} units except {@code FOREVER}.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
default boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit != FOREVER;
}
return unit != null && unit.isSupportedBy(this);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// override for covariant return type // override for covariant return type
/** /**
...@@ -350,7 +397,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> ...@@ -350,7 +397,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) { default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) {
return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.with(adjuster))); return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.with(adjuster));
} }
/** /**
...@@ -368,7 +415,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> ...@@ -368,7 +415,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoZonedDateTime<D> plus(TemporalAmount amount) { default ChronoZonedDateTime<D> plus(TemporalAmount amount) {
return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.plus(amount))); return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.plus(amount));
} }
/** /**
...@@ -386,7 +433,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> ...@@ -386,7 +433,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoZonedDateTime<D> minus(TemporalAmount amount) { default ChronoZonedDateTime<D> minus(TemporalAmount amount) {
return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amount))); return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amount));
} }
/** /**
...@@ -396,7 +443,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> ...@@ -396,7 +443,7 @@ public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>>
*/ */
@Override @Override
default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) { default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {
return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amountToSubtract, unit))); return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amountToSubtract, unit));
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
......
...@@ -101,7 +101,7 @@ import java.util.Objects; ...@@ -101,7 +101,7 @@ import java.util.Objects;
* @param <D> the concrete type for the date of this date-time * @param <D> the concrete type for the date of this date-time
* @since 1.8 * @since 1.8
*/ */
final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>> final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
implements ChronoZonedDateTime<D>, Serializable { implements ChronoZonedDateTime<D>, Serializable {
/** /**
...@@ -131,7 +131,7 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -131,7 +131,7 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>>
* @param preferredOffset the zone offset, null if no preference * @param preferredOffset the zone offset, null if no preference
* @return the zoned date-time, not null * @return the zoned date-time, not null
*/ */
static <R extends ChronoLocalDate<R>> ChronoZonedDateTime<R> ofBest( static <R extends ChronoLocalDate> ChronoZonedDateTime<R> ofBest(
ChronoLocalDateTimeImpl<R> localDateTime, ZoneId zone, ZoneOffset preferredOffset) { ChronoLocalDateTimeImpl<R> localDateTime, ZoneId zone, ZoneOffset preferredOffset) {
Objects.requireNonNull(localDateTime, "localDateTime"); Objects.requireNonNull(localDateTime, "localDateTime");
Objects.requireNonNull(zone, "zone"); Objects.requireNonNull(zone, "zone");
...@@ -167,14 +167,13 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -167,14 +167,13 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>>
* @param zone the zone identifier, not null * @param zone the zone identifier, not null
* @return the zoned date-time, not null * @return the zoned date-time, not null
*/ */
@SuppressWarnings("rawtypes")
static ChronoZonedDateTimeImpl<?> ofInstant(Chronology chrono, Instant instant, ZoneId zone) { static ChronoZonedDateTimeImpl<?> ofInstant(Chronology chrono, Instant instant, ZoneId zone) {
ZoneRules rules = zone.getRules(); ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant); ZoneOffset offset = rules.getOffset(instant);
Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules
LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset); LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
ChronoLocalDateTimeImpl<?> cldt = (ChronoLocalDateTimeImpl<?>) chrono.localDateTime(ldt); ChronoLocalDateTimeImpl<?> cldt = (ChronoLocalDateTimeImpl<?>)chrono.localDateTime(ldt);
return new ChronoZonedDateTimeImpl(cldt, offset, zone); return new ChronoZonedDateTimeImpl<>(cldt, offset, zone);
} }
/** /**
...@@ -184,10 +183,30 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -184,10 +183,30 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>>
* @param zone the time-zone to use, validated not null * @param zone the time-zone to use, validated not null
* @return the zoned date-time, validated not null * @return the zoned date-time, validated not null
*/ */
@SuppressWarnings("unchecked")
private ChronoZonedDateTimeImpl<D> create(Instant instant, ZoneId zone) { private ChronoZonedDateTimeImpl<D> create(Instant instant, ZoneId zone) {
return (ChronoZonedDateTimeImpl<D>)ofInstant(toLocalDate().getChronology(), instant, zone); return (ChronoZonedDateTimeImpl<D>)ofInstant(toLocalDate().getChronology(), instant, zone);
} }
/**
* Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} ensuring it bas the specified chronology.
*
* @param chrono the chronology to check for, not null
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl
* or the chronology is not equal this Chronology
*/
static <R extends ChronoLocalDate> ChronoZonedDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) {
@SuppressWarnings("unchecked")
ChronoZonedDateTimeImpl<R> other = (ChronoZonedDateTimeImpl<R>) temporal;
if (chrono.equals(other.toLocalDate().getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ ", actual: " + other.toLocalDate().getChronology().getId());
}
return other;
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Constructor. * Constructor.
...@@ -271,7 +290,7 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -271,7 +290,7 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>>
} }
return ofBest(dateTime.with(field, newValue), zone, offset); return ofBest(dateTime.with(field, newValue), zone, offset);
} }
return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(field.adjustInto(this, newValue))); return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), field.adjustInto(this, newValue));
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -280,12 +299,12 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -280,12 +299,12 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>>
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
return with(dateTime.plus(amountToAdd, unit)); return with(dateTime.plus(amountToAdd, unit));
} }
return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(unit.addTo(this, amountToAdd))); /// TODO: Generics replacement Risk! return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), unit.addTo(this, amountToAdd)); /// TODO: Generics replacement Risk!
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public long periodUntil(Temporal endDateTime, TemporalUnit unit) { public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof ChronoZonedDateTime == false) { if (endDateTime instanceof ChronoZonedDateTime == false) {
throw new DateTimeException("Unable to calculate amount as objects are of two different types"); throw new DateTimeException("Unable to calculate amount as objects are of two different types");
} }
...@@ -296,7 +315,7 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>> ...@@ -296,7 +315,7 @@ final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate<D>>
} }
if (unit instanceof ChronoUnit) { if (unit instanceof ChronoUnit) {
end = end.withZoneSameInstant(offset); end = end.withZoneSameInstant(offset);
return dateTime.periodUntil(end.toLocalDateTime(), unit); return dateTime.until(end.toLocalDateTime(), unit);
} }
return unit.between(this, endDateTime); return unit.between(this, endDateTime);
} }
......
...@@ -238,7 +238,7 @@ public interface Era extends TemporalAccessor, TemporalAdjuster { ...@@ -238,7 +238,7 @@ public interface Era extends TemporalAccessor, TemporalAdjuster {
if (field == ERA) { if (field == ERA) {
return getValue(); return getValue();
} else if (field instanceof ChronoField) { } else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
......
...@@ -71,8 +71,10 @@ import java.time.DateTimeException; ...@@ -71,8 +71,10 @@ import java.time.DateTimeException;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
...@@ -115,7 +117,7 @@ import sun.util.logging.PlatformLogger; ...@@ -115,7 +117,7 @@ import sun.util.logging.PlatformLogger;
* <tr class="altColor"> * <tr class="altColor">
* <td>Hijrah-umalqura</td> * <td>Hijrah-umalqura</td>
* <td>islamic-umalqura</td> * <td>islamic-umalqura</td>
* <td>ca-islamic-cv-umalqura</td> * <td>ca-islamic-umalqura</td>
* <td>Islamic - Umm Al-Qura calendar of Saudi Arabia</td> * <td>Islamic - Umm Al-Qura calendar of Saudi Arabia</td>
* </tr> * </tr>
* </tbody> * </tbody>
...@@ -126,10 +128,10 @@ import sun.util.logging.PlatformLogger; ...@@ -126,10 +128,10 @@ import sun.util.logging.PlatformLogger;
* <p> * <p>
* Selecting the chronology from the locale uses {@link Chronology#ofLocale} * Selecting the chronology from the locale uses {@link Chronology#ofLocale}
* to find the Chronology based on Locale supported BCP 47 extension mechanism * to find the Chronology based on Locale supported BCP 47 extension mechanism
* to request a specific calendar ("ca") and variant ("cv"). For example, * to request a specific calendar ("ca"). For example,
* </p> * </p>
* <pre> * <pre>
* Locale locale = Locale.forLanguageTag("en-US-u-ca-islamic-cv-umalqura"); * Locale locale = Locale.forLanguageTag("en-US-u-ca-islamic-umalqura");
* Chronology chrono = Chronology.ofLocale(locale); * Chronology chrono = Chronology.ofLocale(locale);
* </pre> * </pre>
* *
...@@ -472,11 +474,16 @@ public final class HijrahChronology extends Chronology implements Serializable { ...@@ -472,11 +474,16 @@ public final class HijrahChronology extends Chronology implements Serializable {
* @param prolepticYear the proleptic-year * @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year * @param dayOfYear the day-of-year
* @return the Hijrah local date, not null * @return the Hijrah local date, not null
* @throws DateTimeException if unable to create the date * @throws DateTimeException if the value of the year is out of range,
* or if the day-of-year is invalid for the year
*/ */
@Override @Override
public HijrahDate dateYearDay(int prolepticYear, int dayOfYear) { public HijrahDate dateYearDay(int prolepticYear, int dayOfYear) {
return HijrahDate.of(this, prolepticYear, 1, 1).plusDays(dayOfYear - 1); // TODO better HijrahDate date = HijrahDate.of(this, prolepticYear, 1, 1);
if (dayOfYear > date.lengthOfYear()) {
throw new DateTimeException("Invalid dayOfYear: " + dayOfYear);
}
return date.plusDays(dayOfYear - 1);
} }
/** /**
...@@ -515,16 +522,19 @@ public final class HijrahChronology extends Chronology implements Serializable { ...@@ -515,16 +522,19 @@ public final class HijrahChronology extends Chronology implements Serializable {
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime<HijrahDate> localDateTime(TemporalAccessor temporal) { public ChronoLocalDateTime<HijrahDate> localDateTime(TemporalAccessor temporal) {
return (ChronoLocalDateTime<HijrahDate>) super.localDateTime(temporal); return (ChronoLocalDateTime<HijrahDate>) super.localDateTime(temporal);
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<HijrahDate> zonedDateTime(TemporalAccessor temporal) { public ChronoZonedDateTime<HijrahDate> zonedDateTime(TemporalAccessor temporal) {
return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(temporal); return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(temporal);
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<HijrahDate> zonedDateTime(Instant instant, ZoneId zone) { public ChronoZonedDateTime<HijrahDate> zonedDateTime(Instant instant, ZoneId zone) {
return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(instant, zone); return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(instant, zone);
} }
...@@ -550,7 +560,7 @@ public final class HijrahChronology extends Chronology implements Serializable { ...@@ -550,7 +560,7 @@ public final class HijrahChronology extends Chronology implements Serializable {
} }
@Override @Override
public Era eraOf(int eraValue) { public HijrahEra eraOf(int eraValue) {
switch (eraValue) { switch (eraValue) {
case 1: case 1:
return HijrahEra.AH; return HijrahEra.AH;
...@@ -580,6 +590,8 @@ public final class HijrahChronology extends Chronology implements Serializable { ...@@ -580,6 +590,8 @@ public final class HijrahChronology extends Chronology implements Serializable {
case YEAR: case YEAR:
case YEAR_OF_ERA: case YEAR_OF_ERA:
return ValueRange.of(getMinimumYear(), getMaximumYear()); return ValueRange.of(getMinimumYear(), getMaximumYear());
case ERA:
return ValueRange.of(1, 1);
default: default:
return field.range(); return field.range();
} }
...@@ -587,6 +599,13 @@ public final class HijrahChronology extends Chronology implements Serializable { ...@@ -587,6 +599,13 @@ public final class HijrahChronology extends Chronology implements Serializable {
return field.range(); return field.range();
} }
//-----------------------------------------------------------------------
@Override // override for return type
public HijrahDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (HijrahDate) super.resolveDate(fieldValues, resolverStyle);
}
//-----------------------------------------------------------------------
/** /**
* Check the validity of a year. * Check the validity of a year.
* *
......
...@@ -109,7 +109,7 @@ import java.time.temporal.ValueRange; ...@@ -109,7 +109,7 @@ import java.time.temporal.ValueRange;
*/ */
public final class HijrahDate public final class HijrahDate
extends ChronoDateImpl<HijrahDate> extends ChronoDateImpl<HijrahDate>
implements ChronoLocalDate<HijrahDate>, Serializable { implements ChronoLocalDate, Serializable {
/** /**
* Serialization version. * Serialization version.
...@@ -204,7 +204,7 @@ public final class HijrahDate ...@@ -204,7 +204,7 @@ public final class HijrahDate
* @throws DateTimeException if the current date cannot be obtained * @throws DateTimeException if the current date cannot be obtained
*/ */
public static HijrahDate now(Clock clock) { public static HijrahDate now(Clock clock) {
return HijrahChronology.INSTANCE.date(LocalDate.now(clock)); return HijrahDate.ofEpochDay(HijrahChronology.INSTANCE, LocalDate.now(clock).toEpochDay());
} }
/** /**
...@@ -349,7 +349,7 @@ public final class HijrahDate ...@@ -349,7 +349,7 @@ public final class HijrahDate
} }
return getChronology().range(f); return getChronology().range(f);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.rangeRefinedBy(this); return field.rangeRefinedBy(this);
} }
...@@ -372,7 +372,7 @@ public final class HijrahDate ...@@ -372,7 +372,7 @@ public final class HijrahDate
case YEAR: return prolepticYear; case YEAR: return prolepticYear;
case ERA: return getEraValue(); case ERA: return getEraValue();
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
...@@ -393,7 +393,7 @@ public final class HijrahDate ...@@ -393,7 +393,7 @@ public final class HijrahDate
case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH)); case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH));
case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR)); case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR));
case DAY_OF_MONTH: return resolvePreviousValid(prolepticYear, monthOfYear, nvalue); case DAY_OF_MONTH: return resolvePreviousValid(prolepticYear, monthOfYear, nvalue);
case DAY_OF_YEAR: return resolvePreviousValid(prolepticYear, ((nvalue - 1) / 30) + 1, ((nvalue - 1) % 30) + 1); case DAY_OF_YEAR: return plusDays(Math.min(nvalue, lengthOfYear()) - getDayOfYear());
case EPOCH_DAY: return new HijrahDate(chrono, newValue); case EPOCH_DAY: return new HijrahDate(chrono, newValue);
case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * 7); case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * 7);
case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * 7); case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * 7);
...@@ -403,9 +403,9 @@ public final class HijrahDate ...@@ -403,9 +403,9 @@ public final class HijrahDate
case YEAR: return resolvePreviousValid(nvalue, monthOfYear, dayOfMonth); case YEAR: return resolvePreviousValid(nvalue, monthOfYear, dayOfMonth);
case ERA: return resolvePreviousValid(1 - prolepticYear, monthOfYear, dayOfMonth); case ERA: return resolvePreviousValid(1 - prolepticYear, monthOfYear, dayOfMonth);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return ChronoLocalDate.super.with(field, newValue); return super.with(field, newValue);
} }
private HijrahDate resolvePreviousValid(int prolepticYear, int month, int day) { private HijrahDate resolvePreviousValid(int prolepticYear, int month, int day) {
...@@ -479,7 +479,7 @@ public final class HijrahDate ...@@ -479,7 +479,7 @@ public final class HijrahDate
* @return the day-of-year * @return the day-of-year
*/ */
private int getDayOfYear() { private int getDayOfYear() {
return chrono.getDayOfYear(prolepticYear, monthOfYear); return chrono.getDayOfYear(prolepticYear, monthOfYear) + dayOfMonth;
} }
/** /**
...@@ -575,12 +575,13 @@ public final class HijrahDate ...@@ -575,12 +575,13 @@ public final class HijrahDate
} }
@Override // for javadoc and covariant return type @Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<HijrahDate> atTime(LocalTime localTime) { public final ChronoLocalDateTime<HijrahDate> atTime(LocalTime localTime) {
return super.atTime(localTime); return (ChronoLocalDateTime<HijrahDate>)super.atTime(localTime);
} }
@Override @Override
public Period periodUntil(ChronoLocalDate<?> endDate) { public Period until(ChronoLocalDate endDate) {
// TODO: untested // TODO: untested
HijrahDate end = getChronology().date(endDate); HijrahDate end = getChronology().date(endDate);
long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear); // safe long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear); // safe
...@@ -622,7 +623,7 @@ public final class HijrahDate ...@@ -622,7 +623,7 @@ public final class HijrahDate
return this; return this;
} }
static ChronoLocalDate<HijrahDate> readExternal(ObjectInput in) throws IOException, ClassNotFoundException { static HijrahDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
HijrahChronology chrono = (HijrahChronology) in.readObject(); HijrahChronology chrono = (HijrahChronology) in.readObject();
int year = in.readInt(); int year = in.readInt();
int month = in.readByte(); int month = in.readByte();
......
...@@ -61,25 +61,16 @@ ...@@ -61,25 +61,16 @@
*/ */
package java.time.chrono; package java.time.chrono;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.DAY_OF_WEEK;
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import static java.time.temporal.TemporalAdjuster.nextOrSame;
import java.io.Serializable; import java.io.Serializable;
import java.time.Clock; import java.time.Clock;
import java.time.DateTimeException; import java.time.DateTimeException;
import java.time.DayOfWeek;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
...@@ -398,7 +389,7 @@ public final class IsoChronology extends Chronology implements Serializable { ...@@ -398,7 +389,7 @@ public final class IsoChronology extends Chronology implements Serializable {
} }
@Override @Override
public Era eraOf(int eraValue) { public IsoEra eraOf(int eraValue) {
return IsoEra.of(eraValue); return IsoEra.of(eraValue);
} }
...@@ -421,7 +412,7 @@ public final class IsoChronology extends Chronology implements Serializable { ...@@ -421,7 +412,7 @@ public final class IsoChronology extends Chronology implements Serializable {
* as follows. * as follows.
* <ul> * <ul>
* <li>{@code EPOCH_DAY} - If present, this is converted to a {@code LocalDate} * <li>{@code EPOCH_DAY} - If present, this is converted to a {@code LocalDate}
* all other date fields are then cross-checked against the date * and all other date fields are then cross-checked against the date.
* <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the * <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the
* {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart * {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
* then the field is validated. * then the field is validated.
...@@ -430,7 +421,7 @@ public final class IsoChronology extends Chronology implements Serializable { ...@@ -430,7 +421,7 @@ public final class IsoChronology extends Chronology implements Serializable {
* range is not validated, in smart and strict mode it is. The {@code ERA} is * range is not validated, in smart and strict mode it is. The {@code ERA} is
* validated for range in all three modes. If only the {@code YEAR_OF_ERA} is * validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
* present, and the mode is smart or lenient, then the current era (CE/AD) * present, and the mode is smart or lenient, then the current era (CE/AD)
* is assumed. In strict mode, no ers is assumed and the {@code YEAR_OF_ERA} is * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
* left untouched. If only the {@code ERA} is present, then it is left untouched. * left untouched. If only the {@code ERA} is present, then it is left untouched.
* <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} - * <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
* If all three are present, then they are combined to form a {@code LocalDate}. * If all three are present, then they are combined to form a {@code LocalDate}.
...@@ -495,48 +486,11 @@ public final class IsoChronology extends Chronology implements Serializable { ...@@ -495,48 +486,11 @@ public final class IsoChronology extends Chronology implements Serializable {
*/ */
@Override // override for performance @Override // override for performance
public LocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { public LocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
// check epoch-day before inventing era return (LocalDate) super.resolveDate(fieldValues, resolverStyle);
if (fieldValues.containsKey(EPOCH_DAY)) {
return LocalDate.ofEpochDay(fieldValues.remove(EPOCH_DAY));
}
// fix proleptic month before inventing era
resolveProlepticMonth(fieldValues, resolverStyle);
// invent era if necessary to resolve year-of-era
resolveYearOfEra(fieldValues, resolverStyle);
// build date
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
return resolveYMD(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
return resolveYMAA(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(DAY_OF_WEEK)) {
return resolveYMAD(fieldValues, resolverStyle);
}
}
}
if (fieldValues.containsKey(DAY_OF_YEAR)) {
return resolveYD(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
return resolveYAA(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(DAY_OF_WEEK)) {
return resolveYAD(fieldValues, resolverStyle);
}
}
}
return null;
} }
private void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { @Override // override for better proleptic algorithm
void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
Long pMonth = fieldValues.remove(PROLEPTIC_MONTH); Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
if (pMonth != null) { if (pMonth != null) {
if (resolverStyle != ResolverStyle.LENIENT) { if (resolverStyle != ResolverStyle.LENIENT) {
...@@ -547,7 +501,8 @@ public final class IsoChronology extends Chronology implements Serializable { ...@@ -547,7 +501,8 @@ public final class IsoChronology extends Chronology implements Serializable {
} }
} }
private void resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { @Override // override for enhanced behaviour
LocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
Long yoeLong = fieldValues.remove(YEAR_OF_ERA); Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
if (yoeLong != null) { if (yoeLong != null) {
if (resolverStyle != ResolverStyle.LENIENT) { if (resolverStyle != ResolverStyle.LENIENT) {
...@@ -575,10 +530,14 @@ public final class IsoChronology extends Chronology implements Serializable { ...@@ -575,10 +530,14 @@ public final class IsoChronology extends Chronology implements Serializable {
} else { } else {
throw new DateTimeException("Invalid value for era: " + era); throw new DateTimeException("Invalid value for era: " + era);
} }
} else if (fieldValues.containsKey(ERA)) {
ERA.checkValidValue(fieldValues.get(ERA)); // always validated
} }
return null;
} }
private LocalDate resolveYMD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { @Override // override for performance
LocalDate resolveYMD(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) { if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
...@@ -598,96 +557,6 @@ public final class IsoChronology extends Chronology implements Serializable { ...@@ -598,96 +557,6 @@ public final class IsoChronology extends Chronology implements Serializable {
return LocalDate.of(y, moy, dom); return LocalDate.of(y, moy, dom);
} }
private LocalDate resolveYD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) {
long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
return LocalDate.of(y, 1, 1).plusDays(days);
}
int doy = DAY_OF_YEAR.checkValidIntValue(fieldValues.remove(DAY_OF_YEAR));
return LocalDate.ofYearDay(y, doy); // smart is same as strict
}
private LocalDate resolveYMAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1);
return LocalDate.of(y, 1, 1).plusMonths(months).plusWeeks(weeks).plusDays(days);
}
int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR));
int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH));
int ad = ALIGNED_DAY_OF_WEEK_IN_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH));
LocalDate date = LocalDate.of(y, moy, 1).plusDays((aw - 1) * 7 + (ad - 1));
if (resolverStyle == ResolverStyle.STRICT && date.getMonthValue() != moy) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
}
return date;
}
private LocalDate resolveYMAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
return resolveAligned(y, months, weeks, dow);
}
int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR));
int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH));
int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK));
LocalDate date = LocalDate.of(y, moy, 1).plusDays((aw - 1) * 7).with(nextOrSame(DayOfWeek.of(dow)));
if (resolverStyle == ResolverStyle.STRICT && date.getMonthValue() != moy) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
}
return date;
}
private LocalDate resolveYAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) {
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1);
return LocalDate.of(y, 1, 1).plusWeeks(weeks).plusDays(days);
}
int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR));
int ad = ALIGNED_DAY_OF_WEEK_IN_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR));
LocalDate date = LocalDate.of(y, 1, 1).plusDays((aw - 1) * 7 + (ad - 1));
if (resolverStyle == ResolverStyle.STRICT && date.getYear() != y) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
}
return date;
}
private LocalDate resolveYAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) {
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
return resolveAligned(y, 0, weeks, dow);
}
int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR));
int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK));
LocalDate date = LocalDate.of(y, 1, 1).plusDays((aw - 1) * 7).with(nextOrSame(DayOfWeek.of(dow)));
if (resolverStyle == ResolverStyle.STRICT && date.getYear() != y) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
}
return date;
}
private LocalDate resolveAligned(int y, long months, long weeks, long dow) {
LocalDate date = LocalDate.of(y, 1, 1).plusMonths(months).plusWeeks(weeks);
if (dow > 7) {
date = date.plusWeeks((dow - 1) / 7);
dow = ((dow - 1) % 7) + 1;
} else if (dow < 1) {
date = date.plusWeeks(Math.subtractExact(dow, 7) / 7);
dow = ((dow + 6) % 7) + 1;
}
return date.with(nextOrSame(DayOfWeek.of((int) dow)));
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public ValueRange range(ChronoField field) { public ValueRange range(ChronoField field) {
......
...@@ -56,6 +56,15 @@ ...@@ -56,6 +56,15 @@
*/ */
package java.time.chrono; package java.time.chrono;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import java.io.Serializable; import java.io.Serializable;
import java.time.Clock; import java.time.Clock;
import java.time.DateTimeException; import java.time.DateTimeException;
...@@ -63,13 +72,18 @@ import java.time.Instant; ...@@ -63,13 +72,18 @@ import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Year; import java.time.Year;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import sun.util.calendar.CalendarSystem; import sun.util.calendar.CalendarSystem;
import sun.util.calendar.LocalGregorianCalendar; import sun.util.calendar.LocalGregorianCalendar;
...@@ -82,8 +96,22 @@ import sun.util.calendar.LocalGregorianCalendar; ...@@ -82,8 +96,22 @@ import sun.util.calendar.LocalGregorianCalendar;
* The Japanese Imperial calendar system is the same as the ISO calendar system * The Japanese Imperial calendar system is the same as the ISO calendar system
* apart from the era-based year numbering. * apart from the era-based year numbering.
* <p> * <p>
* Only Meiji (1865-04-07 - 1868-09-07) and later eras are supported. * Japan introduced the Gregorian calendar starting with Meiji 6.
* Older eras are handled as an unknown era where the year-of-era is the ISO year. * Only Meiji and later eras are supported;
* dates before Meiji 6, January 1 are not supported.
* <p>
* The supported {@code ChronoField} instances are:
* <ul>
* <li>{@code DAY_OF_WEEK}
* <li>{@code DAY_OF_MONTH}
* <li>{@code DAY_OF_YEAR}
* <li>{@code EPOCH_DAY}
* <li>{@code MONTH_OF_YEAR}
* <li>{@code PROLEPTIC_MONTH}
* <li>{@code YEAR_OF_ERA}
* <li>{@code YEAR}
* <li>{@code ERA}
* </ul>
* *
* @implSpec * @implSpec
* This class is immutable and thread-safe. * This class is immutable and thread-safe.
...@@ -91,7 +119,6 @@ import sun.util.calendar.LocalGregorianCalendar; ...@@ -91,7 +119,6 @@ import sun.util.calendar.LocalGregorianCalendar;
* @since 1.8 * @since 1.8
*/ */
public final class JapaneseChronology extends Chronology implements Serializable { public final class JapaneseChronology extends Chronology implements Serializable {
// TODO: definition for unknown era may break requirement that year-of-era >= 1
static final LocalGregorianCalendar JCAL = static final LocalGregorianCalendar JCAL =
(LocalGregorianCalendar) CalendarSystem.forName("japanese"); (LocalGregorianCalendar) CalendarSystem.forName("japanese");
...@@ -152,6 +179,16 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -152,6 +179,16 @@ public final class JapaneseChronology extends Chronology implements Serializable
/** /**
* Obtains a local date in Japanese calendar system from the * Obtains a local date in Japanese calendar system from the
* era, year-of-era, month-of-year and day-of-month fields. * era, year-of-era, month-of-year and day-of-month fields.
* <p>
* The Japanese month and day-of-month are the same as those in the
* ISO calendar system. They are not reset when the era changes.
* For example:
* <pre>
* 6th Jan Showa 64 = ISO 1989-01-06
* 7th Jan Showa 64 = ISO 1989-01-07
* 8th Jan Heisei 1 = ISO 1989-01-08
* 9th Jan Heisei 1 = ISO 1989-01-09
* </pre>
* *
* @param era the Japanese era, not null * @param era the Japanese era, not null
* @param yearOfEra the year-of-era * @param yearOfEra the year-of-era
...@@ -172,6 +209,9 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -172,6 +209,9 @@ public final class JapaneseChronology extends Chronology implements Serializable
/** /**
* Obtains a local date in Japanese calendar system from the * Obtains a local date in Japanese calendar system from the
* proleptic-year, month-of-year and day-of-month fields. * proleptic-year, month-of-year and day-of-month fields.
* <p>
* The Japanese proleptic year, month and day-of-month are the same as those
* in the ISO calendar system. They are not reset when the era changes.
* *
* @param prolepticYear the proleptic-year * @param prolepticYear the proleptic-year
* @param month the month-of-year * @param month the month-of-year
...@@ -187,6 +227,17 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -187,6 +227,17 @@ public final class JapaneseChronology extends Chronology implements Serializable
/** /**
* Obtains a local date in Japanese calendar system from the * Obtains a local date in Japanese calendar system from the
* era, year-of-era and day-of-year fields. * era, year-of-era and day-of-year fields.
* <p>
* The day-of-year in this factory is expressed relative to the start of the year-of-era.
* This definition changes the normal meaning of day-of-year only in those years
* where the year-of-era is reset to one due to a change in the era.
* For example:
* <pre>
* 6th Jan Showa 64 = day-of-year 6
* 7th Jan Showa 64 = day-of-year 7
* 8th Jan Heisei 1 = day-of-year 1
* 9th Jan Heisei 1 = day-of-year 2
* </pre>
* *
* @param era the Japanese era, not null * @param era the Japanese era, not null
* @param yearOfEra the year-of-era * @param yearOfEra the year-of-era
...@@ -203,6 +254,10 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -203,6 +254,10 @@ public final class JapaneseChronology extends Chronology implements Serializable
/** /**
* Obtains a local date in Japanese calendar system from the * Obtains a local date in Japanese calendar system from the
* proleptic-year and day-of-year fields. * proleptic-year and day-of-year fields.
* <p>
* The day-of-year in this factory is expressed relative to the start of the proleptic year.
* The Japanese proleptic year and day-of-year are the same as those in the ISO calendar system.
* They are not reset when the era changes.
* *
* @param prolepticYear the proleptic-year * @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year * @param dayOfYear the day-of-year
...@@ -211,8 +266,7 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -211,8 +266,7 @@ public final class JapaneseChronology extends Chronology implements Serializable
*/ */
@Override @Override
public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) { public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) {
LocalDate date = LocalDate.ofYearDay(prolepticYear, dayOfYear); return new JapaneseDate(LocalDate.ofYearDay(prolepticYear, dayOfYear));
return date(prolepticYear, date.getMonthValue(), date.getDayOfMonth());
} }
/** /**
...@@ -290,15 +344,6 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -290,15 +344,6 @@ public final class JapaneseChronology extends Chronology implements Serializable
throw new ClassCastException("Era must be JapaneseEra"); throw new ClassCastException("Era must be JapaneseEra");
} }
if (era == JapaneseEra.SEIREKI) {
JapaneseEra nextEra = JapaneseEra.values()[1];
int nextEraYear = nextEra.getPrivateEra().getSinceDate().getYear();
if (yearOfEra >= nextEraYear || yearOfEra < Year.MIN_VALUE) {
throw new DateTimeException("Invalid yearOfEra value");
}
return yearOfEra;
}
JapaneseEra jera = (JapaneseEra) era; JapaneseEra jera = (JapaneseEra) era;
int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1; int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
if (yearOfEra == 1) { if (yearOfEra == 1) {
...@@ -320,14 +365,13 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -320,14 +365,13 @@ public final class JapaneseChronology extends Chronology implements Serializable
* See the description of each Era for the numeric values of: * See the description of each Era for the numeric values of:
* {@link JapaneseEra#HEISEI}, {@link JapaneseEra#SHOWA},{@link JapaneseEra#TAISHO}, * {@link JapaneseEra#HEISEI}, {@link JapaneseEra#SHOWA},{@link JapaneseEra#TAISHO},
* {@link JapaneseEra#MEIJI}), only Meiji and later eras are supported. * {@link JapaneseEra#MEIJI}), only Meiji and later eras are supported.
* Prior to Meiji {@link JapaneseEra#SEIREKI} is used.
* *
* @param eraValue the era value * @param eraValue the era value
* @return the Japanese {@code Era} for the given numeric era value * @return the Japanese {@code Era} for the given numeric era value
* @throws DateTimeException if {@code eraValue} is invalid * @throws DateTimeException if {@code eraValue} is invalid
*/ */
@Override @Override
public Era eraOf(int eraValue) { public JapaneseEra eraOf(int eraValue) {
return JapaneseEra.of(eraValue); return JapaneseEra.of(eraValue);
} }
...@@ -346,49 +390,117 @@ public final class JapaneseChronology extends Chronology implements Serializable ...@@ -346,49 +390,117 @@ public final class JapaneseChronology extends Chronology implements Serializable
@Override @Override
public ValueRange range(ChronoField field) { public ValueRange range(ChronoField field) {
switch (field) { switch (field) {
case YEAR: case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case DAY_OF_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case DAY_OF_WEEK: case ALIGNED_WEEK_OF_MONTH:
case MICRO_OF_DAY: case ALIGNED_WEEK_OF_YEAR:
case MICRO_OF_SECOND: throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
case HOUR_OF_DAY:
case HOUR_OF_AMPM:
case MINUTE_OF_DAY:
case MINUTE_OF_HOUR:
case SECOND_OF_DAY:
case SECOND_OF_MINUTE:
case MILLI_OF_DAY:
case MILLI_OF_SECOND:
case NANO_OF_DAY:
case NANO_OF_SECOND:
case CLOCK_HOUR_OF_DAY:
case CLOCK_HOUR_OF_AMPM:
case EPOCH_DAY:
case PROLEPTIC_MONTH:
case MONTH_OF_YEAR:
return field.range();
case ERA:
return ValueRange.of(JapaneseEra.SEIREKI.getValue(),
getCurrentEra().getValue());
}
Calendar jcal = Calendar.getInstance(LOCALE);
int fieldIndex;
switch (field) {
case YEAR_OF_ERA: { case YEAR_OF_ERA: {
Calendar jcal = Calendar.getInstance(LOCALE);
int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear(); int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear();
return ValueRange.of(Year.MIN_VALUE, jcal.getGreatestMinimum(Calendar.YEAR), return ValueRange.of(1, jcal.getGreatestMinimum(Calendar.YEAR),
jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions
Year.MAX_VALUE - startYear); Year.MAX_VALUE - startYear);
} }
case DAY_OF_YEAR: case DAY_OF_YEAR: {
fieldIndex = Calendar.DAY_OF_YEAR; Calendar jcal = Calendar.getInstance(LOCALE);
break; int fieldIndex = Calendar.DAY_OF_YEAR;
return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex),
jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex));
}
case YEAR:
return ValueRange.of(JapaneseDate.MEIJI_6_ISODATE.getYear(), Year.MAX_VALUE);
case ERA:
return ValueRange.of(JapaneseEra.MEIJI.getValue(), getCurrentEra().getValue());
default: default:
// TODO: review the remaining fields return field.range();
throw new UnsupportedOperationException("Unimplementable field: " + field); }
}
//-----------------------------------------------------------------------
@Override // override for return type
public JapaneseDate resolveDate(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (JapaneseDate) super.resolveDate(fieldValues, resolverStyle);
}
@Override // override for special Japanese behavior
ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
// validate era and year-of-era
Long eraLong = fieldValues.get(ERA);
JapaneseEra era = null;
if (eraLong != null) {
era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); // always validated
}
Long yoeLong = fieldValues.get(YEAR_OF_ERA);
int yoe = 0;
if (yoeLong != null) {
yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); // always validated
}
// if only year-of-era and no year then invent era unless strict
if (era == null && yoeLong != null && fieldValues.containsKey(YEAR) == false && resolverStyle != ResolverStyle.STRICT) {
era = JapaneseEra.values()[JapaneseEra.values().length - 1];
}
// if both present, then try to create date
if (yoeLong != null && era != null) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
return resolveYMD(era, yoe, fieldValues, resolverStyle);
}
}
if (fieldValues.containsKey(DAY_OF_YEAR)) {
return resolveYD(era, yoe, fieldValues, resolverStyle);
}
}
return null;
}
private int prolepticYearLenient(JapaneseEra era, int yearOfEra) {
return era.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
}
private ChronoLocalDate resolveYMD(JapaneseEra era, int yoe, Map<TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
fieldValues.remove(ERA);
fieldValues.remove(YEAR_OF_ERA);
if (resolverStyle == ResolverStyle.LENIENT) {
int y = prolepticYearLenient(era, yoe);
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
}
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
if (resolverStyle == ResolverStyle.SMART) { // previous valid
if (yoe < 1) {
throw new DateTimeException("Invalid YearOfEra: " + yoe);
}
int y = prolepticYearLenient(era, yoe);
JapaneseDate result;
try {
result = date(y, moy, dom);
} catch (DateTimeException ex) {
result = date(y, moy, 1).with(TemporalAdjuster.lastDayOfMonth());
}
// handle the era being changed
// only allow if the new date is in the same Jan-Dec as the era change
// determine by ensuring either original yoe or result yoe is 1
if (result.getEra() != era && result.get(YEAR_OF_ERA) > 1 && yoe > 1) {
throw new DateTimeException("Invalid YearOfEra for Era: " + era + " " + yoe);
}
return result;
}
return date(era, yoe, moy, dom);
}
private ChronoLocalDate resolveYD(JapaneseEra era, int yoe, Map <TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
fieldValues.remove(ERA);
fieldValues.remove(YEAR_OF_ERA);
if (resolverStyle == ResolverStyle.LENIENT) {
int y = prolepticYearLenient(era, yoe);
long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
return dateYearDay(y, 1).plus(days, DAYS);
} }
return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex), int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex)); return dateYearDay(era, yoe, doy); // smart is same as strict
} }
} }
...@@ -56,9 +56,15 @@ ...@@ -56,9 +56,15 @@
*/ */
package java.time.chrono; package java.time.chrono;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
...@@ -96,6 +102,10 @@ import sun.util.calendar.LocalGregorianCalendar; ...@@ -96,6 +102,10 @@ import sun.util.calendar.LocalGregorianCalendar;
* apart from the era-based year numbering. The proleptic-year is defined to be * apart from the era-based year numbering. The proleptic-year is defined to be
* equal to the ISO proleptic-year. * equal to the ISO proleptic-year.
* <p> * <p>
* Japan introduced the Gregorian calendar starting with Meiji 6.
* Only Meiji and later eras are supported;
* dates before Meiji 6, January 1 are not supported.
* <p>
* For example, the Japanese year "Heisei 24" corresponds to ISO year "2012".<br> * For example, the Japanese year "Heisei 24" corresponds to ISO year "2012".<br>
* Calling {@code japaneseDate.get(YEAR_OF_ERA)} will return 24.<br> * Calling {@code japaneseDate.get(YEAR_OF_ERA)} will return 24.<br>
* Calling {@code japaneseDate.get(YEAR)} will return 2012.<br> * Calling {@code japaneseDate.get(YEAR)} will return 2012.<br>
...@@ -109,7 +119,7 @@ import sun.util.calendar.LocalGregorianCalendar; ...@@ -109,7 +119,7 @@ import sun.util.calendar.LocalGregorianCalendar;
*/ */
public final class JapaneseDate public final class JapaneseDate
extends ChronoDateImpl<JapaneseDate> extends ChronoDateImpl<JapaneseDate>
implements ChronoLocalDate<JapaneseDate>, Serializable { implements ChronoLocalDate, Serializable {
/** /**
* Serialization version. * Serialization version.
...@@ -129,6 +139,11 @@ public final class JapaneseDate ...@@ -129,6 +139,11 @@ public final class JapaneseDate
*/ */
private transient int yearOfEra; private transient int yearOfEra;
/**
* The first day supported by the JapaneseChronology is Meiji 6, January 1st.
*/
final static LocalDate MEIJI_6_ISODATE = LocalDate.of(1873, 1, 1);
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Obtains the current {@code JapaneseDate} from the system clock in the default time-zone. * Obtains the current {@code JapaneseDate} from the system clock in the default time-zone.
...@@ -173,7 +188,7 @@ public final class JapaneseDate ...@@ -173,7 +188,7 @@ public final class JapaneseDate
* @throws DateTimeException if the current date cannot be obtained * @throws DateTimeException if the current date cannot be obtained
*/ */
public static JapaneseDate now(Clock clock) { public static JapaneseDate now(Clock clock) {
return JapaneseChronology.INSTANCE.date(LocalDate.now(clock)); return new JapaneseDate(LocalDate.now(clock));
} }
/** /**
...@@ -182,6 +197,16 @@ public final class JapaneseDate ...@@ -182,6 +197,16 @@ public final class JapaneseDate
* <p> * <p>
* This returns a {@code JapaneseDate} with the specified fields. * This returns a {@code JapaneseDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown. * The day must be valid for the year and month, otherwise an exception will be thrown.
* <p>
* The Japanese month and day-of-month are the same as those in the
* ISO calendar system. They are not reset when the era changes.
* For example:
* <pre>
* 6th Jan Showa 64 = ISO 1989-01-06
* 7th Jan Showa 64 = ISO 1989-01-07
* 8th Jan Heisei 1 = ISO 1989-01-08
* 9th Jan Heisei 1 = ISO 1989-01-09
* </pre>
* *
* @param era the Japanese era, not null * @param era the Japanese era, not null
* @param yearOfEra the Japanese year-of-era * @param yearOfEra the Japanese year-of-era
...@@ -192,11 +217,15 @@ public final class JapaneseDate ...@@ -192,11 +217,15 @@ public final class JapaneseDate
* or if the day-of-month is invalid for the month-year, * or if the day-of-month is invalid for the month-year,
* or if the date is not a Japanese era * or if the date is not a Japanese era
*/ */
public static JapaneseDate of(Era era, int yearOfEra, int month, int dayOfMonth) { public static JapaneseDate of(JapaneseEra era, int yearOfEra, int month, int dayOfMonth) {
if (era instanceof JapaneseEra == false) { Objects.requireNonNull(era, "era");
throw new ClassCastException("Era must be JapaneseEra"); LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
jdate.setEra(era.getPrivateEra()).setDate(yearOfEra, month, dayOfMonth);
if (!JapaneseChronology.JCAL.validate(jdate)) {
throw new DateTimeException("year, month, and day not valid for Era");
} }
return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth); LocalDate date = LocalDate.of(jdate.getNormalizedYear(), month, dayOfMonth);
return new JapaneseDate(era, yearOfEra, date);
} }
/** /**
...@@ -205,6 +234,9 @@ public final class JapaneseDate ...@@ -205,6 +234,9 @@ public final class JapaneseDate
* <p> * <p>
* This returns a {@code JapaneseDate} with the specified fields. * This returns a {@code JapaneseDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown. * The day must be valid for the year and month, otherwise an exception will be thrown.
* <p>
* The Japanese proleptic year, month and day-of-month are the same as those
* in the ISO calendar system. They are not reset when the era changes.
* *
* @param prolepticYear the Japanese proleptic-year * @param prolepticYear the Japanese proleptic-year
* @param month the Japanese month-of-year, from 1 to 12 * @param month the Japanese month-of-year, from 1 to 12
...@@ -219,23 +251,31 @@ public final class JapaneseDate ...@@ -219,23 +251,31 @@ public final class JapaneseDate
/** /**
* Obtains a {@code JapaneseDate} representing a date in the Japanese calendar * Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
* system from the proleptic-year and day-of-year fields. * system from the era, year-of-era and day-of-year fields.
* <p> * <p>
* This returns a {@code JapaneseDate} with the specified fields. * This returns a {@code JapaneseDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown. * The day must be valid for the year, otherwise an exception will be thrown.
* <p>
* The day-of-year in this factory is expressed relative to the start of the year-of-era.
* This definition changes the normal meaning of day-of-year only in those years
* where the year-of-era is reset to one due to a change in the era.
* For example:
* <pre>
* 6th Jan Showa 64 = day-of-year 6
* 7th Jan Showa 64 = day-of-year 7
* 8th Jan Heisei 1 = day-of-year 1
* 9th Jan Heisei 1 = day-of-year 2
* </pre>
* *
* @param prolepticYear the chronology proleptic-year * @param era the Japanese era, not null
* @param yearOfEra the Japanese year-of-era
* @param dayOfYear the chronology day-of-year, from 1 to 366 * @param dayOfYear the chronology day-of-year, from 1 to 366
* @return the date in Japanese calendar system, not null * @return the date in Japanese calendar system, not null
* @throws DateTimeException if the value of any field is out of range, * @throws DateTimeException if the value of any field is out of range,
* or if the day-of-year is invalid for the year * or if the day-of-year is invalid for the year
*/ */
public static JapaneseDate ofYearDay(int prolepticYear, int dayOfYear) {
LocalDate date = LocalDate.ofYearDay(prolepticYear, dayOfYear);
return of(prolepticYear, date.getMonthValue(), date.getDayOfMonth());
}
static JapaneseDate ofYearDay(JapaneseEra era, int yearOfEra, int dayOfYear) { static JapaneseDate ofYearDay(JapaneseEra era, int yearOfEra, int dayOfYear) {
Objects.requireNonNull(era, "era");
CalendarDate firstDay = era.getPrivateEra().getSinceDate(); CalendarDate firstDay = era.getPrivateEra().getSinceDate();
LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null); LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
jdate.setEra(era.getPrivateEra()); jdate.setEra(era.getPrivateEra());
...@@ -253,32 +293,6 @@ public final class JapaneseDate ...@@ -253,32 +293,6 @@ public final class JapaneseDate
return new JapaneseDate(era, yearOfEra, localdate); return new JapaneseDate(era, yearOfEra, localdate);
} }
/**
* Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
* system from the era, year-of-era, month-of-year and day-of-month fields.
* <p>
* This returns a {@code JapaneseDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param era the Japanese era, not null
* @param yearOfEra the Japanese year-of-era
* @param month the Japanese month-of-year, from 1 to 12
* @param dayOfMonth the Japanese day-of-month, from 1 to 31
* @return the date in Japanese calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* or if the day-of-month is invalid for the month-year
*/
static JapaneseDate of(JapaneseEra era, int yearOfEra, int month, int dayOfMonth) {
Objects.requireNonNull(era, "era");
LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
jdate.setEra(era.getPrivateEra()).setDate(yearOfEra, month, dayOfMonth);
if (!JapaneseChronology.JCAL.validate(jdate)) {
throw new DateTimeException("year, month, and day not valid for Era");
}
LocalDate date = LocalDate.of(jdate.getNormalizedYear(), month, dayOfMonth);
return new JapaneseDate(era, yearOfEra, date);
}
/** /**
* Obtains a {@code JapaneseDate} from a temporal object. * Obtains a {@code JapaneseDate} from a temporal object.
* <p> * <p>
...@@ -307,6 +321,9 @@ public final class JapaneseDate ...@@ -307,6 +321,9 @@ public final class JapaneseDate
* @param isoDate the standard local date, validated not null * @param isoDate the standard local date, validated not null
*/ */
JapaneseDate(LocalDate isoDate) { JapaneseDate(LocalDate isoDate) {
if (isoDate.isBefore(MEIJI_6_ISODATE)) {
throw new DateTimeException("JapaneseDate before Meiji 6 is not supported");
}
LocalGregorianCalendar.Date jdate = toPrivateJapaneseDate(isoDate); LocalGregorianCalendar.Date jdate = toPrivateJapaneseDate(isoDate);
this.era = JapaneseEra.toJapaneseEra(jdate.getEra()); this.era = JapaneseEra.toJapaneseEra(jdate.getEra());
this.yearOfEra = jdate.getYear(); this.yearOfEra = jdate.getYear();
...@@ -322,6 +339,9 @@ public final class JapaneseDate ...@@ -322,6 +339,9 @@ public final class JapaneseDate
* @param isoDate the standard local date, validated not null * @param isoDate the standard local date, validated not null
*/ */
JapaneseDate(JapaneseEra era, int year, LocalDate isoDate) { JapaneseDate(JapaneseEra era, int year, LocalDate isoDate) {
if (isoDate.isBefore(MEIJI_6_ISODATE)) {
throw new DateTimeException("JapaneseDate before Meiji 6 is not supported");
}
this.era = era; this.era = era;
this.yearOfEra = year; this.yearOfEra = year;
this.isoDate = isoDate; this.isoDate = isoDate;
...@@ -366,55 +386,99 @@ public final class JapaneseDate ...@@ -366,55 +386,99 @@ public final class JapaneseDate
return isoDate.lengthOfMonth(); return isoDate.lengthOfMonth();
} }
@Override
public int lengthOfYear() {
Calendar jcal = Calendar.getInstance(JapaneseChronology.LOCALE);
jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
return jcal.getActualMaximum(Calendar.DAY_OF_YEAR);
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
* <p>
* This checks if this date can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and
* {@link #get(TemporalField) get} methods will throw an exception.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
* <ul>
* <li>{@code DAY_OF_WEEK}
* <li>{@code DAY_OF_MONTH}
* <li>{@code DAY_OF_YEAR}
* <li>{@code EPOCH_DAY}
* <li>{@code MONTH_OF_YEAR}
* <li>{@code PROLEPTIC_MONTH}
* <li>{@code YEAR_OF_ERA}
* <li>{@code YEAR}
* <li>{@code ERA}
* </ul>
* All other {@code ChronoField} instances will return false.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field is supported on this date, false if not
*/
@Override
public boolean isSupported(TemporalField field) {
if (field == ALIGNED_DAY_OF_WEEK_IN_MONTH || field == ALIGNED_DAY_OF_WEEK_IN_YEAR ||
field == ALIGNED_WEEK_OF_MONTH || field == ALIGNED_WEEK_OF_YEAR) {
return false;
}
return ChronoLocalDate.super.isSupported(field);
}
@Override @Override
public ValueRange range(TemporalField field) { public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) { if (field instanceof ChronoField) {
if (isSupported(field)) { if (isSupported(field)) {
ChronoField f = (ChronoField) field; ChronoField f = (ChronoField) field;
switch (f) { switch (f) {
case DAY_OF_MONTH: case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth());
case ALIGNED_WEEK_OF_MONTH: case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear());
return isoDate.range(field); case YEAR_OF_ERA: {
case DAY_OF_YEAR: Calendar jcal = Calendar.getInstance(JapaneseChronology.LOCALE);
return actualRange(Calendar.DAY_OF_YEAR); jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
case YEAR_OF_ERA: jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
return actualRange(Calendar.YEAR); return ValueRange.of(1, jcal.getActualMaximum(Calendar.YEAR));
}
} }
return getChronology().range(f); return getChronology().range(f);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.rangeRefinedBy(this); return field.rangeRefinedBy(this);
} }
private ValueRange actualRange(int calendarField) {
Calendar jcal = Calendar.getInstance(JapaneseChronology.LOCALE);
jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET); // TODO: cannot calculate this way for SEIREKI
jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
return ValueRange.of(jcal.getActualMinimum(calendarField),
jcal.getActualMaximum(calendarField));
}
@Override @Override
public long getLong(TemporalField field) { public long getLong(TemporalField field) {
if (field instanceof ChronoField) { if (field instanceof ChronoField) {
// same as ISO: // same as ISO:
// DAY_OF_WEEK, ALIGNED_DAY_OF_WEEK_IN_MONTH, DAY_OF_MONTH, EPOCH_DAY, // DAY_OF_WEEK, DAY_OF_MONTH, EPOCH_DAY, MONTH_OF_YEAR, PROLEPTIC_MONTH, YEAR
// ALIGNED_WEEK_OF_MONTH, MONTH_OF_YEAR, PROLEPTIC_MONTH, YEAR
// //
// calendar specific fields // calendar specific fields
// ALIGNED_DAY_OF_WEEK_IN_YEAR, DAY_OF_YEAR, ALIGNED_WEEK_OF_YEAR, YEAR_OF_ERA, ERA // DAY_OF_YEAR, YEAR_OF_ERA, ERA
switch ((ChronoField) field) { switch ((ChronoField) field) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case ALIGNED_WEEK_OF_MONTH:
case ALIGNED_WEEK_OF_YEAR:
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
case YEAR_OF_ERA: case YEAR_OF_ERA:
return yearOfEra; return yearOfEra;
case ERA: case ERA:
return era.getValue(); return era.getValue();
case DAY_OF_YEAR: { case DAY_OF_YEAR:
LocalGregorianCalendar.Date jdate = toPrivateJapaneseDate(isoDate); Calendar jcal = Calendar.getInstance(JapaneseChronology.LOCALE);
return JapaneseChronology.JCAL.getDayOfYear(jdate); jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
} jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
// TODO: ALIGNED_DAY_OF_WEEK_IN_YEAR and ALIGNED_WEEK_OF_YEAR ??? return jcal.get(Calendar.DAY_OF_YEAR);
} }
return isoDate.getLong(field); return isoDate.getLong(field);
} }
...@@ -444,7 +508,7 @@ public final class JapaneseDate ...@@ -444,7 +508,7 @@ public final class JapaneseDate
public JapaneseDate with(TemporalField field, long newValue) { public JapaneseDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) { if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field; ChronoField f = (ChronoField) field;
if (getLong(f) == newValue) { if (getLong(f) == newValue) { // getLong() validates for supported fields
return this; return this;
} }
switch (f) { switch (f) {
...@@ -464,10 +528,9 @@ public final class JapaneseDate ...@@ -464,10 +528,9 @@ public final class JapaneseDate
} }
} }
// YEAR, PROLEPTIC_MONTH and others are same as ISO // YEAR, PROLEPTIC_MONTH and others are same as ISO
// TODO: review other fields, such as WEEK_OF_YEAR
return with(isoDate.with(field, newValue)); return with(isoDate.with(field, newValue));
} }
return ChronoLocalDate.super.with(field, newValue); return super.with(field, newValue);
} }
/** /**
...@@ -592,13 +655,14 @@ public final class JapaneseDate ...@@ -592,13 +655,14 @@ public final class JapaneseDate
} }
@Override // for javadoc and covariant return type @Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<JapaneseDate> atTime(LocalTime localTime) { public final ChronoLocalDateTime<JapaneseDate> atTime(LocalTime localTime) {
return super.atTime(localTime); return (ChronoLocalDateTime<JapaneseDate>)super.atTime(localTime);
} }
@Override @Override
public Period periodUntil(ChronoLocalDate<?> endDate) { public Period until(ChronoLocalDate endDate) {
return isoDate.periodUntil(endDate); return isoDate.until(endDate);
} }
@Override // override for performance @Override // override for performance
...@@ -624,14 +688,6 @@ public final class JapaneseDate ...@@ -624,14 +688,6 @@ public final class JapaneseDate
return getChronology().getId().hashCode() ^ isoDate.hashCode(); return getChronology().getId().hashCode() ^ isoDate.hashCode();
} }
@Override
public String toString() {
if (era == JapaneseEra.SEIREKI) {
return getChronology().getId() + " " + isoDate.toString();
}
return super.toString();
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
private Object writeReplace() { private Object writeReplace() {
return new Ser(Ser.JAPANESE_DATE_TYPE, this); return new Ser(Ser.JAPANESE_DATE_TYPE, this);
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
*/ */
package java.time.chrono; package java.time.chrono;
import static java.time.chrono.JapaneseDate.MEIJI_6_ISODATE;
import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.ERA;
import java.io.DataInput; import java.io.DataInput;
...@@ -84,12 +85,9 @@ import sun.util.calendar.CalendarDate; ...@@ -84,12 +85,9 @@ import sun.util.calendar.CalendarDate;
* An era in the Japanese Imperial calendar system. * An era in the Japanese Imperial calendar system.
* <p> * <p>
* This class defines the valid eras for the Japanese chronology. * This class defines the valid eras for the Japanese chronology.
* Only Meiji (1868-09-08 - 1912-07-29) and later eras are supported. * Japan introduced the Gregorian calendar starting with Meiji 6.
* Japan introduced the Gregorian calendar since Meiji 6. The dates * Only Meiji and later eras are supported;
* between Meiji 1 - 5 are not historically correct. * dates before Meiji 6, January 1 are not supported.
* The older eras are recognized as Seireki (Western calendar) era,
* and the year of era of Seireki is proleptic Gregorian year.
* (The Julian to Gregorian transition is not supported.)
* *
* @implSpec * @implSpec
* This class is immutable and thread-safe. * This class is immutable and thread-safe.
...@@ -100,16 +98,11 @@ public final class JapaneseEra ...@@ -100,16 +98,11 @@ public final class JapaneseEra
implements Era, Serializable { implements Era, Serializable {
// The offset value to 0-based index from the era value. // The offset value to 0-based index from the era value.
// i.e., getValue() + ERA_OFFSET == 0-based index; except that -999 is mapped to zero // i.e., getValue() + ERA_OFFSET == 0-based index
static final int ERA_OFFSET = 2; static final int ERA_OFFSET = 2;
static final sun.util.calendar.Era[] ERA_CONFIG; static final sun.util.calendar.Era[] ERA_CONFIG;
/**
* The singleton instance for the before Meiji era ( - 1868-09-07)
* which has the value -999.
*/
public static final JapaneseEra SEIREKI = new JapaneseEra(-999, LocalDate.MIN);
/** /**
* The singleton instance for the 'Meiji' era (1868-09-08 - 1912-07-29) * The singleton instance for the 'Meiji' era (1868-09-08 - 1912-07-29)
* which has the value -1. * which has the value -1.
...@@ -144,17 +137,13 @@ public final class JapaneseEra ...@@ -144,17 +137,13 @@ public final class JapaneseEra
private static final JapaneseEra[] KNOWN_ERAS; private static final JapaneseEra[] KNOWN_ERAS;
static { static {
sun.util.calendar.Era[] sunEras = JapaneseChronology.JCAL.getEras(); ERA_CONFIG = JapaneseChronology.JCAL.getEras();
ERA_CONFIG = new sun.util.calendar.Era[sunEras.length + 1];
for (int i = 1; i < ERA_CONFIG.length; i++) {
ERA_CONFIG[i] = sunEras[i - 1];
}
KNOWN_ERAS = new JapaneseEra[ERA_CONFIG.length]; KNOWN_ERAS = new JapaneseEra[ERA_CONFIG.length];
KNOWN_ERAS[0] = SEIREKI; KNOWN_ERAS[0] = MEIJI;
KNOWN_ERAS[1] = MEIJI; KNOWN_ERAS[1] = TAISHO;
KNOWN_ERAS[2] = TAISHO; KNOWN_ERAS[2] = SHOWA;
KNOWN_ERAS[3] = SHOWA; KNOWN_ERAS[3] = HEISEI;
KNOWN_ERAS[4] = HEISEI;
for (int i = N_ERA_CONSTANTS; i < ERA_CONFIG.length; i++) { for (int i = N_ERA_CONSTANTS; i < ERA_CONFIG.length; i++) {
CalendarDate date = ERA_CONFIG[i].getSinceDate(); CalendarDate date = ERA_CONFIG[i].getSinceDate();
LocalDate isoDate = LocalDate.of(date.getYear(), date.getMonth(), date.getDayOfMonth()); LocalDate isoDate = LocalDate.of(date.getYear(), date.getMonth(), date.getDayOfMonth());
...@@ -203,10 +192,8 @@ public final class JapaneseEra ...@@ -203,10 +192,8 @@ public final class JapaneseEra
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Returns the Sun private Era instance corresponding to this {@code JapaneseEra}. * Returns the Sun private Era instance corresponding to this {@code JapaneseEra}.
* SEIREKI doesn't have its corresponding one.
* *
* @return the Sun private Era instance for this {@code JapaneseEra}, * @return the Sun private Era instance for this {@code JapaneseEra}.
* or null for SEIREKI.
*/ */
sun.util.calendar.Era getPrivateEra() { sun.util.calendar.Era getPrivateEra() {
return ERA_CONFIG[ordinal(eraValue)]; return ERA_CONFIG[ordinal(eraValue)];
...@@ -218,16 +205,14 @@ public final class JapaneseEra ...@@ -218,16 +205,14 @@ public final class JapaneseEra
* <p> * <p>
* The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1 * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1
* Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}), * Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}),
* -1 ({@link #MEIJI}), only Meiji and later eras are supported. The prior to Meiji, * -1 ({@link #MEIJI}), only Meiji and later eras are supported.
* {@link #SEIREKI} is used.
* *
* @param japaneseEra the era to represent * @param japaneseEra the era to represent
* @return the {@code JapaneseEra} singleton, not null * @return the {@code JapaneseEra} singleton, not null
* @throws DateTimeException if the value is invalid * @throws DateTimeException if the value is invalid
*/ */
public static JapaneseEra of(int japaneseEra) { public static JapaneseEra of(int japaneseEra) {
if (japaneseEra != SEIREKI.eraValue && if (japaneseEra < MEIJI.eraValue || japaneseEra > HEISEI.eraValue) {
(japaneseEra < MEIJI.eraValue || japaneseEra > HEISEI.eraValue)) {
throw new DateTimeException("Invalid era: " + japaneseEra); throw new DateTimeException("Invalid era: " + japaneseEra);
} }
return KNOWN_ERAS[ordinal(japaneseEra)]; return KNOWN_ERAS[ordinal(japaneseEra)];
...@@ -276,22 +261,25 @@ public final class JapaneseEra ...@@ -276,22 +261,25 @@ public final class JapaneseEra
* @return the Era singleton, never null * @return the Era singleton, never null
*/ */
static JapaneseEra from(LocalDate date) { static JapaneseEra from(LocalDate date) {
if (date.isBefore(MEIJI_6_ISODATE)) {
throw new DateTimeException("JapaneseDate before Meiji 6 are not supported");
}
for (int i = KNOWN_ERAS.length - 1; i > 0; i--) { for (int i = KNOWN_ERAS.length - 1; i > 0; i--) {
JapaneseEra era = KNOWN_ERAS[i]; JapaneseEra era = KNOWN_ERAS[i];
if (date.compareTo(era.since) >= 0) { if (date.compareTo(era.since) >= 0) {
return era; return era;
} }
} }
return SEIREKI; return null;
} }
static JapaneseEra toJapaneseEra(sun.util.calendar.Era privateEra) { static JapaneseEra toJapaneseEra(sun.util.calendar.Era privateEra) {
for (int i = ERA_CONFIG.length - 1; i > 0; i--) { for (int i = ERA_CONFIG.length - 1; i >= 0; i--) {
if (ERA_CONFIG[i].equals(privateEra)) { if (ERA_CONFIG[i].equals(privateEra)) {
return KNOWN_ERAS[i]; return KNOWN_ERAS[i];
} }
} }
return SEIREKI; return null;
} }
static sun.util.calendar.Era privateEraFrom(LocalDate isoDate) { static sun.util.calendar.Era privateEraFrom(LocalDate isoDate) {
...@@ -306,13 +294,13 @@ public final class JapaneseEra ...@@ -306,13 +294,13 @@ public final class JapaneseEra
/** /**
* Returns the index into the arrays from the Era value. * Returns the index into the arrays from the Era value.
* the eraValue is a valid Era number, -999, -1..2. * the eraValue is a valid Era number, -1..2.
* *
* @param eraValue the era value to convert to the index * @param eraValue the era value to convert to the index
* @return the index of the current Era * @return the index of the current Era
*/ */
private static int ordinal(int eraValue) { private static int ordinal(int eraValue) {
return (eraValue == SEIREKI.eraValue) ? 0 : eraValue + ERA_OFFSET; return eraValue + ERA_OFFSET - 1;
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -321,7 +309,7 @@ public final class JapaneseEra ...@@ -321,7 +309,7 @@ public final class JapaneseEra
* <p> * <p>
* The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1. * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1.
* Later eras are numbered from 2 ({@link #HEISEI}). * Later eras are numbered from 2 ({@link #HEISEI}).
* Earlier eras are numbered 0 ({@link #TAISHO}), -1 ({@link #MEIJI}), and -999 ({@link #SEIREKI}). * Earlier eras are numbered 0 ({@link #TAISHO}), -1 ({@link #MEIJI})).
* *
* @return the era value * @return the era value
*/ */
...@@ -374,11 +362,7 @@ public final class JapaneseEra ...@@ -374,11 +362,7 @@ public final class JapaneseEra
} }
String getName() { String getName() {
int index = ordinal(getValue()); return ERA_CONFIG[ordinal(getValue())].getName();
if (index == 0) {
return "Seireki";
}
return ERA_CONFIG[index].getName();
} }
@Override @Override
......
...@@ -65,12 +65,15 @@ import java.time.DateTimeException; ...@@ -65,12 +65,15 @@ import java.time.DateTimeException;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
/** /**
* The Minguo calendar system. * The Minguo calendar system.
...@@ -253,16 +256,19 @@ public final class MinguoChronology extends Chronology implements Serializable { ...@@ -253,16 +256,19 @@ public final class MinguoChronology extends Chronology implements Serializable {
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime<MinguoDate> localDateTime(TemporalAccessor temporal) { public ChronoLocalDateTime<MinguoDate> localDateTime(TemporalAccessor temporal) {
return (ChronoLocalDateTime<MinguoDate>)super.localDateTime(temporal); return (ChronoLocalDateTime<MinguoDate>)super.localDateTime(temporal);
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<MinguoDate> zonedDateTime(TemporalAccessor temporal) { public ChronoZonedDateTime<MinguoDate> zonedDateTime(TemporalAccessor temporal) {
return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(temporal); return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(temporal);
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<MinguoDate> zonedDateTime(Instant instant, ZoneId zone) { public ChronoZonedDateTime<MinguoDate> zonedDateTime(Instant instant, ZoneId zone) {
return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(instant, zone); return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(instant, zone);
} }
...@@ -292,7 +298,7 @@ public final class MinguoChronology extends Chronology implements Serializable { ...@@ -292,7 +298,7 @@ public final class MinguoChronology extends Chronology implements Serializable {
} }
@Override @Override
public Era eraOf(int eraValue) { public MinguoEra eraOf(int eraValue) {
return MinguoEra.of(eraValue); return MinguoEra.of(eraValue);
} }
...@@ -321,4 +327,10 @@ public final class MinguoChronology extends Chronology implements Serializable { ...@@ -321,4 +327,10 @@ public final class MinguoChronology extends Chronology implements Serializable {
return field.range(); return field.range();
} }
//-----------------------------------------------------------------------
@Override // override for return type
public MinguoDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (MinguoDate) super.resolveDate(fieldValues, resolverStyle);
}
} }
...@@ -96,7 +96,7 @@ import java.util.Objects; ...@@ -96,7 +96,7 @@ import java.util.Objects;
*/ */
public final class MinguoDate public final class MinguoDate
extends ChronoDateImpl<MinguoDate> extends ChronoDateImpl<MinguoDate>
implements ChronoLocalDate<MinguoDate>, Serializable { implements ChronoLocalDate, Serializable {
/** /**
* Serialization version. * Serialization version.
...@@ -152,7 +152,7 @@ public final class MinguoDate ...@@ -152,7 +152,7 @@ public final class MinguoDate
* @throws DateTimeException if the current date cannot be obtained * @throws DateTimeException if the current date cannot be obtained
*/ */
public static MinguoDate now(Clock clock) { public static MinguoDate now(Clock clock) {
return MinguoChronology.INSTANCE.date(LocalDate.now(clock)); return new MinguoDate(LocalDate.now(clock));
} }
/** /**
...@@ -264,7 +264,7 @@ public final class MinguoDate ...@@ -264,7 +264,7 @@ public final class MinguoDate
} }
return getChronology().range(f); return getChronology().range(f);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.rangeRefinedBy(this); return field.rangeRefinedBy(this);
} }
...@@ -325,7 +325,7 @@ public final class MinguoDate ...@@ -325,7 +325,7 @@ public final class MinguoDate
} }
return with(isoDate.with(field, newValue)); return with(isoDate.with(field, newValue));
} }
return ChronoLocalDate.super.with(field, newValue); return super.with(field, newValue);
} }
/** /**
...@@ -369,6 +369,11 @@ public final class MinguoDate ...@@ -369,6 +369,11 @@ public final class MinguoDate
return with(isoDate.plusMonths(months)); return with(isoDate.plusMonths(months));
} }
@Override
MinguoDate plusWeeks(long weeksToAdd) {
return super.plusWeeks(weeksToAdd);
}
@Override @Override
MinguoDate plusDays(long days) { MinguoDate plusDays(long days) {
return with(isoDate.plusDays(days)); return with(isoDate.plusDays(days));
...@@ -384,11 +389,6 @@ public final class MinguoDate ...@@ -384,11 +389,6 @@ public final class MinguoDate
return super.minus(amountToAdd, unit); return super.minus(amountToAdd, unit);
} }
@Override
MinguoDate plusWeeks(long weeksToAdd) {
return super.plusWeeks(weeksToAdd);
}
@Override @Override
MinguoDate minusYears(long yearsToSubtract) { MinguoDate minusYears(long yearsToSubtract) {
return super.minusYears(yearsToSubtract); return super.minusYears(yearsToSubtract);
...@@ -414,13 +414,14 @@ public final class MinguoDate ...@@ -414,13 +414,14 @@ public final class MinguoDate
} }
@Override // for javadoc and covariant return type @Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<MinguoDate> atTime(LocalTime localTime) { public final ChronoLocalDateTime<MinguoDate> atTime(LocalTime localTime) {
return super.atTime(localTime); return (ChronoLocalDateTime<MinguoDate>)super.atTime(localTime);
} }
@Override @Override
public Period periodUntil(ChronoLocalDate<?> endDate) { public Period until(ChronoLocalDate endDate) {
return isoDate.periodUntil(endDate); return isoDate.until(endDate);
} }
@Override // override for performance @Override // override for performance
...@@ -458,7 +459,7 @@ public final class MinguoDate ...@@ -458,7 +459,7 @@ public final class MinguoDate
out.writeByte(get(DAY_OF_MONTH)); out.writeByte(get(DAY_OF_MONTH));
} }
static ChronoLocalDate<?> readExternal(DataInput in) throws IOException { static MinguoDate readExternal(DataInput in) throws IOException {
int year = in.readInt(); int year = in.readInt();
int month = in.readByte(); int month = in.readByte();
int dayOfMonth = in.readByte(); int dayOfMonth = in.readByte();
......
...@@ -65,13 +65,16 @@ import java.time.DateTimeException; ...@@ -65,13 +65,16 @@ import java.time.DateTimeException;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange; import java.time.temporal.ValueRange;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
/** /**
* The Thai Buddhist calendar system. * The Thai Buddhist calendar system.
...@@ -289,16 +292,19 @@ public final class ThaiBuddhistChronology extends Chronology implements Serializ ...@@ -289,16 +292,19 @@ public final class ThaiBuddhistChronology extends Chronology implements Serializ
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime<ThaiBuddhistDate> localDateTime(TemporalAccessor temporal) { public ChronoLocalDateTime<ThaiBuddhistDate> localDateTime(TemporalAccessor temporal) {
return (ChronoLocalDateTime<ThaiBuddhistDate>)super.localDateTime(temporal); return (ChronoLocalDateTime<ThaiBuddhistDate>)super.localDateTime(temporal);
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(TemporalAccessor temporal) { public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(TemporalAccessor temporal) {
return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(temporal); return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(temporal);
} }
@Override @Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(Instant instant, ZoneId zone) { public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(Instant instant, ZoneId zone) {
return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(instant, zone); return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(instant, zone);
} }
...@@ -328,7 +334,7 @@ public final class ThaiBuddhistChronology extends Chronology implements Serializ ...@@ -328,7 +334,7 @@ public final class ThaiBuddhistChronology extends Chronology implements Serializ
} }
@Override @Override
public Era eraOf(int eraValue) { public ThaiBuddhistEra eraOf(int eraValue) {
return ThaiBuddhistEra.of(eraValue); return ThaiBuddhistEra.of(eraValue);
} }
...@@ -357,4 +363,10 @@ public final class ThaiBuddhistChronology extends Chronology implements Serializ ...@@ -357,4 +363,10 @@ public final class ThaiBuddhistChronology extends Chronology implements Serializ
return field.range(); return field.range();
} }
//-----------------------------------------------------------------------
@Override // override for return type
public ThaiBuddhistDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (ThaiBuddhistDate) super.resolveDate(fieldValues, resolverStyle);
}
} }
...@@ -96,7 +96,7 @@ import java.util.Objects; ...@@ -96,7 +96,7 @@ import java.util.Objects;
*/ */
public final class ThaiBuddhistDate public final class ThaiBuddhistDate
extends ChronoDateImpl<ThaiBuddhistDate> extends ChronoDateImpl<ThaiBuddhistDate>
implements ChronoLocalDate<ThaiBuddhistDate>, Serializable { implements ChronoLocalDate, Serializable {
/** /**
* Serialization version. * Serialization version.
...@@ -152,7 +152,7 @@ public final class ThaiBuddhistDate ...@@ -152,7 +152,7 @@ public final class ThaiBuddhistDate
* @throws DateTimeException if the current date cannot be obtained * @throws DateTimeException if the current date cannot be obtained
*/ */
public static ThaiBuddhistDate now(Clock clock) { public static ThaiBuddhistDate now(Clock clock) {
return ThaiBuddhistChronology.INSTANCE.date(LocalDate.now(clock)); return new ThaiBuddhistDate(LocalDate.now(clock));
} }
/** /**
...@@ -264,7 +264,7 @@ public final class ThaiBuddhistDate ...@@ -264,7 +264,7 @@ public final class ThaiBuddhistDate
} }
return getChronology().range(f); return getChronology().range(f);
} }
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.rangeRefinedBy(this); return field.rangeRefinedBy(this);
} }
...@@ -325,7 +325,7 @@ public final class ThaiBuddhistDate ...@@ -325,7 +325,7 @@ public final class ThaiBuddhistDate
} }
return with(isoDate.with(field, newValue)); return with(isoDate.with(field, newValue));
} }
return ChronoLocalDate.super.with(field, newValue); return super.with(field, newValue);
} }
/** /**
...@@ -414,13 +414,14 @@ public final class ThaiBuddhistDate ...@@ -414,13 +414,14 @@ public final class ThaiBuddhistDate
} }
@Override // for javadoc and covariant return type @Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<ThaiBuddhistDate> atTime(LocalTime localTime) { public final ChronoLocalDateTime<ThaiBuddhistDate> atTime(LocalTime localTime) {
return super.atTime(localTime); return (ChronoLocalDateTime<ThaiBuddhistDate>) super.atTime(localTime);
} }
@Override @Override
public Period periodUntil(ChronoLocalDate<?> endDate) { public Period until(ChronoLocalDate endDate) {
return isoDate.periodUntil(endDate); return isoDate.until(endDate);
} }
@Override // override for performance @Override // override for performance
......
...@@ -103,7 +103,7 @@ ...@@ -103,7 +103,7 @@
* // Enumerate the list of available calendars and print todays date for each. * // Enumerate the list of available calendars and print todays date for each.
* Set&lt;Chronology&gt; chronos = Chronology.getAvailableChronologies(); * Set&lt;Chronology&gt; chronos = Chronology.getAvailableChronologies();
* for (Chronology chrono : chronos) { * for (Chronology chrono : chronos) {
* ChronoLocalDate&lt;?&gt; date = chrono.dateNow(); * ChronoLocalDate date = chrono.dateNow();
* System.out.printf(" %20s: %s%n", chrono.getId(), date.toString()); * System.out.printf(" %20s: %s%n", chrono.getId(), date.toString());
* } * }
* </pre> * </pre>
...@@ -113,7 +113,7 @@ ...@@ -113,7 +113,7 @@
* </p> * </p>
* <pre> * <pre>
* // Print the Thai Buddhist date * // Print the Thai Buddhist date
* ChronoLocalDate&lt;?&gt; now1 = Chronology.of("ThaiBuddhist").dateNow(); * ChronoLocalDate now1 = Chronology.of("ThaiBuddhist").dateNow();
* int day = now1.get(ChronoField.DAY_OF_MONTH); * int day = now1.get(ChronoField.DAY_OF_MONTH);
* int dow = now1.get(ChronoField.DAY_OF_WEEK); * int dow = now1.get(ChronoField.DAY_OF_WEEK);
* int month = now1.get(ChronoField.MONTH_OF_YEAR); * int month = now1.get(ChronoField.MONTH_OF_YEAR);
...@@ -121,10 +121,10 @@ ...@@ -121,10 +121,10 @@
* System.out.printf(" Today is %s %s %d-%s-%d%n", now1.getChronology().getId(), * System.out.printf(" Today is %s %s %d-%s-%d%n", now1.getChronology().getId(),
* dow, day, month, year); * dow, day, month, year);
* // Print today's date and the last day of the year for the Thai Buddhist Calendar. * // Print today's date and the last day of the year for the Thai Buddhist Calendar.
* ChronoLocalDate&lt;?&gt; first = now1 * ChronoLocalDate first = now1
* .with(ChronoField.DAY_OF_MONTH, 1) * .with(ChronoField.DAY_OF_MONTH, 1)
* .with(ChronoField.MONTH_OF_YEAR, 1); * .with(ChronoField.MONTH_OF_YEAR, 1);
* ChronoLocalDate&lt;?&gt; last = first * ChronoLocalDate last = first
* .plus(1, ChronoUnit.YEARS) * .plus(1, ChronoUnit.YEARS)
* .minus(1, ChronoUnit.DAYS); * .minus(1, ChronoUnit.DAYS);
* System.out.printf(" %s: 1st of year: %s; end of year: %s%n", last.getChronology().getId(), * System.out.printf(" %s: 1st of year: %s; end of year: %s%n", last.getChronology().getId(),
......
...@@ -265,7 +265,7 @@ import java.util.Set; ...@@ -265,7 +265,7 @@ import java.util.Set;
* <p> * <p>
* For example: * For example:
* <blockquote><pre> * <blockquote><pre>
* DateTimeFormatter formatter = DateTimeFormatter.pattern("yyyy MM dd"); * DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
* String text = date.toString(formatter); * String text = date.toString(formatter);
* LocalDate date = LocalDate.parse(text, formatter); * LocalDate date = LocalDate.parse(text, formatter);
* </pre></blockquote> * </pre></blockquote>
...@@ -460,7 +460,7 @@ import java.util.Set; ...@@ -460,7 +460,7 @@ import java.util.Set;
* <li>The {@code ChronoField} time fields are resolved. * <li>The {@code ChronoField} time fields are resolved.
* This is documented on {@link ChronoField} and is the same for all chronologies. * This is documented on {@link ChronoField} and is the same for all chronologies.
* <li>Any fields that are not {@code ChronoField} are processed. * <li>Any fields that are not {@code ChronoField} are processed.
* This is achieved using {@link TemporalField#resolve(TemporalAccessor, long, ResolverStyle)}. * This is achieved using {@link TemporalField#resolve(Map, Chronology, ZoneId, ResolverStyle)}.
* Documentation about field resolution is located in the implementation * Documentation about field resolution is located in the implementation
* of {@code TemporalField}. * of {@code TemporalField}.
* <li>The {@code ChronoField} date and time fields are re-resolved. * <li>The {@code ChronoField} date and time fields are re-resolved.
......
...@@ -77,7 +77,6 @@ import java.math.BigInteger; ...@@ -77,7 +77,6 @@ import java.math.BigInteger;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.text.ParsePosition; import java.text.ParsePosition;
import java.time.DateTimeException; import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
...@@ -962,12 +961,9 @@ public final class DateTimeFormatterBuilder { ...@@ -962,12 +961,9 @@ public final class DateTimeFormatterBuilder {
* <pre> * <pre>
* "Europe/London" -- ZoneId.of("Europe/London") * "Europe/London" -- ZoneId.of("Europe/London")
* "Z" -- ZoneOffset.UTC * "Z" -- ZoneOffset.UTC
* "UT" -- ZoneOffset.UTC * "UT" -- ZoneId.of("UT")
* "UTC" -- ZoneOffset.UTC * "UTC" -- ZoneId.of("UTC")
* "GMT" -- ZoneOffset.UTC * "GMT" -- ZoneId.of("GMT")
* "UT0" -- ZoneOffset.UTC
* "UTC0" -- ZoneOffset.UTC
* "GMT0" -- ZoneOffset.UTC
* "+01:30" -- ZoneOffset.of("+01:30") * "+01:30" -- ZoneOffset.of("+01:30")
* "UT+01:30" -- ZoneOffset.of("+01:30") * "UT+01:30" -- ZoneOffset.of("+01:30")
* "UTC+01:30" -- ZoneOffset.of("+01:30") * "UTC+01:30" -- ZoneOffset.of("+01:30")
...@@ -1016,12 +1012,9 @@ public final class DateTimeFormatterBuilder { ...@@ -1016,12 +1012,9 @@ public final class DateTimeFormatterBuilder {
* <pre> * <pre>
* "Europe/London" -- ZoneId.of("Europe/London") * "Europe/London" -- ZoneId.of("Europe/London")
* "Z" -- ZoneOffset.UTC * "Z" -- ZoneOffset.UTC
* "UT" -- ZoneOffset.UTC * "UT" -- ZoneId.of("UT")
* "UTC" -- ZoneOffset.UTC * "UTC" -- ZoneId.of("UTC")
* "GMT" -- ZoneOffset.UTC * "GMT" -- ZoneId.of("GMT")
* "UT0" -- ZoneOffset.UTC
* "UTC0" -- ZoneOffset.UTC
* "GMT0" -- ZoneOffset.UTC
* "+01:30" -- ZoneOffset.of("+01:30") * "+01:30" -- ZoneOffset.of("+01:30")
* "UT+01:30" -- ZoneOffset.of("+01:30") * "UT+01:30" -- ZoneOffset.of("+01:30")
* "UTC+01:30" -- ZoneOffset.of("+01:30") * "UTC+01:30" -- ZoneOffset.of("+01:30")
...@@ -1077,16 +1070,13 @@ public final class DateTimeFormatterBuilder { ...@@ -1077,16 +1070,13 @@ public final class DateTimeFormatterBuilder {
* <pre> * <pre>
* "Europe/London" -- ZoneId.of("Europe/London") * "Europe/London" -- ZoneId.of("Europe/London")
* "Z" -- ZoneOffset.UTC * "Z" -- ZoneOffset.UTC
* "UT" -- ZoneOffset.UTC * "UT" -- ZoneId.of("UT")
* "UTC" -- ZoneOffset.UTC * "UTC" -- ZoneId.of("UTC")
* "GMT" -- ZoneOffset.UTC * "GMT" -- ZoneId.of("GMT")
* "UT0" -- ZoneOffset.UTC
* "UTC0" -- ZoneOffset.UTC
* "GMT0" -- ZoneOffset.UTC
* "+01:30" -- ZoneOffset.of("+01:30") * "+01:30" -- ZoneOffset.of("+01:30")
* "UT+01:30" -- ZoneOffset.of("+01:30") * "UT+01:30" -- ZoneOffset.of("UT+01:30")
* "UTC+01:30" -- ZoneOffset.of("+01:30") * "UTC+01:30" -- ZoneOffset.of("UTC+01:30")
* "GMT+01:30" -- ZoneOffset.of("+01:30") * "GMT+01:30" -- ZoneOffset.of("GMT+01:30")
* </pre> * </pre>
* <p> * <p>
* Note that this method is is identical to {@code appendZoneId()} except * Note that this method is is identical to {@code appendZoneId()} except
...@@ -2530,7 +2520,7 @@ public final class DateTimeFormatterBuilder { ...@@ -2530,7 +2520,7 @@ public final class DateTimeFormatterBuilder {
DecimalStyle decimalStyle = context.getDecimalStyle(); DecimalStyle decimalStyle = context.getDecimalStyle();
String str = (value == Long.MIN_VALUE ? "9223372036854775808" : Long.toString(Math.abs(value))); String str = (value == Long.MIN_VALUE ? "9223372036854775808" : Long.toString(Math.abs(value)));
if (str.length() > maxWidth) { if (str.length() > maxWidth) {
throw new DateTimeException("Field " + field.getName() + throw new DateTimeException("Field " + field +
" cannot be printed as the value " + value + " cannot be printed as the value " + value +
" exceeds the maximum print width of " + maxWidth); " exceeds the maximum print width of " + maxWidth);
} }
...@@ -2555,7 +2545,7 @@ public final class DateTimeFormatterBuilder { ...@@ -2555,7 +2545,7 @@ public final class DateTimeFormatterBuilder {
buf.append(decimalStyle.getNegativeSign()); buf.append(decimalStyle.getNegativeSign());
break; break;
case NOT_NEGATIVE: case NOT_NEGATIVE:
throw new DateTimeException("Field " + field.getName() + throw new DateTimeException("Field " + field +
" cannot be printed as the value " + value + " cannot be printed as the value " + value +
" cannot be negative according to the SignStyle"); " cannot be negative according to the SignStyle");
} }
...@@ -2699,12 +2689,12 @@ public final class DateTimeFormatterBuilder { ...@@ -2699,12 +2689,12 @@ public final class DateTimeFormatterBuilder {
@Override @Override
public String toString() { public String toString() {
if (minWidth == 1 && maxWidth == 19 && signStyle == SignStyle.NORMAL) { if (minWidth == 1 && maxWidth == 19 && signStyle == SignStyle.NORMAL) {
return "Value(" + field.getName() + ")"; return "Value(" + field + ")";
} }
if (minWidth == maxWidth && signStyle == SignStyle.NOT_NEGATIVE) { if (minWidth == maxWidth && signStyle == SignStyle.NOT_NEGATIVE) {
return "Value(" + field.getName() + "," + minWidth + ")"; return "Value(" + field + "," + minWidth + ")";
} }
return "Value(" + field.getName() + "," + minWidth + "," + maxWidth + "," + signStyle + ")"; return "Value(" + field + "," + minWidth + "," + maxWidth + "," + signStyle + ")";
} }
} }
...@@ -2817,7 +2807,7 @@ public final class DateTimeFormatterBuilder { ...@@ -2817,7 +2807,7 @@ public final class DateTimeFormatterBuilder {
@Override @Override
public String toString() { public String toString() {
return "ReducedValue(" + field.getName() + "," + minWidth + "," + maxWidth + "," + baseValue + ")"; return "ReducedValue(" + field + "," + minWidth + "," + maxWidth + "," + baseValue + ")";
} }
} }
...@@ -2842,7 +2832,7 @@ public final class DateTimeFormatterBuilder { ...@@ -2842,7 +2832,7 @@ public final class DateTimeFormatterBuilder {
FractionPrinterParser(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) { FractionPrinterParser(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) {
Objects.requireNonNull(field, "field"); Objects.requireNonNull(field, "field");
if (field.range().isFixed() == false) { if (field.range().isFixed() == false) {
throw new IllegalArgumentException("Field must have a fixed set of values: " + field.getName()); throw new IllegalArgumentException("Field must have a fixed set of values: " + field);
} }
if (minWidth < 0 || minWidth > 9) { if (minWidth < 0 || minWidth > 9) {
throw new IllegalArgumentException("Minimum width must be from 0 to 9 inclusive but was " + minWidth); throw new IllegalArgumentException("Minimum width must be from 0 to 9 inclusive but was " + minWidth);
...@@ -2984,7 +2974,7 @@ public final class DateTimeFormatterBuilder { ...@@ -2984,7 +2974,7 @@ public final class DateTimeFormatterBuilder {
@Override @Override
public String toString() { public String toString() {
String decimal = (decimalPoint ? ",DecimalPoint" : ""); String decimal = (decimalPoint ? ",DecimalPoint" : "");
return "Fraction(" + field.getName() + "," + minWidth + "," + maxWidth + decimal + ")"; return "Fraction(" + field + "," + minWidth + "," + maxWidth + decimal + ")";
} }
} }
...@@ -3079,9 +3069,9 @@ public final class DateTimeFormatterBuilder { ...@@ -3079,9 +3069,9 @@ public final class DateTimeFormatterBuilder {
@Override @Override
public String toString() { public String toString() {
if (textStyle == TextStyle.FULL) { if (textStyle == TextStyle.FULL) {
return "Text(" + field.getName() + ")"; return "Text(" + field + ")";
} }
return "Text(" + field.getName() + "," + textStyle + ")"; return "Text(" + field + "," + textStyle + ")";
} }
} }
...@@ -3756,17 +3746,17 @@ public final class DateTimeFormatterBuilder { ...@@ -3756,17 +3746,17 @@ public final class DateTimeFormatterBuilder {
// handle fixed time-zone IDs // handle fixed time-zone IDs
char nextChar = text.charAt(position); char nextChar = text.charAt(position);
if (nextChar == '+' || nextChar == '-') { if (nextChar == '+' || nextChar == '-') {
return parseOffsetBased(context, text, position, OffsetIdPrinterParser.INSTANCE_ID_Z); return parseOffsetBased(context, text, position, position, OffsetIdPrinterParser.INSTANCE_ID_Z);
} else if (length >= position + 2) { } else if (length >= position + 2) {
char nextNextChar = text.charAt(position + 1); char nextNextChar = text.charAt(position + 1);
if (context.charEquals(nextChar, 'U') && context.charEquals(nextNextChar, 'T')) { if (context.charEquals(nextChar, 'U') && context.charEquals(nextNextChar, 'T')) {
if (length >= position + 3 && context.charEquals(text.charAt(position + 2), 'C')) { if (length >= position + 3 && context.charEquals(text.charAt(position + 2), 'C')) {
return parseOffsetBased(context, text, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO); return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
} }
return parseOffsetBased(context, text, position + 2, OffsetIdPrinterParser.INSTANCE_ID_ZERO); return parseOffsetBased(context, text, position, position + 2, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
} else if (context.charEquals(nextChar, 'G') && length >= position + 3 && } else if (context.charEquals(nextChar, 'G') && length >= position + 3 &&
context.charEquals(nextNextChar, 'M') && context.charEquals(text.charAt(position + 2), 'T')) { context.charEquals(nextNextChar, 'M') && context.charEquals(text.charAt(position + 2), 'T')) {
return parseOffsetBased(context, text, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO); return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
} }
} }
...@@ -3785,20 +3775,49 @@ public final class DateTimeFormatterBuilder { ...@@ -3785,20 +3775,49 @@ public final class DateTimeFormatterBuilder {
return ppos.getIndex(); return ppos.getIndex();
} }
private int parseOffsetBased(DateTimeParseContext context, CharSequence text, int position, OffsetIdPrinterParser parser) { /**
* Parse an offset following a prefix and set the ZoneId if it is valid.
* To matching the parsing of ZoneId.of the values are not normalized
* to ZoneOffsets.
*
* @param context the parse context
* @param text the input text
* @param prefixPos start of the prefix
* @param position start of text after the prefix
* @param parser parser for the value after the prefix
* @return the position after the parse
*/
private int parseOffsetBased(DateTimeParseContext context, CharSequence text, int prefixPos, int position, OffsetIdPrinterParser parser) {
String prefix = text.toString().substring(prefixPos, position).toUpperCase();
if (position >= text.length()) {
context.setParsed(ZoneId.of(prefix));
return position;
}
// '0' or 'Z' after prefix is not part of a valid ZoneId; use bare prefix
if (text.charAt(position) == '0' ||
context.charEquals(text.charAt(position), 'Z')) {
context.setParsed(ZoneId.of(prefix));
return position;
}
DateTimeParseContext newContext = context.copy(); DateTimeParseContext newContext = context.copy();
int endPos = parser.parse(newContext, text, position); int endPos = parser.parse(newContext, text, position);
if (endPos < 0) { try {
if (parser == OffsetIdPrinterParser.INSTANCE_ID_Z) { if (endPos < 0) {
return ~position; if (parser == OffsetIdPrinterParser.INSTANCE_ID_Z) {
return ~prefixPos;
}
context.setParsed(ZoneId.of(prefix));
return position;
} }
context.setParsed(ZoneOffset.UTC); int offset = (int) newContext.getParsed(OFFSET_SECONDS).longValue();
return position; ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(offset);
context.setParsed(ZoneId.ofOffset(prefix, zoneOffset));
return endPos;
} catch (DateTimeException dte) {
return ~prefixPos;
} }
int offset = (int) newContext.getParsed(OFFSET_SECONDS).longValue();
ZoneId zone = ZoneOffset.ofTotalSeconds(offset);
context.setParsed(zone);
return endPos;
} }
@Override @Override
......
...@@ -157,7 +157,7 @@ final class DateTimePrintContext { ...@@ -157,7 +157,7 @@ final class DateTimePrintContext {
} }
} }
final ZoneId effectiveZone = (overrideZone != null ? overrideZone : temporalZone); final ZoneId effectiveZone = (overrideZone != null ? overrideZone : temporalZone);
final ChronoLocalDate<?> effectiveDate; final ChronoLocalDate effectiveDate;
if (overrideChrono != null) { if (overrideChrono != null) {
if (temporal.isSupported(EPOCH_DAY)) { if (temporal.isSupported(EPOCH_DAY)) {
effectiveDate = effectiveChrono.date(temporal); effectiveDate = effectiveChrono.date(temporal);
......
...@@ -143,7 +143,7 @@ final class Parsed implements TemporalAccessor { ...@@ -143,7 +143,7 @@ final class Parsed implements TemporalAccessor {
/** /**
* The resolved date. * The resolved date.
*/ */
private ChronoLocalDate<?> date; private ChronoLocalDate date;
/** /**
* The resolved time. * The resolved time.
*/ */
...@@ -197,7 +197,7 @@ final class Parsed implements TemporalAccessor { ...@@ -197,7 +197,7 @@ final class Parsed implements TemporalAccessor {
return time.getLong(field); return time.getLong(field);
} }
if (field instanceof ChronoField) { if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName()); throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
} }
return field.getFrom(this); return field.getFrom(this);
} }
...@@ -255,42 +255,34 @@ final class Parsed implements TemporalAccessor { ...@@ -255,42 +255,34 @@ final class Parsed implements TemporalAccessor {
// if any other fields, handle them // if any other fields, handle them
// any lenient date resolution should return epoch-day // any lenient date resolution should return epoch-day
if (fieldValues.size() > 0) { if (fieldValues.size() > 0) {
boolean changed = false; int changedCount = 0;
outer: outer:
while (true) { while (changedCount < 50) {
for (Map.Entry<TemporalField, Long> entry : fieldValues.entrySet()) { for (Map.Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
TemporalField targetField = entry.getKey(); TemporalField targetField = entry.getKey();
Map<TemporalField, Long> changes = targetField.resolve(this, entry.getValue(), resolverStyle); ChronoLocalDate resolvedDate = targetField.resolve(fieldValues, chrono, zone, resolverStyle);
if (changes != null) { if (resolvedDate != null) {
changed = true; updateCheckConflict(resolvedDate);
resolveFieldsMakeChanges(targetField, changes); changedCount++;
fieldValues.remove(targetField); // helps avoid infinite loops continue outer; // have to restart to avoid concurrent modification
} else if (fieldValues.containsKey(targetField) == false) {
changedCount++;
continue outer; // have to restart to avoid concurrent modification continue outer; // have to restart to avoid concurrent modification
} }
} }
break; break;
} }
if (changedCount == 50) { // catch infinite loops
throw new DateTimeException("One of the parsed fields has an incorrectly implemented resolve method");
}
// if something changed then have to redo ChronoField resolve // if something changed then have to redo ChronoField resolve
if (changed) { if (changedCount > 0) {
resolveDateFields(); resolveDateFields();
resolveTimeFields(); resolveTimeFields();
} }
} }
} }
private void resolveFieldsMakeChanges(TemporalField targetField, Map<TemporalField, Long> changes) {
for (Map.Entry<TemporalField, Long> change : changes.entrySet()) {
TemporalField changeField = change.getKey();
Long changeValue = change.getValue();
Objects.requireNonNull(changeField, "changeField");
if (changeValue != null) {
updateCheckConflict(targetField, changeField, changeValue);
} else {
fieldValues.remove(changeField);
}
}
}
private void updateCheckConflict(TemporalField targetField, TemporalField changeField, Long changeValue) { private void updateCheckConflict(TemporalField targetField, TemporalField changeField, Long changeValue) {
Long old = fieldValues.put(changeField, changeValue); Long old = fieldValues.put(changeField, changeValue);
if (old != null && old.longValue() != changeValue.longValue()) { if (old != null && old.longValue() != changeValue.longValue()) {
...@@ -305,7 +297,7 @@ final class Parsed implements TemporalAccessor { ...@@ -305,7 +297,7 @@ final class Parsed implements TemporalAccessor {
updateCheckConflict(chrono.resolveDate(fieldValues, resolverStyle)); updateCheckConflict(chrono.resolveDate(fieldValues, resolverStyle));
} }
private void updateCheckConflict(ChronoLocalDate<?> cld) { private void updateCheckConflict(ChronoLocalDate cld) {
if (date != null) { if (date != null) {
if (cld != null && date.equals(cld) == false) { if (cld != null && date.equals(cld) == false) {
throw new DateTimeException("Conflict found: Fields resolved to two different dates: " + date + " " + cld); throw new DateTimeException("Conflict found: Fields resolved to two different dates: " + date + " " + cld);
......
...@@ -403,6 +403,12 @@ public enum ChronoField implements TemporalField { ...@@ -403,6 +403,12 @@ public enum ChronoField implements TemporalField {
* Non-ISO calendar systems should implement this field using the most recognized * Non-ISO calendar systems should implement this field using the most recognized
* day-of-year values for users of the calendar system. * day-of-year values for users of the calendar system.
* Normally, this is a count of days from 1 to the length of the year. * Normally, this is a count of days from 1 to the length of the year.
* <p>
* Note that a non-ISO calendar system may have year numbering system that changes
* at a different point to the natural reset in the month numbering. An example
* of this is the Japanese calendar system where a change of era, which resets
* the year number to 1, can happen on any date. The era and year reset also cause
* the day-of-year to be reset to 1, but not the month-of-year or day-of-month.
*/ */
DAY_OF_YEAR("DayOfYear", DAYS, YEARS, ValueRange.of(1, 365, 366)), DAY_OF_YEAR("DayOfYear", DAYS, YEARS, ValueRange.of(1, 365, 366)),
/** /**
...@@ -559,12 +565,11 @@ public enum ChronoField implements TemporalField { ...@@ -559,12 +565,11 @@ public enum ChronoField implements TemporalField {
* <p> * <p>
* This represents the concept of the sequential count of seconds where * This represents the concept of the sequential count of seconds where
* 1970-01-01T00:00Z (ISO) is zero. * 1970-01-01T00:00Z (ISO) is zero.
* This field may be used with {@link #NANO_OF_DAY} to represent the fraction of the day. * This field may be used with {@link #NANO_OF_SECOND} to represent the fraction of the second.
* <p> * <p>
* An {@link Instant} represents an instantaneous point on the time-line. * An {@link Instant} represents an instantaneous point on the time-line.
* On their own they have no elements which allow a local date-time to be obtained. * On their own, an instant has insufficient information to allow a local date-time to be obtained.
* Only when paired with an offset or time-zone can the local date or time be found. * Only when paired with an offset or time-zone can the local date or time be calculated.
* This field allows the seconds part of the instant to be queried.
* <p> * <p>
* This field is strictly defined to have the same meaning in all calendar systems. * This field is strictly defined to have the same meaning in all calendar systems.
* This is necessary to ensure interoperation between calendars. * This is necessary to ensure interoperation between calendars.
...@@ -608,24 +613,18 @@ public enum ChronoField implements TemporalField { ...@@ -608,24 +613,18 @@ public enum ChronoField implements TemporalField {
this.displayNameKey = displayNameKey; this.displayNameKey = displayNameKey;
} }
//-----------------------------------------------------------------------
@Override
public String getName() {
return name;
}
@Override @Override
public String getDisplayName(Locale locale) { public String getDisplayName(Locale locale) {
Objects.requireNonNull(locale, "locale"); Objects.requireNonNull(locale, "locale");
if (displayNameKey == null) { if (displayNameKey == null) {
return getName(); return name;
} }
LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased() LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased()
.getLocaleResources(locale); .getLocaleResources(locale);
ResourceBundle rb = lr.getJavaTimeFormatData(); ResourceBundle rb = lr.getJavaTimeFormatData();
String key = "field." + displayNameKey; String key = "field." + displayNameKey;
return rb.containsKey(key) ? rb.getString(key) : getName(); return rb.containsKey(key) ? rb.getString(key) : name;
} }
@Override @Override
...@@ -748,7 +747,7 @@ public enum ChronoField implements TemporalField { ...@@ -748,7 +747,7 @@ public enum ChronoField implements TemporalField {
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public String toString() { public String toString() {
return getName(); return name;
} }
} }
...@@ -57,9 +57,6 @@ ...@@ -57,9 +57,6 @@
package java.time.temporal; package java.time.temporal;
import java.time.Duration; import java.time.Duration;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
/** /**
* A standard set of date periods units. * A standard set of date periods units.
...@@ -200,12 +197,6 @@ public enum ChronoUnit implements TemporalUnit { ...@@ -200,12 +197,6 @@ public enum ChronoUnit implements TemporalUnit {
this.duration = estimatedDuration; this.duration = estimatedDuration;
} }
//-----------------------------------------------------------------------
@Override
public String getName() {
return name;
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Gets the estimated duration of this unit in the ISO calendar system. * Gets the estimated duration of this unit in the ISO calendar system.
...@@ -233,41 +224,40 @@ public enum ChronoUnit implements TemporalUnit { ...@@ -233,41 +224,40 @@ public enum ChronoUnit implements TemporalUnit {
*/ */
@Override @Override
public boolean isDurationEstimated() { public boolean isDurationEstimated() {
return isDateUnit(); return this.compareTo(DAYS) >= 0;
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Checks if this unit is a date unit. * Checks if this unit is a date unit.
* <p>
* All units from days to eras inclusive are date-based.
* Time-based units and {@code FOREVER} return false.
* *
* @return true if a date unit, false if a time unit * @return true if a date unit, false if a time unit
*/ */
public boolean isDateUnit() { @Override
return this.compareTo(DAYS) >= 0; public boolean isDateBased() {
return this.compareTo(DAYS) >= 0 && this != FOREVER;
} }
/** /**
* Checks if this unit is a time unit. * Checks if this unit is a time unit.
* <p>
* All units from nanos to half-days inclusive are time-based.
* Date-based units and {@code FOREVER} return false.
* *
* @return true if a time unit, false if a date unit * @return true if a time unit, false if a date unit
*/ */
public boolean isTimeUnit() { @Override
public boolean isTimeBased() {
return this.compareTo(DAYS) < 0; return this.compareTo(DAYS) < 0;
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public boolean isSupportedBy(Temporal temporal) { public boolean isSupportedBy(Temporal temporal) {
if (this == FOREVER) { return temporal.isSupported(this);
return false;
}
if (temporal instanceof ChronoLocalDate) {
return isDateUnit();
}
if (temporal instanceof ChronoLocalDateTime || temporal instanceof ChronoZonedDateTime) {
return true;
}
return TemporalUnit.super.isSupportedBy(temporal);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
...@@ -279,13 +269,13 @@ public enum ChronoUnit implements TemporalUnit { ...@@ -279,13 +269,13 @@ public enum ChronoUnit implements TemporalUnit {
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public long between(Temporal temporal1, Temporal temporal2) { public long between(Temporal temporal1, Temporal temporal2) {
return temporal1.periodUntil(temporal2, this); return temporal1.until(temporal2, this);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public String toString() { public String toString() {
return getName(); return name;
} }
} }
...@@ -66,6 +66,9 @@ import static java.time.temporal.ChronoUnit.DAYS; ...@@ -66,6 +66,9 @@ import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.FOREVER;
import java.time.DateTimeException; import java.time.DateTimeException;
import java.time.ZoneId;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Chronology;
import java.time.format.ResolverStyle; import java.time.format.ResolverStyle;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
...@@ -232,11 +235,6 @@ public final class JulianFields { ...@@ -232,11 +235,6 @@ public final class JulianFields {
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override
public String getName() {
return name;
}
@Override @Override
public TemporalUnit getBaseUnit() { public TemporalUnit getBaseUnit() {
return baseUnit; return baseUnit;
...@@ -252,6 +250,11 @@ public final class JulianFields { ...@@ -252,6 +250,11 @@ public final class JulianFields {
return true; return true;
} }
@Override
public boolean isTimeBased() {
return false;
}
@Override @Override
public ValueRange range() { public ValueRange range() {
return range; return range;
...@@ -287,15 +290,14 @@ public final class JulianFields { ...@@ -287,15 +290,14 @@ public final class JulianFields {
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@Override @Override
public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long value, ResolverStyle resolverStyle) { public ChronoLocalDate resolve(
long epochDay; Map<TemporalField, Long> fieldValues, Chronology chronology, ZoneId zone, ResolverStyle resolverStyle) {
long value = fieldValues.remove(this);
if (resolverStyle == ResolverStyle.LENIENT) { if (resolverStyle == ResolverStyle.LENIENT) {
epochDay = Math.subtractExact(value, offset); return chronology.dateEpochDay(Math.subtractExact(value, offset));
} else {
range().checkValidValue(value, this);
epochDay = value - offset;
} }
return Collections.<TemporalField, Long>singletonMap(EPOCH_DAY, epochDay); range().checkValidValue(value, this);
return chronology.dateEpochDay(value - offset);
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
......
...@@ -62,7 +62,6 @@ ...@@ -62,7 +62,6 @@
package java.time.temporal; package java.time.temporal;
import java.time.DateTimeException; import java.time.DateTimeException;
import java.time.ZoneId;
/** /**
* Framework-level interface defining read-write access to a temporal object, * Framework-level interface defining read-write access to a temporal object,
...@@ -81,7 +80,8 @@ import java.time.ZoneId; ...@@ -81,7 +80,8 @@ import java.time.ZoneId;
* See {@link ChronoField} for the standard set of fields. * See {@link ChronoField} for the standard set of fields.
* <p> * <p>
* Two pieces of date/time information cannot be represented by numbers, * Two pieces of date/time information cannot be represented by numbers,
* the {@linkplain java.time.chrono.Chronology chronology} and the {@linkplain ZoneId time-zone}. * the {@linkplain java.time.chrono.Chronology chronology} and the
* {@linkplain java.time.ZoneId time-zone}.
* These can be accessed via {@link #query(TemporalQuery) queries} using * These can be accessed via {@link #query(TemporalQuery) queries} using
* the static methods defined on {@link TemporalQuery}. * the static methods defined on {@link TemporalQuery}.
* <p> * <p>
...@@ -128,6 +128,29 @@ import java.time.ZoneId; ...@@ -128,6 +128,29 @@ import java.time.ZoneId;
*/ */
public interface Temporal extends TemporalAccessor { public interface Temporal extends TemporalAccessor {
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to, or subtracted from, this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
*
* @implSpec
* Implementations must check and handle all units defined in {@link ChronoUnit}.
* If the unit is supported, then true must be returned, otherwise false must be returned.
* <p>
* If the field is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* <p>
* Implementations must ensure that no observable state is altered when this
* read-only method is invoked.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
boolean isSupported(TemporalUnit unit);
/** /**
* Returns an adjusted object of the same type as this object with the adjustment made. * Returns an adjusted object of the same type as this object with the adjustment made.
* <p> * <p>
...@@ -352,7 +375,7 @@ public interface Temporal extends TemporalAccessor { ...@@ -352,7 +375,7 @@ public interface Temporal extends TemporalAccessor {
* The start and end points are {@code this} and the specified temporal. * The start and end points are {@code this} and the specified temporal.
* The result will be negative if the end is before the start. * The result will be negative if the end is before the start.
* For example, the period in hours between two temporal objects can be * For example, the period in hours between two temporal objects can be
* calculated using {@code startTime.periodUntil(endTime, HOURS)}. * calculated using {@code startTime.until(endTime, HOURS)}.
* <p> * <p>
* The calculation returns a whole number, representing the number of * The calculation returns a whole number, representing the number of
* complete units between the two temporals. * complete units between the two temporals.
...@@ -364,7 +387,7 @@ public interface Temporal extends TemporalAccessor { ...@@ -364,7 +387,7 @@ public interface Temporal extends TemporalAccessor {
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre> * <pre>
* // these two lines are equivalent * // these two lines are equivalent
* temporal = start.periodUntil(end, unit); * temporal = start.until(end, unit);
* temporal = unit.between(start, end); * temporal = unit.between(start, end);
* </pre> * </pre>
* The choice should be made based on which makes the code more readable. * The choice should be made based on which makes the code more readable.
...@@ -372,7 +395,7 @@ public interface Temporal extends TemporalAccessor { ...@@ -372,7 +395,7 @@ public interface Temporal extends TemporalAccessor {
* For example, this method allows the number of days between two dates to * For example, this method allows the number of days between two dates to
* be calculated: * be calculated:
* <pre> * <pre>
* long daysBetween = start.periodUntil(end, DAYS); * long daysBetween = start.until(end, DAYS);
* // or alternatively * // or alternatively
* long daysBetween = DAYS.between(start, end); * long daysBetween = DAYS.between(start, end);
* </pre> * </pre>
...@@ -399,7 +422,8 @@ public interface Temporal extends TemporalAccessor { ...@@ -399,7 +422,8 @@ public interface Temporal extends TemporalAccessor {
* return unit.between(this, endTemporal); * return unit.between(this, endTemporal);
* </pre> * </pre>
* <p> * <p>
* Neither this object, nor the specified temporal, may be altered. * Implementations must ensure that no observable state is altered when this
* read-only method is invoked.
* *
* @param endTemporal the end temporal, of the same type as this object, not null * @param endTemporal the end temporal, of the same type as this object, not null
* @param unit the unit to measure the amount in, not null * @param unit the unit to measure the amount in, not null
...@@ -410,6 +434,6 @@ public interface Temporal extends TemporalAccessor { ...@@ -410,6 +434,6 @@ public interface Temporal extends TemporalAccessor {
* @throws UnsupportedTemporalTypeException if the unit is not supported * @throws UnsupportedTemporalTypeException if the unit is not supported
* @throws ArithmeticException if numeric overflow occurs * @throws ArithmeticException if numeric overflow occurs
*/ */
long periodUntil(Temporal endTemporal, TemporalUnit unit); long until(Temporal endTemporal, TemporalUnit unit);
} }
...@@ -331,7 +331,7 @@ public final class ValueRange implements Serializable { ...@@ -331,7 +331,7 @@ public final class ValueRange implements Serializable {
private String genInvalidFieldMessage(TemporalField field, long value) { private String genInvalidFieldMessage(TemporalField field, long value) {
if (field != null) { if (field != null) {
return "Invalid value for " + field.getName() + " (valid values " + this + "): " + value; return "Invalid value for " + field + " (valid values " + this + "): " + value;
} else { } else {
return "Invalid value (valid values " + this + "): " + value; return "Invalid value (valid values " + this + "): " + value;
} }
......
...@@ -176,7 +176,7 @@ public final class MockSimplePeriod ...@@ -176,7 +176,7 @@ public final class MockSimplePeriod
@Override @Override
public String toString() { public String toString() {
return amount + " " + unit.getName(); return amount + " " + unit;
} }
} }
...@@ -91,6 +91,7 @@ public class TCKClock_Fixed extends AbstractTCKTest { ...@@ -91,6 +91,7 @@ public class TCKClock_Fixed extends AbstractTCKTest {
Clock test = Clock.fixed(INSTANT, PARIS); Clock test = Clock.fixed(INSTANT, PARIS);
assertEquals(test.instant(), INSTANT); assertEquals(test.instant(), INSTANT);
assertEquals(test.getZone(), PARIS); assertEquals(test.getZone(), PARIS);
assertEquals(test.instant().getEpochSecond()*1000, test.millis());
} }
@Test(expectedExceptions = NullPointerException.class) @Test(expectedExceptions = NullPointerException.class)
......
...@@ -85,6 +85,8 @@ public class TCKHijrahEra { ...@@ -85,6 +85,8 @@ public class TCKHijrahEra {
@Test(dataProvider="HijrahEras") @Test(dataProvider="HijrahEras")
public void test_valueOf(HijrahEra era , String eraName, int eraValue) { public void test_valueOf(HijrahEra era , String eraName, int eraValue) {
assertEquals(era.getValue(), eraValue); assertEquals(era.getValue(), eraValue);
assertEquals(HijrahChronology.INSTANCE.eraOf(eraValue), era);
assertEquals(HijrahEra.of(eraValue), era); assertEquals(HijrahEra.of(eraValue), era);
assertEquals(HijrahEra.valueOf(eraName), era); assertEquals(HijrahEra.valueOf(eraName), era);
} }
......
...@@ -81,7 +81,6 @@ public class TCKJapaneseEra { ...@@ -81,7 +81,6 @@ public class TCKJapaneseEra {
{JapaneseEra.SHOWA, "Showa", 1}, {JapaneseEra.SHOWA, "Showa", 1},
{JapaneseEra.TAISHO, "Taisho", 0}, {JapaneseEra.TAISHO, "Taisho", 0},
{JapaneseEra.MEIJI, "Meiji", -1}, {JapaneseEra.MEIJI, "Meiji", -1},
{JapaneseEra.SEIREKI, "Seireki", -999},
}; };
} }
...@@ -112,8 +111,8 @@ public class TCKJapaneseEra { ...@@ -112,8 +111,8 @@ public class TCKJapaneseEra {
public void test_range() { public void test_range() {
// eras may be added after release // eras may be added after release
for (JapaneseEra era : JapaneseEra.values()) { for (JapaneseEra era : JapaneseEra.values()) {
assertEquals(era.range(ERA).getMinimum(), -999); assertEquals(era.range(ERA).getMinimum(), -1);
assertEquals(era.range(ERA).getLargestMinimum(), -999); assertEquals(era.range(ERA).getLargestMinimum(), -1);
assertEquals(era.range(ERA).getSmallestMaximum(), era.range(ERA).getMaximum()); assertEquals(era.range(ERA).getSmallestMaximum(), era.range(ERA).getMaximum());
assertEquals(era.range(ERA).getMaximum() >= 2, true); assertEquals(era.range(ERA).getMaximum() >= 2, true);
} }
......
此差异已折叠。
此差异已折叠。
...@@ -91,4 +91,14 @@ public class TCKTextStyle { ...@@ -91,4 +91,14 @@ public class TCKTextStyle {
assertTrue(!TextStyle.NARROW.isStandalone()); assertTrue(!TextStyle.NARROW.isStandalone());
} }
//-----------------------------------------------------------------------
// valueOf()
//-----------------------------------------------------------------------
@Test
public void test_valueOf() {
for (TextStyle style : TextStyle.values()) {
assertEquals(TextStyle.valueOf(style.name()), style);
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册