Skip to content

Instantly share code, notes, and snippets.

@jodastephen
Created March 7, 2013 18:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jodastephen/5110523 to your computer and use it in GitHub Desktop.
Save jodastephen/5110523 to your computer and use it in GitHub Desktop.
APPLIED: Refactor, enhance and test resolving logic
# HG changeset patch
# User scolebourne
# Date 1362681211 0
# Node ID 4eee025632151c4cb7c1f2c46f5125bb7bb5fc62
# Parent 8aca6a33db804462acda2c85fea8d7857be14774
Refactor, enhance and test resolving logic
Remove public resolveYearOfEra method
diff --git a/src/share/classes/java/time/chrono/Chronology.java b/src/share/classes/java/time/chrono/Chronology.java
--- a/src/share/classes/java/time/chrono/Chronology.java
+++ b/src/share/classes/java/time/chrono/Chronology.java
@@ -70,6 +70,7 @@
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.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
@@ -842,40 +843,19 @@
//-----------------------------------------------------------------------
/**
- * Resolves the year-of-era and era during parsing.
- * <p>
- * This combines the era and year-of-era into the single proleptic-year field.
- * A suitable default value for the era must be used if the era is not provided.
- * <p>
- * The default implementation defaults using the last era in the list
- * returned by {@link #eras()}.
- *
- * @param yearOfEra the chronology year-of-era
- * @param era the era of the correct type for the chronology, null if
- * the era must be defaulted by this method
- * @return the proleptic-year
- * @throws DateTimeException if unable to resolve the year
- */
- public long resolveYearOfEra(long yearOfEra, Long era) {
- int yoe = range(YEAR_OF_ERA).checkValidIntValue(yearOfEra, YEAR_OF_ERA);
- Era eraObj;
- if (era != null) {
- eraObj = eraOf(Math.toIntExact(era));
- } else {
- List<Era> eras = eras();
- if (eras.isEmpty()) {
- return yoe;
- } else {
- eraObj = eras.get(eras.size() - 1);
- }
- }
- return prolepticYear(eraObj, yoe);
- }
-
- /**
* Resolves parsed {@code ChronoField} values into a date during parsing.
* <p>
+ * Most {@code TemporalField} implementations are resolved using the
+ * resolve method on the field. By contrast, the {@code ChronoField} class
+ * defines fields that only have meaning relative to the chronology.
+ * As such, {@code ChronoField} date fields are resolved here in the
+ * context of a specific chronology.
+ * <p>
* The default implementation is suitable for most calendar systems.
+ * If {@link ChronoField#YEAR_OF_ERA} is found without an {@link ChronoField#ERA}
+ * then the last era in {@link #eras()} is used.
+ * The implementation assumes a 7 day week, that the first day-of-year
+ * has the value 1, and that proleptic-year zero is allowed.
*
* @param fieldValues the map of fields to values, which can be updated, not null
* @return the resolved date, null if insufficient information to create a date
@@ -883,21 +863,44 @@
* because of a conflict in the input data
*/
public ChronoLocalDate<?> resolveDate(Map<TemporalField, Long> fieldValues) {
- // year-of-era and era resolved to year before this method starts
-
+ // check epoch-day before inventing era
if (fieldValues.containsKey(EPOCH_DAY)) {
// TODO: really need a dateFromEpochDay() method
return date(LocalDate.ofEpochDay(fieldValues.remove(EPOCH_DAY)));
}
- if (fieldValues.containsKey(PROLEPTIC_MONTH)) {
- long em = fieldValues.remove(PROLEPTIC_MONTH);
- ChronoLocalDate<?> chronoDate = date(0, 1, 1);
- chronoDate = chronoDate.plus(em, ChronoUnit.MONTHS);
+ // fix proleptic month before inventing era
+ Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
+ if (pMonth != null) {
+ ChronoLocalDate<?> chronoDate = dateYearDay(0, 1);
+ chronoDate = chronoDate.plus(pMonth, ChronoUnit.MONTHS);
addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
}
+ // invent era if necessary to resolve year-of-era
+ Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
+ if (yoeLong != null) {
+ Long eraLong = fieldValues.remove(ERA);
+ int yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
+ if (eraLong != null) {
+ Era eraObj = eraOf(Math.toIntExact(eraLong));
+ addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
+ } else if (fieldValues.containsKey(YEAR)) {
+ int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
+ ChronoLocalDate<?> chronoDate = dateYearDay(year, 1);
+ addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
+ } else {
+ List<Era> eras = eras();
+ if (eras.isEmpty()) {
+ addFieldValue(fieldValues, YEAR, yoe);
+ } else {
+ Era eraObj = eras.get(eras.size() - 1);
+ addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
+ }
+ }
+ }
+
// build date
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
@@ -966,7 +969,7 @@
void addFieldValue(Map<TemporalField, Long> fieldValues, ChronoField field, long value) {
Long old = fieldValues.get(field); // check first for better error message
if (old != null && old.longValue() != value) {
- throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value + ": " + this);
+ throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value);
}
fieldValues.put(field, value);
}
diff --git a/src/share/classes/java/time/chrono/IsoChronology.java b/src/share/classes/java/time/chrono/IsoChronology.java
--- a/src/share/classes/java/time/chrono/IsoChronology.java
+++ b/src/share/classes/java/time/chrono/IsoChronology.java
@@ -70,9 +70,11 @@
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.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import java.io.Serializable;
import java.time.Clock;
@@ -398,31 +400,34 @@
}
@Override // override for performance
- public long resolveYearOfEra(long yearOfEra, Long era) {
- if (era == null || era.longValue() == 1L) {
- return yearOfEra;
- } else if (era.longValue() == 0L) {
- return Math.subtractExact(1, yearOfEra);
- } else {
- throw new DateTimeException("Invalid value for era: " + era);
- }
- }
-
- @Override // override for performance
public LocalDate resolveDate(Map<TemporalField, Long> fieldValues) {
- // year-of-era and era resolved to year before this method starts
-
+ // check epoch-day before inventing era
if (fieldValues.containsKey(EPOCH_DAY)) {
return LocalDate.ofEpochDay(fieldValues.remove(EPOCH_DAY));
}
- // normalize fields
- if (fieldValues.containsKey(PROLEPTIC_MONTH)) {
- long em = fieldValues.remove(PROLEPTIC_MONTH);
- addFieldValue(fieldValues, MONTH_OF_YEAR, Math.floorMod(em, 12) + 1);
- addFieldValue(fieldValues, YEAR, Math.floorDiv(em, 12));
+ // fix proleptic month before inventing era
+ Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
+ if (pMonth != null) {
+ addFieldValue(fieldValues, MONTH_OF_YEAR, Math.floorMod(pMonth, 12) + 1);
+ addFieldValue(fieldValues, YEAR, Math.floorDiv(pMonth, 12));
}
+ // invent era if necessary to resolve year-of-era
+ Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
+ if (yoeLong != null) {
+ Long era = fieldValues.remove(ERA);
+ if (era == null) {
+ Long year = fieldValues.get(YEAR);
+ addFieldValue(fieldValues, YEAR, (year == null || year > 0 ? yoeLong: Math.subtractExact(1, yoeLong)));
+ } else if (era.longValue() == 1L) {
+ addFieldValue(fieldValues, YEAR, yoeLong);
+ } else if (era.longValue() == 0L) {
+ addFieldValue(fieldValues, YEAR, Math.subtractExact(1, yoeLong));
+ } else {
+ throw new DateTimeException("Invalid value for era: " + era);
+ }
+ }
// build date
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
diff --git a/src/share/classes/java/time/format/Parsed.java b/src/share/classes/java/time/format/Parsed.java
--- a/src/share/classes/java/time/format/Parsed.java
+++ b/src/share/classes/java/time/format/Parsed.java
@@ -64,7 +64,6 @@
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.ERA;
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;
@@ -77,15 +76,12 @@
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 static java.time.temporal.ChronoField.YEAR;
-import static java.time.temporal.ChronoField.YEAR_OF_ERA;
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.ChronoLocalDateTime;
import java.time.chrono.Chronology;
import java.time.temporal.ChronoField;
import java.time.temporal.Queries;
@@ -224,34 +220,40 @@
TemporalAccessor resolve() {
chrono = effectiveChrono;
resolveFields();
- resolveDate();
- resolveTime();
+ resolveTimeLenient();
crossCheck();
return this;
}
//-----------------------------------------------------------------------
private void resolveFields() {
+ // resolve ChronoField
resolveDateFields();
resolveTimeFields();
- 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());
- if (changes != null) {
- changed = true;
- resolveFieldsMakeChanges(targetField, changes);
- fieldValues.remove(targetField); // helps avoid infinite loops
- continue outer; // have to restart to avoid concurrent modification
+
+ // 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());
+ 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;
}
- break;
- }
- if (changed) {
- resolveDateFields();
- resolveTimeFields();
+ // if something changed then have to redo ChronoField resolve
+ if (changed) {
+ resolveDateFields();
+ resolveTimeFields();
+ }
}
}
@@ -279,18 +281,151 @@
//-----------------------------------------------------------------------
private void resolveDateFields() {
- Long yoeVal = fieldValues.remove(YEAR_OF_ERA);
- if (yoeVal != null) {
- Long eraVal = fieldValues.remove(ERA);
- long year = chrono.resolveYearOfEra(yoeVal, eraVal);
- updateCheckConflict(YEAR_OF_ERA, YEAR, year);
+ updateCheckConflict(chrono.resolveDate(fieldValues));
+ }
+
+ 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 resolveDate() {
- date = chrono.resolveDate(fieldValues);
+ //-----------------------------------------------------------------------
+ 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
@@ -317,100 +452,14 @@
}
long val2 = entry.getValue();
if (val1 != val2) {
- throw new DateTimeException("Conflict found: Field " + field + " " + val1 + " differs from " + field + " " + val2 + " derived from " + date);
+ throw new DateTimeException("Conflict found: Field " + field + " " + val1 +
+ " differs from " + field + " " + val2 + " derived from " + target);
}
it.remove();
}
}
//-----------------------------------------------------------------------
- private void resolveTimeFields() {
- 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(NANO_OF_DAY)) {
- long nod = fieldValues.remove(NANO_OF_DAY);
- updateCheckConflict(NANO_OF_DAY, SECOND_OF_DAY, nod / 1000_000_000L);
- updateCheckConflict(NANO_OF_DAY, NANO_OF_SECOND, nod % 1000_000_000L);
- }
- if (fieldValues.containsKey(MICRO_OF_DAY)) {
- long cod = fieldValues.remove(MICRO_OF_DAY);
- updateCheckConflict(MICRO_OF_DAY, SECOND_OF_DAY, cod / 1000_000L);
- updateCheckConflict(MICRO_OF_DAY, MICRO_OF_SECOND, cod % 1000_000L);
- }
- if (fieldValues.containsKey(MILLI_OF_DAY)) {
- long lod = fieldValues.remove(MILLI_OF_DAY);
- updateCheckConflict(MILLI_OF_DAY, SECOND_OF_DAY, lod / 1000);
- updateCheckConflict(MILLI_OF_DAY, MILLI_OF_SECOND, lod % 1000);
- }
- 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);
- }
- if (fieldValues.containsKey(MICRO_OF_SECOND)) {
- long mos = fieldValues.remove(MICRO_OF_SECOND);
- Long nos = fieldValues.get(NANO_OF_SECOND);
- if (nos != null) {
- updateCheckConflict(MICRO_OF_SECOND, NANO_OF_SECOND, mos * 1000 + (nos % 1000));
- } else {
- fieldValues.put(NANO_OF_SECOND, mos * 1000);
- }
- }
- if (fieldValues.containsKey(MILLI_OF_SECOND)) {
- long mos = fieldValues.remove(MILLI_OF_SECOND);
- Long nos = fieldValues.get(NANO_OF_SECOND);
- if (nos != null) {
- updateCheckConflict(MILLI_OF_SECOND, NANO_OF_SECOND, mos * 1000000 + (nos % 1000000));
- } else {
- fieldValues.put(NANO_OF_SECOND, mos * 1000000);
- }
- }
- }
-
- private void resolveTime() {
- Long hod = fieldValues.get(HOUR_OF_DAY);
- Long moh = fieldValues.get(MINUTE_OF_HOUR);
- Long som = fieldValues.get(SECOND_OF_MINUTE);
- Long nos = fieldValues.get(NANO_OF_SECOND);
- if (hod != null) {
- int hodVal = HOUR_OF_DAY.checkValidIntValue(hod);
- if (moh != null) {
- int mohVal = MINUTE_OF_HOUR.checkValidIntValue(moh);
- if (som != null) {
- int somVal = SECOND_OF_MINUTE.checkValidIntValue(som);
- if (nos != null) {
- int nosVal = NANO_OF_SECOND.checkValidIntValue(nos);
- time = LocalTime.of(hodVal, mohVal, somVal, nosVal);
- } else {
- time = LocalTime.of(hodVal, mohVal, somVal);
- }
- } else {
- time = LocalTime.of(hodVal, mohVal);
- }
- } else {
- time = LocalTime.of(hodVal, 0);
- }
- }
- }
-
- //-----------------------------------------------------------------------
@Override
public String toString() {
String str = fieldValues.toString() + "," + chrono + "," + zone;
diff --git a/test/java/time/tck/java/time/format/TCKDateTimeParseResolver.java b/test/java/time/tck/java/time/format/TCKDateTimeParseResolver.java
new file mode 100644
--- /dev/null
+++ b/test/java/time/tck/java/time/format/TCKDateTimeParseResolver.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 tck.java.time.format;
+
+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.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.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.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.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.SECOND_OF_DAY;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static org.testng.Assert.assertEquals;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.IsoFields;
+import java.time.temporal.Queries;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * Test parse resolving.
+ */
+@Test
+public class TCKDateTimeParseResolver {
+ // TODO: tests with weird TenporalField implementations
+ // TODO: tests with non-ISO chronologies
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveOneNoChange")
+ Object[][] data_resolveOneNoChange() {
+ return new Object[][]{
+ {YEAR, 2012},
+ {MONTH_OF_YEAR, 8},
+ {DAY_OF_MONTH, 7},
+ {DAY_OF_YEAR, 6},
+ {DAY_OF_WEEK, 5},
+ };
+ }
+
+ @Test(dataProvider="resolveOneNoChange")
+ public void test_resolveOneNoChange(TemporalField field1, long value1) {
+ String str = Long.toString(value1);
+ DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), null);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ assertEquals(accessor.isSupported(field1), true);
+ assertEquals(accessor.getLong(field1), value1);
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveTwoNoChange")
+ Object[][] data_resolveTwoNoChange() {
+ return new Object[][]{
+ {YEAR, 2012, MONTH_OF_YEAR, 5},
+ {YEAR, 2012, DAY_OF_MONTH, 5},
+ {YEAR, 2012, DAY_OF_WEEK, 5},
+ {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5},
+ {YEAR, 2012, ALIGNED_WEEK_OF_MONTH, 5},
+ {YEAR, 2012, IsoFields.QUARTER_OF_YEAR, 3},
+ {YEAR, 2012, MINUTE_OF_HOUR, 5},
+ {YEAR, 2012, SECOND_OF_MINUTE, 5},
+ {YEAR, 2012, NANO_OF_SECOND, 5},
+
+ {MONTH_OF_YEAR, 5, DAY_OF_MONTH, 5},
+ {MONTH_OF_YEAR, 5, DAY_OF_WEEK, 5},
+ {MONTH_OF_YEAR, 5, ALIGNED_WEEK_OF_YEAR, 5},
+ {MONTH_OF_YEAR, 5, ALIGNED_WEEK_OF_MONTH, 5},
+ {MONTH_OF_YEAR, 3, IsoFields.QUARTER_OF_YEAR, 5},
+ {MONTH_OF_YEAR, 5, MINUTE_OF_HOUR, 5},
+ {MONTH_OF_YEAR, 5, SECOND_OF_MINUTE, 5},
+ {MONTH_OF_YEAR, 5, NANO_OF_SECOND, 5},
+ };
+ }
+
+ @Test(dataProvider="resolveTwoNoChange")
+ public void test_resolveTwoNoChange(TemporalField field1, long value1, TemporalField field2, long value2) {
+ String str = value1 + " " + value2;
+ DateTimeFormatter f = new DateTimeFormatterBuilder()
+ .appendValue(field1).appendLiteral(' ')
+ .appendValue(field2).toFormatter();
+ TemporalAccessor accessor = f.parse(str);
+
+ assertEquals(accessor.query(Queries.localDate()), null);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ assertEquals(accessor.isSupported(field1), true);
+ assertEquals(accessor.isSupported(field2), true);
+ assertEquals(accessor.getLong(field1), value1);
+ assertEquals(accessor.getLong(field2), value2);
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveThreeNoChange")
+ Object[][] data_resolveThreeNoChange() {
+ return new Object[][]{
+ {YEAR, 2012, MONTH_OF_YEAR, 5, DAY_OF_WEEK, 5},
+ {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, DAY_OF_MONTH, 5},
+ {YEAR, 2012, ALIGNED_WEEK_OF_MONTH, 5, DAY_OF_MONTH, 5},
+ {YEAR, 2012, MONTH_OF_YEAR, 5, DAY_OF_WEEK, 5},
+ {ERA, 1, MONTH_OF_YEAR, 5, DAY_OF_MONTH, 5},
+ {MONTH_OF_YEAR, 1, DAY_OF_MONTH, 5, IsoFields.QUARTER_OF_YEAR, 3},
+ {HOUR_OF_DAY, 1, SECOND_OF_MINUTE, 5, NANO_OF_SECOND, 5},
+ {MINUTE_OF_HOUR, 1, SECOND_OF_MINUTE, 5, NANO_OF_SECOND, 5},
+ };
+ }
+
+ @Test(dataProvider="resolveThreeNoChange")
+ public void test_resolveThreeNoChange(TemporalField field1, long value1, TemporalField field2, long value2, TemporalField field3, long value3) {
+ String str = value1 + " " + value2 + " " + value3;
+ DateTimeFormatter f = new DateTimeFormatterBuilder()
+ .appendValue(field1).appendLiteral(' ')
+ .appendValue(field2).appendLiteral(' ')
+ .appendValue(field3).toFormatter();
+ TemporalAccessor accessor = f.parse(str);
+
+ assertEquals(accessor.query(Queries.localDate()), null);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ assertEquals(accessor.isSupported(field1), true);
+ assertEquals(accessor.isSupported(field2), true);
+ assertEquals(accessor.isSupported(field3), true);
+ assertEquals(accessor.getLong(field1), value1);
+ assertEquals(accessor.getLong(field2), value2);
+ assertEquals(accessor.getLong(field3), value3);
+ }
+
+ //-----------------------------------------------------------------------
+ //-----------------------------------------------------------------------
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveOneToField")
+ Object[][] data_resolveOneToField() {
+ return new Object[][]{
+ {YEAR_OF_ERA, 2012, YEAR, 2012L, null, null},
+ {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), YEAR, 2012L, MONTH_OF_YEAR, 3L},
+
+ {CLOCK_HOUR_OF_AMPM, 8, HOUR_OF_AMPM, 8L, null, null},
+ {CLOCK_HOUR_OF_AMPM, 12, HOUR_OF_AMPM, 0L, null, null},
+ {MICRO_OF_SECOND, 12, NANO_OF_SECOND, 12_000L, null, null},
+ {MILLI_OF_SECOND, 12, NANO_OF_SECOND, 12_000_000L, null, null},
+ };
+ }
+
+ @Test(dataProvider="resolveOneToField")
+ public void test_resolveOneToField(TemporalField field1, long value1,
+ TemporalField expectedField1, Long expectedValue1,
+ TemporalField expectedField2, Long expectedValue2) {
+ String str = Long.toString(value1);
+ DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), null);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ if (expectedField1 != null) {
+ assertEquals(accessor.isSupported(expectedField1), true);
+ assertEquals(accessor.getLong(expectedField1), expectedValue1.longValue());
+ }
+ if (expectedField2 != null) {
+ assertEquals(accessor.isSupported(expectedField2), true);
+ assertEquals(accessor.getLong(expectedField2), expectedValue2.longValue());
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveOneToDate")
+ Object[][] data_resolveOneToDate() {
+ return new Object[][]{
+ {EPOCH_DAY, 32, LocalDate.of(1970, 2, 2)},
+ };
+ }
+
+ @Test(dataProvider="resolveOneToDate")
+ public void test_resolveOneToDate(TemporalField field1, long value1, LocalDate expectedDate) {
+ String str = Long.toString(value1);
+ DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), expectedDate);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveOneToTime")
+ Object[][] data_resolveOneToTime() {
+ return new Object[][]{
+ {HOUR_OF_DAY, 8, LocalTime.of(8, 0)},
+ {CLOCK_HOUR_OF_DAY, 8, LocalTime.of(8, 0)},
+ {CLOCK_HOUR_OF_DAY, 24, LocalTime.of(0, 0)},
+ {MINUTE_OF_DAY, 650, LocalTime.of(10, 50)},
+ {SECOND_OF_DAY, 3600 + 650, LocalTime.of(1, 10, 50)},
+ {MILLI_OF_DAY, (3600 + 650) * 1_000L + 2, LocalTime.of(1, 10, 50, 2_000_000)},
+ {MICRO_OF_DAY, (3600 + 650) * 1_000_000L + 2, LocalTime.of(1, 10, 50, 2_000)},
+ {NANO_OF_DAY, (3600 + 650) * 1_000_000_000L + 2, LocalTime.of(1, 10, 50, 2)},
+ };
+ }
+
+ @Test(dataProvider="resolveOneToTime")
+ public void test_resolveOneToTime(TemporalField field1, long value1, LocalTime expectedTime) {
+ String str = Long.toString(value1);
+ DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field1).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), null);
+ assertEquals(accessor.query(Queries.localTime()), expectedTime);
+ }
+
+ //-----------------------------------------------------------------------
+ //-----------------------------------------------------------------------
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveTwoToField")
+ Object[][] data_resolveTwoToField() {
+ return new Object[][]{
+ // cross-check
+ {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), YEAR, 2012, YEAR, 2012L, MONTH_OF_YEAR, 3L},
+ {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), YEAR_OF_ERA, 2012, YEAR, 2012L, MONTH_OF_YEAR, 3L},
+ {PROLEPTIC_MONTH, 2012 * 12L + (3 - 1), ERA, 1, YEAR, 2012L, MONTH_OF_YEAR, 3L},
+ {PROLEPTIC_MONTH, (3 - 1), YEAR, 0, YEAR, 0L, MONTH_OF_YEAR, 3L},
+ {PROLEPTIC_MONTH, (3 - 1), YEAR_OF_ERA, 1, YEAR, 0L, MONTH_OF_YEAR, 3L},
+ {PROLEPTIC_MONTH, (3 - 1), ERA, 0, YEAR, 0L, MONTH_OF_YEAR, 3L},
+ };
+ }
+
+ @Test(dataProvider="resolveTwoToField")
+ public void test_resolveTwoToField(TemporalField field1, long value1,
+ TemporalField field2, long value2,
+ TemporalField expectedField1, Long expectedValue1,
+ TemporalField expectedField2, Long expectedValue2) {
+ String str = value1 + " " + value2;
+ DateTimeFormatter f = new DateTimeFormatterBuilder()
+ .appendValue(field1).appendLiteral(' ')
+ .appendValue(field2).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), null);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ if (expectedField1 != null) {
+ assertEquals(accessor.isSupported(expectedField1), true);
+ assertEquals(accessor.getLong(expectedField1), expectedValue1.longValue());
+ }
+ if (expectedField2 != null) {
+ assertEquals(accessor.isSupported(expectedField2), true);
+ assertEquals(accessor.getLong(expectedField2), expectedValue2.longValue());
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveTwoToDate")
+ Object[][] data_resolveTwoToDate() {
+ return new Object[][]{
+ // merge
+ {YEAR, 2012, DAY_OF_YEAR, 32, LocalDate.of(2012, 2, 1)},
+ {YEAR_OF_ERA, 2012, DAY_OF_YEAR, 32, LocalDate.of(2012, 2, 1)},
+
+ // merge
+ {PROLEPTIC_MONTH, 2012 * 12 + (2 - 1), DAY_OF_MONTH, 25, LocalDate.of(2012, 2, 25)},
+ {PROLEPTIC_MONTH, 2012 * 12 + (2 - 1), DAY_OF_YEAR, 56, LocalDate.of(2012, 2, 25)},
+
+ // cross-check
+ {EPOCH_DAY, 32, ERA, 1, LocalDate.of(1970, 2, 2)},
+ {EPOCH_DAY, -146097 * 5L, ERA, 0, LocalDate.of(1970 - (400 * 5), 1, 1)},
+ {EPOCH_DAY, 32, YEAR, 1970, LocalDate.of(1970, 2, 2)},
+ {EPOCH_DAY, -146097 * 5L, YEAR, 1970 - (400 * 5), LocalDate.of(1970 - (400 * 5), 1, 1)},
+ {EPOCH_DAY, 32, YEAR_OF_ERA, 1970, LocalDate.of(1970, 2, 2)},
+ {EPOCH_DAY, -146097 * 5L, YEAR_OF_ERA, 1 - (1970 - (400 * 5)), LocalDate.of(1970 - (400 * 5), 1, 1)},
+ {EPOCH_DAY, 32, MONTH_OF_YEAR, 2, LocalDate.of(1970, 2, 2)},
+ {EPOCH_DAY, 32, DAY_OF_YEAR, 33, LocalDate.of(1970, 2, 2)},
+ {EPOCH_DAY, 32, DAY_OF_MONTH, 2, LocalDate.of(1970, 2, 2)},
+ {EPOCH_DAY, 32, DAY_OF_WEEK, 1, LocalDate.of(1970, 2, 2)},
+ };
+ }
+
+ @Test(dataProvider="resolveTwoToDate")
+ public void test_resolveTwoToDate(TemporalField field1, long value1,
+ TemporalField field2, long value2,
+ LocalDate expectedDate) {
+ String str = value1 + " " + value2;
+ DateTimeFormatter f = new DateTimeFormatterBuilder()
+ .appendValue(field1).appendLiteral(' ')
+ .appendValue(field2).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), expectedDate);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveTwoToTime")
+ Object[][] data_resolveTwoToTime() {
+ return new Object[][]{
+ // merge
+ {HOUR_OF_DAY, 8, MINUTE_OF_HOUR, 6, LocalTime.of(8, 6)},
+
+ // merge
+ {AMPM_OF_DAY, 0, HOUR_OF_AMPM, 5, LocalTime.of(5, 0)},
+ {AMPM_OF_DAY, 1, HOUR_OF_AMPM, 5, LocalTime.of(17, 0)},
+ {AMPM_OF_DAY, 0, CLOCK_HOUR_OF_AMPM, 5, LocalTime.of(5, 0)},
+ {AMPM_OF_DAY, 1, CLOCK_HOUR_OF_AMPM, 5, LocalTime.of(17, 0)},
+ {AMPM_OF_DAY, 0, HOUR_OF_DAY, 5, LocalTime.of(5, 0)},
+ {AMPM_OF_DAY, 1, HOUR_OF_DAY, 17, LocalTime.of(17, 0)},
+ {AMPM_OF_DAY, 0, CLOCK_HOUR_OF_DAY, 5, LocalTime.of(5, 0)},
+ {AMPM_OF_DAY, 1, CLOCK_HOUR_OF_DAY, 17, LocalTime.of(17, 0)},
+
+ // merge
+ {CLOCK_HOUR_OF_DAY, 8, MINUTE_OF_HOUR, 6, LocalTime.of(8, 6)},
+ {CLOCK_HOUR_OF_DAY, 24, MINUTE_OF_HOUR, 6, LocalTime.of(0, 6)},
+ // cross-check
+ {CLOCK_HOUR_OF_DAY, 8, HOUR_OF_DAY, 8, LocalTime.of(8, 0)},
+ {CLOCK_HOUR_OF_DAY, 8, CLOCK_HOUR_OF_AMPM, 8, LocalTime.of(8, 0)},
+ {CLOCK_HOUR_OF_DAY, 20, CLOCK_HOUR_OF_AMPM, 8, LocalTime.of(20, 0)},
+ {CLOCK_HOUR_OF_DAY, 8, AMPM_OF_DAY, 0, LocalTime.of(8, 0)},
+ {CLOCK_HOUR_OF_DAY, 20, AMPM_OF_DAY, 1, LocalTime.of(20, 0)},
+
+ // merge
+ {MINUTE_OF_DAY, 650, SECOND_OF_MINUTE, 8, LocalTime.of(10, 50, 8)},
+ // cross-check
+ {MINUTE_OF_DAY, 650, HOUR_OF_DAY, 10, LocalTime.of(10, 50)},
+ {MINUTE_OF_DAY, 650, CLOCK_HOUR_OF_DAY, 10, LocalTime.of(10, 50)},
+ {MINUTE_OF_DAY, 650, CLOCK_HOUR_OF_AMPM, 10, LocalTime.of(10, 50)},
+ {MINUTE_OF_DAY, 650, AMPM_OF_DAY, 0, LocalTime.of(10, 50)},
+ {MINUTE_OF_DAY, 650, MINUTE_OF_HOUR, 50, LocalTime.of(10, 50)},
+
+ // merge
+ {SECOND_OF_DAY, 3600 + 650, MILLI_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000_000)},
+ {SECOND_OF_DAY, 3600 + 650, MICRO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000)},
+ {SECOND_OF_DAY, 3600 + 650, NANO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2)},
+ // cross-check
+ {SECOND_OF_DAY, 3600 + 650, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50)},
+ {SECOND_OF_DAY, 3600 + 650, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50)},
+ {SECOND_OF_DAY, 3600 + 650, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50)},
+
+ // merge
+ {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, MICRO_OF_SECOND, 2_004, LocalTime.of(1, 10, 50, 2_004_000)},
+ {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, NANO_OF_SECOND, 2_000_004, LocalTime.of(1, 10, 50, 2_000_004)},
+ // cross-check
+ {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50, 2_000_000)},
+ {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50, 2_000_000)},
+ {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50, 2_000_000)},
+ {MILLI_OF_DAY, (3600 + 650) * 1000L + 2, MILLI_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000_000)},
+
+ // merge
+ {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, NANO_OF_SECOND, 2_004, LocalTime.of(1, 10, 50, 2_004)},
+ // cross-check
+ {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50, 2_000)},
+ {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50, 2_000)},
+ {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50, 2_000)},
+ {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, MILLI_OF_SECOND, 0, LocalTime.of(1, 10, 50, 2_000)},
+ {MICRO_OF_DAY, (3600 + 650) * 1000_000L + 2, MICRO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2_000)},
+
+ // cross-check
+ {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, HOUR_OF_DAY, 1, LocalTime.of(1, 10, 50, 2)},
+ {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, MINUTE_OF_HOUR, 10, LocalTime.of(1, 10, 50, 2)},
+ {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, SECOND_OF_MINUTE, 50, LocalTime.of(1, 10, 50, 2)},
+ {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, MILLI_OF_SECOND, 0, LocalTime.of(1, 10, 50, 2)},
+ {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, MICRO_OF_SECOND, 0, LocalTime.of(1, 10, 50, 2)},
+ {NANO_OF_DAY, (3600 + 650) * 1000_000_000L + 2, NANO_OF_SECOND, 2, LocalTime.of(1, 10, 50, 2)},
+ };
+ }
+
+ @Test(dataProvider="resolveTwoToTime")
+ public void test_resolveTwoToTime(TemporalField field1, long value1,
+ TemporalField field2, long value2,
+ LocalTime expectedTime) {
+ String str = value1 + " " + value2;
+ DateTimeFormatter f = new DateTimeFormatterBuilder()
+ .appendValue(field1).appendLiteral(' ')
+ .appendValue(field2).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), null);
+ assertEquals(accessor.query(Queries.localTime()), expectedTime);
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveThreeToDate")
+ Object[][] data_resolveThreeToDate() {
+ return new Object[][]{
+ // merge
+ {YEAR, 2012, MONTH_OF_YEAR, 2, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
+ {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, LocalDate.of(2012, 2, 1)},
+ {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
+
+ // cross-check
+ {YEAR, 2012, DAY_OF_YEAR, 32, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
+ {YEAR_OF_ERA, 2012, DAY_OF_YEAR, 32, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
+ {YEAR, 2012, DAY_OF_YEAR, 32, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
+ {PROLEPTIC_MONTH, 2012 * 12 + (2 - 1), DAY_OF_MONTH, 25, DAY_OF_WEEK, 6, LocalDate.of(2012, 2, 25)},
+ };
+ }
+
+ @Test(dataProvider="resolveThreeToDate")
+ public void test_resolveThreeToDate(TemporalField field1, long value1,
+ TemporalField field2, long value2,
+ TemporalField field3, long value3,
+ LocalDate expectedDate) {
+ String str = value1 + " " + value2 + " " + value3;
+ DateTimeFormatter f = new DateTimeFormatterBuilder()
+ .appendValue(field1).appendLiteral(' ')
+ .appendValue(field2).appendLiteral(' ')
+ .appendValue(field3).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), expectedDate);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ }
+
+ //-----------------------------------------------------------------------
+ @DataProvider(name="resolveFourToDate")
+ Object[][] data_resolveFourToDate() {
+ return new Object[][]{
+ // merge
+ {YEAR, 2012, MONTH_OF_YEAR, 2, ALIGNED_WEEK_OF_MONTH, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, LocalDate.of(2012, 2, 1)},
+ {YEAR, 2012, MONTH_OF_YEAR, 2, ALIGNED_WEEK_OF_MONTH, 1, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
+
+ // cross-check
+ {YEAR, 2012, MONTH_OF_YEAR, 2, DAY_OF_MONTH, 1, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
+ {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, DAY_OF_WEEK, 3, LocalDate.of(2012, 2, 1)},
+ {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 5, DAY_OF_WEEK, 3, DAY_OF_MONTH, 1, LocalDate.of(2012, 2, 1)},
+ };
+ }
+
+ @Test(dataProvider="resolveFourToDate")
+ public void test_resolveFourToDate(TemporalField field1, long value1,
+ TemporalField field2, long value2,
+ TemporalField field3, long value3,
+ TemporalField field4, long value4,
+ LocalDate expectedDate) {
+ String str = value1 + " " + value2 + " " + value3 + " " + value4;
+ DateTimeFormatter f = new DateTimeFormatterBuilder()
+ .appendValue(field1).appendLiteral(' ')
+ .appendValue(field2).appendLiteral(' ')
+ .appendValue(field3).appendLiteral(' ')
+ .appendValue(field4).toFormatter();
+
+ TemporalAccessor accessor = f.parse(str);
+ assertEquals(accessor.query(Queries.localDate()), expectedDate);
+ assertEquals(accessor.query(Queries.localTime()), null);
+ }
+
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment