Created
March 7, 2013 18:34
-
-
Save jodastephen/5110523 to your computer and use it in GitHub Desktop.
APPLIED: Refactor, enhance and test resolving logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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