483 lines
20 KiB
Java
483 lines
20 KiB
Java
|
/*
|
||
|
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
|
||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||
|
*
|
||
|
* This code is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU General Public License version 2 only, as
|
||
|
* published by the Free Software Foundation. Oracle designates this
|
||
|
* particular file as subject to the "Classpath" exception as provided
|
||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||
|
*
|
||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||
|
* accompanied this code).
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License version
|
||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
*
|
||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||
|
* or visit www.oracle.com if you need additional information or have any
|
||
|
* questions.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* This file is available under and governed by the GNU General Public
|
||
|
* License version 2 only, as published by the Free Software Foundation.
|
||
|
* However, the following notice accompanied the original version of this
|
||
|
* file:
|
||
|
*
|
||
|
* Copyright (c) 2008-2013, Stephen Colebourne & Michael Nascimento Santos
|
||
|
*
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are met:
|
||
|
*
|
||
|
* * Redistributions of source code must retain the above copyright notice,
|
||
|
* this list of conditions and the following disclaimer.
|
||
|
*
|
||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||
|
* this list of conditions and the following disclaimer in the documentation
|
||
|
* and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* * Neither the name of JSR-310 nor the names of its contributors
|
||
|
* may be used to endorse or promote products derived from this software
|
||
|
* without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
package java.time.format;
|
||
|
|
||
|
import static java.time.temporal.ChronoField.AMPM_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM;
|
||
|
import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.HOUR_OF_AMPM;
|
||
|
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.MICRO_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
|
||
|
import static java.time.temporal.ChronoField.MILLI_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
|
||
|
import static java.time.temporal.ChronoField.MINUTE_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
|
||
|
import static java.time.temporal.ChronoField.NANO_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
|
||
|
import static java.time.temporal.ChronoField.SECOND_OF_DAY;
|
||
|
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
|
||
|
|
||
|
import java.time.DateTimeException;
|
||
|
import java.time.LocalDate;
|
||
|
import java.time.LocalTime;
|
||
|
import java.time.ZoneId;
|
||
|
import java.time.chrono.ChronoLocalDate;
|
||
|
import java.time.chrono.Chronology;
|
||
|
import java.time.temporal.ChronoField;
|
||
|
import java.time.temporal.TemporalAccessor;
|
||
|
import java.time.temporal.TemporalField;
|
||
|
import java.time.temporal.TemporalQuery;
|
||
|
import java.time.temporal.UnsupportedTemporalTypeException;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.Map;
|
||
|
import java.util.Map.Entry;
|
||
|
import java.util.Objects;
|
||
|
import java.util.Set;
|
||
|
|
||
|
/**
|
||
|
* A store of parsed data.
|
||
|
* <p>
|
||
|
* This class is used during parsing to collect the data. Part of the parsing process
|
||
|
* involves handling optional blocks and multiple copies of the data get created to
|
||
|
* support the necessary backtracking.
|
||
|
* <p>
|
||
|
* Once parsing is completed, this class can be used as the resultant {@code TemporalAccessor}.
|
||
|
* In most cases, it is only exposed once the fields have been resolved.
|
||
|
*
|
||
|
* <h3>Specification for implementors</h3>
|
||
|
* This class is a mutable context intended for use from a single thread.
|
||
|
* Usage of the class is thread-safe within standard parsing as a new instance of this class
|
||
|
* is automatically created for each parse and parsing is single-threaded
|
||
|
*
|
||
|
* @since 1.8
|
||
|
*/
|
||
|
final class Parsed implements TemporalAccessor {
|
||
|
// some fields are accessed using package scope from DateTimeParseContext
|
||
|
|
||
|
/**
|
||
|
* The parsed fields.
|
||
|
*/
|
||
|
final Map<TemporalField, Long> fieldValues = new HashMap<>();
|
||
|
/**
|
||
|
* The parsed zone.
|
||
|
*/
|
||
|
ZoneId zone;
|
||
|
/**
|
||
|
* The parsed chronology.
|
||
|
*/
|
||
|
Chronology chrono;
|
||
|
/**
|
||
|
* The effective chronology.
|
||
|
*/
|
||
|
Chronology effectiveChrono;
|
||
|
/**
|
||
|
* The resolver style to use.
|
||
|
*/
|
||
|
private ResolverStyle resolverStyle;
|
||
|
/**
|
||
|
* The resolved date.
|
||
|
*/
|
||
|
private ChronoLocalDate<?> date;
|
||
|
/**
|
||
|
* The resolved time.
|
||
|
*/
|
||
|
private LocalTime time;
|
||
|
|
||
|
/**
|
||
|
* Creates an instance.
|
||
|
*/
|
||
|
Parsed() {
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a copy.
|
||
|
*/
|
||
|
Parsed copy() {
|
||
|
// only copy fields used in parsing stage
|
||
|
Parsed cloned = new Parsed();
|
||
|
cloned.fieldValues.putAll(this.fieldValues);
|
||
|
cloned.zone = this.zone;
|
||
|
cloned.chrono = this.chrono;
|
||
|
return cloned;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------
|
||
|
@Override
|
||
|
public boolean isSupported(TemporalField field) {
|
||
|
if (fieldValues.containsKey(field) ||
|
||
|
(date != null && date.isSupported(field)) ||
|
||
|
(time != null && time.isSupported(field))) {
|
||
|
return true;
|
||
|
}
|
||
|
return field != null && (field instanceof ChronoField == false) && field.isSupportedBy(this);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public long getLong(TemporalField field) {
|
||
|
Objects.requireNonNull(field, "field");
|
||
|
Long value = fieldValues.get(field);
|
||
|
if (value != null) {
|
||
|
return value;
|
||
|
}
|
||
|
if (date != null && date.isSupported(field)) {
|
||
|
return date.getLong(field);
|
||
|
}
|
||
|
if (time != null && time.isSupported(field)) {
|
||
|
return time.getLong(field);
|
||
|
}
|
||
|
if (field instanceof ChronoField) {
|
||
|
throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
|
||
|
}
|
||
|
return field.getFrom(this);
|
||
|
}
|
||
|
|
||
|
@SuppressWarnings("unchecked")
|
||
|
@Override
|
||
|
public <R> R query(TemporalQuery<R> query) {
|
||
|
if (query == TemporalQuery.zoneId()) {
|
||
|
return (R) zone;
|
||
|
} else if (query == TemporalQuery.chronology()) {
|
||
|
return (R) chrono;
|
||
|
} else if (query == TemporalQuery.localDate()) {
|
||
|
return (R) (date != null ? LocalDate.from(date) : null);
|
||
|
} else if (query == TemporalQuery.localTime()) {
|
||
|
return (R) time;
|
||
|
} else if (query == TemporalQuery.zone() || query == TemporalQuery.offset()) {
|
||
|
return query.queryFrom(this);
|
||
|
} else if (query == TemporalQuery.precision()) {
|
||
|
return null; // not a complete date/time
|
||
|
}
|
||
|
// inline TemporalAccessor.super.query(query) as an optimization
|
||
|
// non-JDK classes are not permitted to make this optimization
|
||
|
return query.queryFrom(this);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------
|
||
|
/**
|
||
|
* Resolves the fields in this context.
|
||
|
*
|
||
|
* @param resolverStyle the resolver style, not null
|
||
|
* @param resolverFields the fields to use for resolving, null for all fields
|
||
|
* @return this, for method chaining
|
||
|
* @throws DateTimeException if resolving one field results in a value for
|
||
|
* another field that is in conflict
|
||
|
*/
|
||
|
TemporalAccessor resolve(ResolverStyle resolverStyle, Set<TemporalField> resolverFields) {
|
||
|
if (resolverFields != null) {
|
||
|
fieldValues.keySet().retainAll(resolverFields);
|
||
|
}
|
||
|
this.resolverStyle = resolverStyle;
|
||
|
chrono = effectiveChrono;
|
||
|
resolveFields();
|
||
|
resolveTimeLenient();
|
||
|
crossCheck();
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------
|
||
|
private void resolveFields() {
|
||
|
// resolve ChronoField
|
||
|
resolveDateFields();
|
||
|
resolveTimeFields();
|
||
|
|
||
|
// if any other fields, handle them
|
||
|
// any lenient date resolution should return epoch-day
|
||
|
if (fieldValues.size() > 0) {
|
||
|
boolean changed = false;
|
||
|
outer:
|
||
|
while (true) {
|
||
|
for (Map.Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
|
||
|
TemporalField targetField = entry.getKey();
|
||
|
Map<TemporalField, Long> changes = targetField.resolve(this, entry.getValue(), resolverStyle);
|
||
|
if (changes != null) {
|
||
|
changed = true;
|
||
|
resolveFieldsMakeChanges(targetField, changes);
|
||
|
fieldValues.remove(targetField); // helps avoid infinite loops
|
||
|
continue outer; // have to restart to avoid concurrent modification
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
// if something changed then have to redo ChronoField resolve
|
||
|
if (changed) {
|
||
|
resolveDateFields();
|
||
|
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) {
|
||
|
Long old = fieldValues.put(changeField, changeValue);
|
||
|
if (old != null && old.longValue() != changeValue.longValue()) {
|
||
|
throw new DateTimeException("Conflict found: " + changeField + " " + old +
|
||
|
" differs from " + changeField + " " + changeValue +
|
||
|
" while resolving " + targetField);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------
|
||
|
private void resolveDateFields() {
|
||
|
updateCheckConflict(chrono.resolveDate(fieldValues, resolverStyle));
|
||
|
}
|
||
|
|
||
|
private void updateCheckConflict(ChronoLocalDate<?> cld) {
|
||
|
if (date != null) {
|
||
|
if (cld != null && date.equals(cld) == false) {
|
||
|
throw new DateTimeException("Conflict found: Fields resolved to two different dates: " + date + " " + cld);
|
||
|
}
|
||
|
} else {
|
||
|
date = cld;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------
|
||
|
private void resolveTimeFields() {
|
||
|
// simplify fields
|
||
|
if (fieldValues.containsKey(CLOCK_HOUR_OF_DAY)) {
|
||
|
long ch = fieldValues.remove(CLOCK_HOUR_OF_DAY);
|
||
|
updateCheckConflict(CLOCK_HOUR_OF_DAY, HOUR_OF_DAY, ch == 24 ? 0 : ch);
|
||
|
}
|
||
|
if (fieldValues.containsKey(CLOCK_HOUR_OF_AMPM)) {
|
||
|
long ch = fieldValues.remove(CLOCK_HOUR_OF_AMPM);
|
||
|
updateCheckConflict(CLOCK_HOUR_OF_AMPM, HOUR_OF_AMPM, ch == 12 ? 0 : ch);
|
||
|
}
|
||
|
if (fieldValues.containsKey(AMPM_OF_DAY) && fieldValues.containsKey(HOUR_OF_AMPM)) {
|
||
|
long ap = fieldValues.remove(AMPM_OF_DAY);
|
||
|
long hap = fieldValues.remove(HOUR_OF_AMPM);
|
||
|
updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, ap * 12 + hap);
|
||
|
}
|
||
|
if (fieldValues.containsKey(MICRO_OF_DAY)) {
|
||
|
long cod = fieldValues.remove(MICRO_OF_DAY);
|
||
|
updateCheckConflict(MICRO_OF_DAY, SECOND_OF_DAY, cod / 1_000_000L);
|
||
|
updateCheckConflict(MICRO_OF_DAY, MICRO_OF_SECOND, cod % 1_000_000L);
|
||
|
}
|
||
|
if (fieldValues.containsKey(MILLI_OF_DAY)) {
|
||
|
long lod = fieldValues.remove(MILLI_OF_DAY);
|
||
|
updateCheckConflict(MILLI_OF_DAY, SECOND_OF_DAY, lod / 1_000);
|
||
|
updateCheckConflict(MILLI_OF_DAY, MILLI_OF_SECOND, lod % 1_000);
|
||
|
}
|
||
|
if (fieldValues.containsKey(SECOND_OF_DAY)) {
|
||
|
long sod = fieldValues.remove(SECOND_OF_DAY);
|
||
|
updateCheckConflict(SECOND_OF_DAY, HOUR_OF_DAY, sod / 3600);
|
||
|
updateCheckConflict(SECOND_OF_DAY, MINUTE_OF_HOUR, (sod / 60) % 60);
|
||
|
updateCheckConflict(SECOND_OF_DAY, SECOND_OF_MINUTE, sod % 60);
|
||
|
}
|
||
|
if (fieldValues.containsKey(MINUTE_OF_DAY)) {
|
||
|
long mod = fieldValues.remove(MINUTE_OF_DAY);
|
||
|
updateCheckConflict(MINUTE_OF_DAY, HOUR_OF_DAY, mod / 60);
|
||
|
updateCheckConflict(MINUTE_OF_DAY, MINUTE_OF_HOUR, mod % 60);
|
||
|
}
|
||
|
|
||
|
// combine partial second fields strictly, leaving lenient expansion to later
|
||
|
if (fieldValues.containsKey(NANO_OF_SECOND)) {
|
||
|
long nos = fieldValues.get(NANO_OF_SECOND);
|
||
|
if (fieldValues.containsKey(MICRO_OF_SECOND)) {
|
||
|
long cos = fieldValues.remove(MICRO_OF_SECOND);
|
||
|
nos = cos * 1000 + (nos % 1000);
|
||
|
updateCheckConflict(MICRO_OF_SECOND, NANO_OF_SECOND, nos);
|
||
|
}
|
||
|
if (fieldValues.containsKey(MILLI_OF_SECOND)) {
|
||
|
long los = fieldValues.remove(MILLI_OF_SECOND);
|
||
|
updateCheckConflict(MILLI_OF_SECOND, NANO_OF_SECOND, los * 1_000_000L + (nos % 1_000_000L));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// convert to time if possible
|
||
|
if (fieldValues.containsKey(NANO_OF_DAY)) {
|
||
|
long nod = fieldValues.remove(NANO_OF_DAY);
|
||
|
updateCheckConflict(LocalTime.ofNanoOfDay(nod));
|
||
|
}
|
||
|
if (fieldValues.containsKey(HOUR_OF_DAY) && fieldValues.containsKey(MINUTE_OF_HOUR) &&
|
||
|
fieldValues.containsKey(SECOND_OF_MINUTE) && fieldValues.containsKey(NANO_OF_SECOND)) {
|
||
|
int hodVal = HOUR_OF_DAY.checkValidIntValue(fieldValues.remove(HOUR_OF_DAY));
|
||
|
int mohVal = MINUTE_OF_HOUR.checkValidIntValue(fieldValues.remove(MINUTE_OF_HOUR));
|
||
|
int somVal = SECOND_OF_MINUTE.checkValidIntValue(fieldValues.remove(SECOND_OF_MINUTE));
|
||
|
int nosVal = NANO_OF_SECOND.checkValidIntValue(fieldValues.remove(NANO_OF_SECOND));
|
||
|
updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void resolveTimeLenient() {
|
||
|
// leniently create a time from incomplete information
|
||
|
// done after everything else as it creates information from nothing
|
||
|
// which would break updateCheckConflict(field)
|
||
|
|
||
|
if (time == null) {
|
||
|
// can only get here if NANO_OF_SECOND not present
|
||
|
if (fieldValues.containsKey(MILLI_OF_SECOND)) {
|
||
|
long los = fieldValues.remove(MILLI_OF_SECOND);
|
||
|
if (fieldValues.containsKey(MICRO_OF_SECOND)) {
|
||
|
// merge milli-of-second and micro-of-second for better error message
|
||
|
long cos = los * 1_000 + (fieldValues.get(MICRO_OF_SECOND) % 1_000);
|
||
|
updateCheckConflict(MILLI_OF_SECOND, MICRO_OF_SECOND, cos);
|
||
|
fieldValues.remove(MICRO_OF_SECOND);
|
||
|
fieldValues.put(NANO_OF_SECOND, cos * 1_000L);
|
||
|
} else {
|
||
|
// convert milli-of-second to nano-of-second
|
||
|
fieldValues.put(NANO_OF_SECOND, los * 1_000_000L);
|
||
|
}
|
||
|
} else if (fieldValues.containsKey(MICRO_OF_SECOND)) {
|
||
|
// convert micro-of-second to nano-of-second
|
||
|
long cos = fieldValues.remove(MICRO_OF_SECOND);
|
||
|
fieldValues.put(NANO_OF_SECOND, cos * 1_000L);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// merge hour/minute/second/nano leniently
|
||
|
Long hod = fieldValues.get(HOUR_OF_DAY);
|
||
|
if (hod != null) {
|
||
|
int hodVal = HOUR_OF_DAY.checkValidIntValue(hod);
|
||
|
Long moh = fieldValues.get(MINUTE_OF_HOUR);
|
||
|
Long som = fieldValues.get(SECOND_OF_MINUTE);
|
||
|
Long nos = fieldValues.get(NANO_OF_SECOND);
|
||
|
|
||
|
// check for invalid combinations that cannot be defaulted
|
||
|
if (time == null) {
|
||
|
if ((moh == null && (som != null || nos != null)) ||
|
||
|
(moh != null && som == null && nos != null)) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// default as necessary and build time
|
||
|
int mohVal = (moh != null ? MINUTE_OF_HOUR.checkValidIntValue(moh) : (time != null ? time.getMinute() : 0));
|
||
|
int somVal = (som != null ? SECOND_OF_MINUTE.checkValidIntValue(som) : (time != null ? time.getSecond() : 0));
|
||
|
int nosVal = (nos != null ? NANO_OF_SECOND.checkValidIntValue(nos) : (time != null ? time.getNano() : 0));
|
||
|
updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal));
|
||
|
fieldValues.remove(HOUR_OF_DAY);
|
||
|
fieldValues.remove(MINUTE_OF_HOUR);
|
||
|
fieldValues.remove(SECOND_OF_MINUTE);
|
||
|
fieldValues.remove(NANO_OF_SECOND);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void updateCheckConflict(LocalTime lt) {
|
||
|
if (time != null) {
|
||
|
if (lt != null && time.equals(lt) == false) {
|
||
|
throw new DateTimeException("Conflict found: Fields resolved to two different times: " + time + " " + lt);
|
||
|
}
|
||
|
} else {
|
||
|
time = lt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------
|
||
|
private void crossCheck() {
|
||
|
// only cross-check date, time and date-time
|
||
|
// avoid object creation if possible
|
||
|
if (date != null) {
|
||
|
crossCheck(date);
|
||
|
}
|
||
|
if (time != null) {
|
||
|
crossCheck(time);
|
||
|
if (date != null && fieldValues.size() > 0) {
|
||
|
crossCheck(date.atTime(time));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void crossCheck(TemporalAccessor target) {
|
||
|
for (Iterator<Entry<TemporalField, Long>> it = fieldValues.entrySet().iterator(); it.hasNext(); ) {
|
||
|
Entry<TemporalField, Long> entry = it.next();
|
||
|
TemporalField field = entry.getKey();
|
||
|
long val1;
|
||
|
try {
|
||
|
val1 = target.getLong(field);
|
||
|
} catch (RuntimeException ex) {
|
||
|
continue;
|
||
|
}
|
||
|
long val2 = entry.getValue();
|
||
|
if (val1 != val2) {
|
||
|
throw new DateTimeException("Conflict found: Field " + field + " " + val1 +
|
||
|
" differs from " + field + " " + val2 + " derived from " + target);
|
||
|
}
|
||
|
it.remove();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
String str = fieldValues.toString() + "," + chrono + "," + zone;
|
||
|
if (date != null || time != null) {
|
||
|
str += " resolved to " + date + "," + time;
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
}
|