/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* 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.
INSTANT_SECONDS;
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.
OFFSET_SECONDS;
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.
Instant;
import java.time.
LocalDate;
import java.time.
LocalTime;
import java.time.
Period;
import java.time.
ZoneId;
import java.time.
ZoneOffset;
import java.time.chrono.
ChronoLocalDate;
import java.time.chrono.
ChronoLocalDateTime;
import java.time.chrono.
ChronoZonedDateTime;
import java.time.chrono.
Chronology;
import java.time.temporal.
ChronoField;
import java.time.temporal.
TemporalAccessor;
import java.time.temporal.
TemporalField;
import java.time.temporal.
TemporalQueries;
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.
*
* @implSpec
* 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;
/**
* Whether a leap-second is parsed.
*/
boolean
leapSecond;
/**
* The resolver style to use.
*/
private
ResolverStyle resolverStyle;
/**
* The resolved date.
*/
private
ChronoLocalDate date;
/**
* The resolved time.
*/
private
LocalTime time;
/**
* The excess period from time-only parsing.
*/
Period excessDays =
Period.
ZERO;
/**
* 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;
cloned.
leapSecond = this.
leapSecond;
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);
}
return
field.
getFrom(this);
}
@
SuppressWarnings("unchecked")
@
Override
public <R> R
query(
TemporalQuery<R>
query) {
if (
query ==
TemporalQueries.
zoneId()) {
return (R)
zone;
} else if (
query ==
TemporalQueries.
chronology()) {
return (R)
chrono;
} else if (
query ==
TemporalQueries.
localDate()) {
return (R) (
date != null ?
LocalDate.
from(
date) : null);
} else if (
query ==
TemporalQueries.
localTime()) {
return (R)
time;
} else if (
query ==
TemporalQueries.
zone() ||
query ==
TemporalQueries.
offset()) {
return
query.
queryFrom(this);
} else if (
query ==
TemporalQueries.
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;
resolveFields();
resolveTimeLenient();
crossCheck();
resolvePeriod();
resolveFractional();
resolveInstant();
return this;
}
//-----------------------------------------------------------------------
private void
resolveFields() {
// resolve ChronoField
resolveInstantFields();
resolveDateFields();
resolveTimeFields();
// if any other fields, handle them
// any lenient date resolution should return epoch-day
if (
fieldValues.
size() > 0) {
int
changedCount = 0;
outer:
while (
changedCount < 50) {
for (
Map.
Entry<
TemporalField,
Long>
entry :
fieldValues.
entrySet()) {
TemporalField targetField =
entry.
getKey();
TemporalAccessor resolvedObject =
targetField.
resolve(
fieldValues, this,
resolverStyle);
if (
resolvedObject != null) {
if (
resolvedObject instanceof
ChronoZonedDateTime) {
ChronoZonedDateTime<?>
czdt = (
ChronoZonedDateTime<?>)
resolvedObject;
if (
zone == null) {
zone =
czdt.
getZone();
} else if (
zone.
equals(
czdt.
getZone()) == false) {
throw new
DateTimeException("ChronoZonedDateTime must use the effective parsed zone: " +
zone);
}
resolvedObject =
czdt.
toLocalDateTime();
}
if (
resolvedObject instanceof
ChronoLocalDateTime) {
ChronoLocalDateTime<?>
cldt = (
ChronoLocalDateTime<?>)
resolvedObject;
updateCheckConflict(
cldt.
toLocalTime(),
Period.
ZERO);
updateCheckConflict(
cldt.
toLocalDate());
changedCount++;
continue
outer; // have to restart to avoid concurrent modification
}
if (
resolvedObject instanceof
ChronoLocalDate) {
updateCheckConflict((
ChronoLocalDate)
resolvedObject);
changedCount++;
continue
outer; // have to restart to avoid concurrent modification
}
if (
resolvedObject instanceof
LocalTime) {
updateCheckConflict((
LocalTime)
resolvedObject,
Period.
ZERO);
changedCount++;
continue
outer; // have to restart to avoid concurrent modification
}
throw new
DateTimeException("Method resolve() can only return ChronoZonedDateTime, " +
"ChronoLocalDateTime, ChronoLocalDate or LocalTime");
} else if (
fieldValues.
containsKey(
targetField) == false) {
changedCount++;
continue
outer; // have to restart to avoid concurrent modification
}
}
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 (
changedCount > 0) {
resolveInstantFields();
resolveDateFields();
resolveTimeFields();
}
}
}
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
resolveInstantFields() {
// resolve parsed instant seconds to date and time if zone available
if (
fieldValues.
containsKey(
INSTANT_SECONDS)) {
if (
zone != null) {
resolveInstantFields0(
zone);
} else {
Long offsetSecs =
fieldValues.
get(
OFFSET_SECONDS);
if (
offsetSecs != null) {
ZoneOffset offset =
ZoneOffset.
ofTotalSeconds(
offsetSecs.
intValue());
resolveInstantFields0(
offset);
}
}
}
}
private void
resolveInstantFields0(
ZoneId selectedZone) {
Instant instant =
Instant.
ofEpochSecond(
fieldValues.
remove(
INSTANT_SECONDS));
ChronoZonedDateTime<?>
zdt =
chrono.
zonedDateTime(
instant,
selectedZone);
updateCheckConflict(
zdt.
toLocalDate());
updateCheckConflict(
INSTANT_SECONDS,
SECOND_OF_DAY, (long)
zdt.
toLocalTime().
toSecondOfDay());
}
//-----------------------------------------------------------------------
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 if (
cld != null) {
if (
chrono.
equals(
cld.
getChronology()) == false) {
throw new
DateTimeException("ChronoLocalDate must use the effective parsed chronology: " +
chrono);
}
date =
cld;
}
}
//-----------------------------------------------------------------------
private void
resolveTimeFields() {
// simplify fields
if (
fieldValues.
containsKey(
CLOCK_HOUR_OF_DAY)) {
// lenient allows anything, smart allows 0-24, strict allows 1-24
long
ch =
fieldValues.
remove(
CLOCK_HOUR_OF_DAY);
if (
resolverStyle ==
ResolverStyle.
STRICT || (
resolverStyle ==
ResolverStyle.
SMART &&
ch != 0)) {
CLOCK_HOUR_OF_DAY.
checkValidValue(
ch);
}
updateCheckConflict(
CLOCK_HOUR_OF_DAY,
HOUR_OF_DAY,
ch == 24 ? 0 :
ch);
}
if (
fieldValues.
containsKey(
CLOCK_HOUR_OF_AMPM)) {
// lenient allows anything, smart allows 0-12, strict allows 1-12
long
ch =
fieldValues.
remove(
CLOCK_HOUR_OF_AMPM);
if (
resolverStyle ==
ResolverStyle.
STRICT || (
resolverStyle ==
ResolverStyle.
SMART &&
ch != 0)) {
CLOCK_HOUR_OF_AMPM.
checkValidValue(
ch);
}
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);
if (
resolverStyle ==
ResolverStyle.
LENIENT) {
updateCheckConflict(
AMPM_OF_DAY,
HOUR_OF_DAY,
Math.
addExact(
Math.
multiplyExact(
ap, 12),
hap));
} else { // STRICT or SMART
AMPM_OF_DAY.
checkValidValue(
ap);
HOUR_OF_AMPM.
checkValidValue(
ap);
updateCheckConflict(
AMPM_OF_DAY,
HOUR_OF_DAY,
ap * 12 +
hap);
}
}
if (
fieldValues.
containsKey(
NANO_OF_DAY)) {
long
nod =
fieldValues.
remove(
NANO_OF_DAY);
if (
resolverStyle !=
ResolverStyle.
LENIENT) {
NANO_OF_DAY.
checkValidValue(
nod);
}
updateCheckConflict(
NANO_OF_DAY,
HOUR_OF_DAY,
nod / 3600_000_000_000L);
updateCheckConflict(
NANO_OF_DAY,
MINUTE_OF_HOUR, (
nod / 60_000_000_000L) % 60);
updateCheckConflict(
NANO_OF_DAY,
SECOND_OF_MINUTE, (
nod / 1_000_000_000L) % 60);
updateCheckConflict(
NANO_OF_DAY,
NANO_OF_SECOND,
nod % 1_000_000_000L);
}
if (
fieldValues.
containsKey(
MICRO_OF_DAY)) {
long
cod =
fieldValues.
remove(
MICRO_OF_DAY);
if (
resolverStyle !=
ResolverStyle.
LENIENT) {
MICRO_OF_DAY.
checkValidValue(
cod);
}
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);
if (
resolverStyle !=
ResolverStyle.
LENIENT) {
MILLI_OF_DAY.
checkValidValue(
lod);
}
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);
if (
resolverStyle !=
ResolverStyle.
LENIENT) {
SECOND_OF_DAY.
checkValidValue(
sod);
}
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);
if (
resolverStyle !=
ResolverStyle.
LENIENT) {
MINUTE_OF_DAY.
checkValidValue(
mod);
}
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 (
resolverStyle !=
ResolverStyle.
LENIENT) {
NANO_OF_SECOND.
checkValidValue(
nos);
}
if (
fieldValues.
containsKey(
MICRO_OF_SECOND)) {
long
cos =
fieldValues.
remove(
MICRO_OF_SECOND);
if (
resolverStyle !=
ResolverStyle.
LENIENT) {
MICRO_OF_SECOND.
checkValidValue(
cos);
}
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);
if (
resolverStyle !=
ResolverStyle.
LENIENT) {
MILLI_OF_SECOND.
checkValidValue(
los);
}
updateCheckConflict(
MILLI_OF_SECOND,
NANO_OF_SECOND,
los * 1_000_000L + (
nos % 1_000_000L));
}
}
// convert to time if all four fields available (optimization)
if (
fieldValues.
containsKey(
HOUR_OF_DAY) &&
fieldValues.
containsKey(
MINUTE_OF_HOUR) &&
fieldValues.
containsKey(
SECOND_OF_MINUTE) &&
fieldValues.
containsKey(
NANO_OF_SECOND)) {
long
hod =
fieldValues.
remove(
HOUR_OF_DAY);
long
moh =
fieldValues.
remove(
MINUTE_OF_HOUR);
long
som =
fieldValues.
remove(
SECOND_OF_MINUTE);
long
nos =
fieldValues.
remove(
NANO_OF_SECOND);
resolveTime(
hod,
moh,
som,
nos);
}
}
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) {
// NANO_OF_SECOND merged with MILLI/MICRO above
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) {
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 ((
moh == null && (
som != null ||
nos != null)) ||
(
moh != null &&
som == null &&
nos != null)) {
return;
}
// default as necessary and build time
long
mohVal = (
moh != null ?
moh : 0);
long
somVal = (
som != null ?
som : 0);
long
nosVal = (
nos != null ?
nos : 0);
resolveTime(
hod,
mohVal,
somVal,
nosVal);
fieldValues.
remove(
HOUR_OF_DAY);
fieldValues.
remove(
MINUTE_OF_HOUR);
fieldValues.
remove(
SECOND_OF_MINUTE);
fieldValues.
remove(
NANO_OF_SECOND);
}
}
// validate remaining
if (
resolverStyle !=
ResolverStyle.
LENIENT &&
fieldValues.
size() > 0) {
for (
Entry<
TemporalField,
Long>
entry :
fieldValues.
entrySet()) {
TemporalField field =
entry.
getKey();
if (
field instanceof
ChronoField &&
field.
isTimeBased()) {
((
ChronoField)
field).
checkValidValue(
entry.
getValue());
}
}
}
}
private void
resolveTime(long
hod, long
moh, long
som, long
nos) {
if (
resolverStyle ==
ResolverStyle.
LENIENT) {
long
totalNanos =
Math.
multiplyExact(
hod, 3600_000_000_000L);
totalNanos =
Math.
addExact(
totalNanos,
Math.
multiplyExact(
moh, 60_000_000_000L));
totalNanos =
Math.
addExact(
totalNanos,
Math.
multiplyExact(
som, 1_000_000_000L));
totalNanos =
Math.
addExact(
totalNanos,
nos);
int
excessDays = (int)
Math.
floorDiv(
totalNanos, 86400_000_000_000L); // safe int cast
long
nod =
Math.
floorMod(
totalNanos, 86400_000_000_000L);
updateCheckConflict(
LocalTime.
ofNanoOfDay(
nod),
Period.
ofDays(
excessDays));
} else { // STRICT or SMART
int
mohVal =
MINUTE_OF_HOUR.
checkValidIntValue(
moh);
int
nosVal =
NANO_OF_SECOND.
checkValidIntValue(
nos);
// handle 24:00 end of day
if (
resolverStyle ==
ResolverStyle.
SMART &&
hod == 24 &&
mohVal == 0 &&
som == 0 &&
nosVal == 0) {
updateCheckConflict(
LocalTime.
MIDNIGHT,
Period.
ofDays(1));
} else {
int
hodVal =
HOUR_OF_DAY.
checkValidIntValue(
hod);
int
somVal =
SECOND_OF_MINUTE.
checkValidIntValue(
som);
updateCheckConflict(
LocalTime.
of(
hodVal,
mohVal,
somVal,
nosVal),
Period.
ZERO);
}
}
}
private void
resolvePeriod() {
// add whole days if we have both date and time
if (
date != null &&
time != null &&
excessDays.
isZero() == false) {
date =
date.
plus(
excessDays);
excessDays =
Period.
ZERO;
}
}
private void
resolveFractional() {
// ensure fractional seconds available as ChronoField requires
// resolveTimeLenient() will have merged MICRO_OF_SECOND/MILLI_OF_SECOND to NANO_OF_SECOND
if (
time == null &&
(
fieldValues.
containsKey(
INSTANT_SECONDS) ||
fieldValues.
containsKey(
SECOND_OF_DAY) ||
fieldValues.
containsKey(
SECOND_OF_MINUTE))) {
if (
fieldValues.
containsKey(
NANO_OF_SECOND)) {
long
nos =
fieldValues.
get(
NANO_OF_SECOND);
fieldValues.
put(
MICRO_OF_SECOND,
nos / 1000);
fieldValues.
put(
MILLI_OF_SECOND,
nos / 1000000);
} else {
fieldValues.
put(
NANO_OF_SECOND, 0L);
fieldValues.
put(
MICRO_OF_SECOND, 0L);
fieldValues.
put(
MILLI_OF_SECOND, 0L);
}
}
}
private void
resolveInstant() {
// add instant seconds if we have date, time and zone
if (
date != null &&
time != null) {
if (
zone != null) {
long
instant =
date.
atTime(
time).
atZone(
zone).
getLong(
ChronoField.
INSTANT_SECONDS);
fieldValues.
put(
INSTANT_SECONDS,
instant);
} else {
Long offsetSecs =
fieldValues.
get(
OFFSET_SECONDS);
if (
offsetSecs != null) {
ZoneOffset offset =
ZoneOffset.
ofTotalSeconds(
offsetSecs.
intValue());
long
instant =
date.
atTime(
time).
atZone(
offset).
getLong(
ChronoField.
INSTANT_SECONDS);
fieldValues.
put(
INSTANT_SECONDS,
instant);
}
}
}
}
private void
updateCheckConflict(
LocalTime timeToSet,
Period periodToSet) {
if (
time != null) {
if (
time.
equals(
timeToSet) == false) {
throw new
DateTimeException("Conflict found: Fields resolved to different times: " +
time + " " +
timeToSet);
}
if (
excessDays.
isZero() == false &&
periodToSet.
isZero() == false &&
excessDays.
equals(
periodToSet) == false) {
throw new
DateTimeException("Conflict found: Fields resolved to different excess periods: " +
excessDays + " " +
periodToSet);
} else {
excessDays =
periodToSet;
}
} else {
time =
timeToSet;
excessDays =
periodToSet;
}
}
//-----------------------------------------------------------------------
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();
if (
target.
isSupported(
field)) {
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() {
StringBuilder buf = new
StringBuilder(64);
buf.
append(
fieldValues).
append(',').
append(
chrono);
if (
zone != null) {
buf.
append(',').
append(
zone);
}
if (
date != null ||
time != null) {
buf.
append(" resolved to ");
if (
date != null) {
buf.
append(
date);
if (
time != null) {
buf.
append('T').
append(
time);
}
} else {
buf.
append(
time);
}
}
return
buf.
toString();
}
}