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.
      *