src/share/classes/java/time/format/DateTimePrintContext.java
Print this page
@@ -61,18 +61,20 @@
*/
package java.time.format;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
import java.time.temporal.ChronoField;
-import java.time.chrono.ChronoLocalDate;
-import java.time.temporal.Queries;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.ValueRange;
import java.util.Locale;
@@ -116,79 +118,109 @@
this.temporal = adjust(temporal, formatter);
this.formatter = formatter;
}
private static TemporalAccessor adjust(final TemporalAccessor temporal, DateTimeFormatter formatter) {
- // normal case first
+ // normal case first (early return is an optimization)
Chronology overrideChrono = formatter.getChronology();
ZoneId overrideZone = formatter.getZone();
if (overrideChrono == null && overrideZone == null) {
return temporal;
}
- // ensure minimal change
- Chronology temporalChrono = Chronology.from(temporal); // default to ISO, handles Instant
- ZoneId temporalZone = temporal.query(Queries.zone()); // zone then offset, handles OffsetDateTime
- if (temporal.isSupported(EPOCH_DAY) == false || Objects.equals(overrideChrono, temporalChrono)) {
+ // ensure minimal change (early return is an optimization)
+ Chronology temporalChrono = temporal.query(TemporalQuery.chronology());
+ ZoneId temporalZone = temporal.query(TemporalQuery.zoneId());
+ if (Objects.equals(overrideChrono, temporalChrono)) {
overrideChrono = null;
}
- if (temporal.isSupported(INSTANT_SECONDS) == false || Objects.equals(overrideZone, temporalZone)) {
+ if (Objects.equals(overrideZone, temporalZone)) {
overrideZone = null;
}
if (overrideChrono == null && overrideZone == null) {
return temporal;
}
// make adjustment
- if (overrideChrono != null && overrideZone != null) {
- return overrideChrono.zonedDateTime(Instant.from(temporal), overrideZone);
- } else if (overrideZone != null) {
- return temporalChrono.zonedDateTime(Instant.from(temporal), overrideZone);
- } else { // overrideChrono != null
- // need class here to handle non-standard cases
- final ChronoLocalDate date = overrideChrono.date(temporal);
+ final Chronology effectiveChrono = (overrideChrono != null ? overrideChrono : temporalChrono);
+ if (overrideZone != null) {
+ // if have zone and instant, calculation is simple, defaulting chrono if necessary
+ if (temporal.isSupported(INSTANT_SECONDS)) {
+ Chronology chrono = (effectiveChrono != null ? effectiveChrono : IsoChronology.INSTANCE);
+ return chrono.zonedDateTime(Instant.from(temporal), overrideZone);
+ }
+ // block changing zone on OffsetTime, and similar problem cases
+ if (overrideZone.normalized() instanceof ZoneOffset && temporal.isSupported(OFFSET_SECONDS) &&
+ temporal.get(OFFSET_SECONDS) != overrideZone.getRules().getOffset(Instant.EPOCH).getTotalSeconds()) {
+ throw new DateTimeException("Unable to apply override zone '" + overrideZone +
+ "' because the temporal object being formatted has a different offset but" +
+ " does not represent an instant: " + temporal);
+ }
+ }
+ final ZoneId effectiveZone = (overrideZone != null ? overrideZone : temporalZone);
+ final ChronoLocalDate<?> effectiveDate;
+ if (overrideChrono != null) {
+ if (temporal.isSupported(EPOCH_DAY)) {
+ effectiveDate = effectiveChrono.date(temporal);
+ } else {
+ // check for date fields other than epoch-day, ignoring case of converting null to ISO
+ if (!(overrideChrono == IsoChronology.INSTANCE && temporalChrono == null)) {
+ for (ChronoField f : ChronoField.values()) {
+ if (f.isDateBased() && temporal.isSupported(f)) {
+ throw new DateTimeException("Unable to apply override chronology '" + overrideChrono +
+ "' because the temporal object being formatted contains date fields but" +
+ " does not represent a whole date: " + temporal);
+ }
+ }
+ }
+ effectiveDate = null;
+ }
+ } else {
+ effectiveDate = null;
+ }
+
+ // combine available data
+ // this is a non-standard temporal that is almost a pure delegate
+ // this better handles map-like underlying temporal instances
return new TemporalAccessor() {
@Override
public boolean isSupported(TemporalField field) {
+ if (effectiveDate != null && field.isDateBased()) {
+ return effectiveDate.isSupported(field);
+ }
return temporal.isSupported(field);
}
@Override
public ValueRange range(TemporalField field) {
- if (field instanceof ChronoField) {
- if (((ChronoField) field).isDateField()) {
- return date.range(field);
- } else {
- return temporal.range(field);
- }
+ if (effectiveDate != null && field.isDateBased()) {
+ return effectiveDate.range(field);
}
- return field.rangeRefinedBy(this);
+ return temporal.range(field);
}
@Override
public long getLong(TemporalField field) {
- if (field instanceof ChronoField) {
- if (((ChronoField) field).isDateField()) {
- return date.getLong(field);
- } else {
- return temporal.getLong(field);
- }
+ if (effectiveDate != null && field.isDateBased()) {
+ return effectiveDate.getLong(field);
}
- return field.getFrom(this);
+ return temporal.getLong(field);
}
@SuppressWarnings("unchecked")
@Override
public <R> R query(TemporalQuery<R> query) {
- if (query == Queries.chronology()) {
- return (R) date.getChronology();
+ if (query == TemporalQuery.chronology()) {
+ return (R) effectiveChrono;
}
- if (query == Queries.zoneId() || query == Queries.precision()) {
+ if (query == TemporalQuery.zoneId()) {
+ return (R) effectiveZone;
+ }
+ if (query == TemporalQuery.precision()) {
return temporal.query(query);
}
return query.queryFrom(this);
}
};
}
- }
//-----------------------------------------------------------------------
/**
* Gets the temporal object being output.
*