提交 0f6947ef 编写于 作者: R rriggs

8024834: Better return type for TemporalField resolve

Summary: Allow resolve method to return more than just ChronoLocalDate
Reviewed-by: sherman
Contributed-by: scolebourne@joda.org
上级 8975a12d
......@@ -83,6 +83,8 @@ import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
......@@ -260,11 +262,34 @@ final class Parsed implements TemporalAccessor {
while (changedCount < 50) {
for (Map.Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
TemporalField targetField = entry.getKey();
ChronoLocalDate resolvedDate = targetField.resolve(fieldValues, chrono, zone, resolverStyle);
if (resolvedDate != null) {
updateCheckConflict(resolvedDate);
changedCount++;
continue outer; // have to restart to avoid concurrent modification
TemporalAccessor resolvedObject = targetField.resolve(fieldValues, chrono, zone, resolverStyle);
if (resolvedObject != null) {
if (resolvedObject instanceof ChronoZonedDateTime) {
ChronoZonedDateTime czdt = (ChronoZonedDateTime) resolvedObject;
if (zone.equals(czdt.getZone()) == false) {
throw new DateTimeException("ChronoZonedDateTime must use the effective parsed zone: " + zone);
}
resolvedObject = czdt.toLocalDateTime();
}
if (resolvedObject instanceof ChronoLocalDateTime) {
ChronoLocalDateTime cldt = (ChronoLocalDateTime) resolvedObject;
updateCheckConflict(cldt.toLocalTime(), Period.ZERO);
updateCheckConflict(cldt.toLocalDate());
changedCount++;
continue outer; // have to restart to avoid concurrent modification
}
if (resolvedObject instanceof ChronoLocalDate) {
updateCheckConflict((ChronoLocalDate) resolvedObject);
changedCount++;
continue outer; // have to restart to avoid concurrent modification
}
if (resolvedObject instanceof LocalTime) {
updateCheckConflict((LocalTime) resolvedObject, Period.ZERO);
changedCount++;
continue outer; // have to restart to avoid concurrent modification
}
throw new DateTimeException("Method resolveFields() can only return ChronoZonedDateTime," +
"ChronoLocalDateTime, ChronoLocalDate or LocalTime");
} else if (fieldValues.containsKey(targetField) == false) {
changedCount++;
continue outer; // have to restart to avoid concurrent modification
......@@ -302,7 +327,10 @@ final class Parsed implements TemporalAccessor {
if (cld != null && date.equals(cld) == false) {
throw new DateTimeException("Conflict found: Fields resolved to two different dates: " + date + " " + cld);
}
} else {
} else if (cld != null) {
if (chrono.equals(cld.getChronology()) == false) {
throw new DateTimeException("ChronoLocalDate must use the effective parsed chronology: " + chrono);
}
date = cld;
}
}
......
......@@ -63,7 +63,6 @@ package java.time.temporal;
import java.time.DateTimeException;
import java.time.ZoneId;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Chronology;
import java.time.format.ResolverStyle;
import java.util.Locale;
......@@ -350,6 +349,10 @@ public interface TemporalField {
* be acceptable for the date fields to be resolved into other {@code ChronoField}
* instances that can produce a date, such as {@code EPOCH_DAY}.
* <p>
* Not all {@code TemporalAccessor} implementations are accepted as return values.
* Implementations must accept {@code ChronoLocalDate}, {@code ChronoLocalDateTime},
* {@code ChronoZonedDateTime} and {@code LocalTime}.
* <p>
* The zone is not normally required for resolution, but is provided for completeness.
* <p>
* The default implementation must return null.
......@@ -358,13 +361,13 @@ public interface TemporalField {
* @param chronology the effective chronology, not null
* @param zone the effective zone, not null
* @param resolverStyle the requested type of resolve, not null
* @return the resolved date; null if resolving only changed the map,
* or no resolve occurred
* @return the resolved temporal object; null if resolving only
* changed the map, or no resolve occurred
* @throws ArithmeticException if numeric overflow occurs
* @throws DateTimeException if resolving results in an error. This must not be thrown
* by querying a field on the temporal without first checking if it is supported
*/
default ChronoLocalDate resolve(
default TemporalAccessor resolve(
Map<TemporalField, Long> fieldValues, Chronology chronology,
ZoneId zone, ResolverStyle resolverStyle) {
return null;
......
......@@ -90,20 +90,33 @@ 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 java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.ThaiBuddhistChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoUnit;
import java.time.temporal.IsoFields;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ValueRange;
import java.util.Map;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
......@@ -872,4 +885,210 @@ public class TCKDateTimeParseResolver {
}
}
//-----------------------------------------------------------------------
@Test
public void test_fieldResolvesToLocalTime() {
TemporalField field = new TemporalField() {
@Override
public TemporalUnit getBaseUnit() {
throw new UnsupportedOperationException();
}
@Override
public TemporalUnit getRangeUnit() {
throw new UnsupportedOperationException();
}
@Override
public ValueRange range() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDateBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isTimeBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isSupportedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public long getFrom(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
throw new UnsupportedOperationException();
}
@Override
public TemporalAccessor resolve(
Map<TemporalField, Long> fieldValues, Chronology chronology,
ZoneId zone, ResolverStyle resolverStyle) {
return LocalTime.MIDNIGHT.plusNanos(fieldValues.remove(this));
}
};
DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field).toFormatter();
TemporalAccessor accessor = f.parse("1234567890");
assertEquals(accessor.query(TemporalQuery.localDate()), null);
assertEquals(accessor.query(TemporalQuery.localTime()), LocalTime.of(0, 0, 1, 234_567_890));
}
@Test
public void test_fieldResolvesToChronoLocalDateTime() {
TemporalField field = new TemporalField() {
@Override
public TemporalUnit getBaseUnit() {
throw new UnsupportedOperationException();
}
@Override
public TemporalUnit getRangeUnit() {
throw new UnsupportedOperationException();
}
@Override
public ValueRange range() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDateBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isTimeBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isSupportedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public long getFrom(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
throw new UnsupportedOperationException();
}
@Override
public TemporalAccessor resolve(
Map<TemporalField, Long> fieldValues, Chronology chronology,
ZoneId zone, ResolverStyle resolverStyle) {
fieldValues.remove(this);
return LocalDateTime.of(2010, 6, 30, 12, 30);
}
};
DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field).toFormatter();
TemporalAccessor accessor = f.parse("1234567890");
assertEquals(accessor.query(TemporalQuery.localDate()), LocalDate.of(2010, 6, 30));
assertEquals(accessor.query(TemporalQuery.localTime()), LocalTime.of(12, 30));
}
@Test(expectedExceptions = DateTimeParseException.class)
public void test_fieldResolvesWrongChrono() {
TemporalField field = new TemporalField() {
@Override
public TemporalUnit getBaseUnit() {
throw new UnsupportedOperationException();
}
@Override
public TemporalUnit getRangeUnit() {
throw new UnsupportedOperationException();
}
@Override
public ValueRange range() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDateBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isTimeBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isSupportedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public long getFrom(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
throw new UnsupportedOperationException();
}
@Override
public TemporalAccessor resolve(
Map<TemporalField, Long> fieldValues, Chronology chronology,
ZoneId zone, ResolverStyle resolverStyle) {
return ThaiBuddhistChronology.INSTANCE.dateNow();
}
};
DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field).toFormatter();
f.parse("1234567890");
}
@Test(expectedExceptions = DateTimeParseException.class)
public void test_fieldResolvesWrongZone() {
TemporalField field = new TemporalField() {
@Override
public TemporalUnit getBaseUnit() {
throw new UnsupportedOperationException();
}
@Override
public TemporalUnit getRangeUnit() {
throw new UnsupportedOperationException();
}
@Override
public ValueRange range() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDateBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isTimeBased() {
throw new UnsupportedOperationException();
}
@Override
public boolean isSupportedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public long getFrom(TemporalAccessor temporal) {
throw new UnsupportedOperationException();
}
@Override
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
throw new UnsupportedOperationException();
}
@Override
public TemporalAccessor resolve(
Map<TemporalField, Long> fieldValues, Chronology chronology,
ZoneId zone, ResolverStyle resolverStyle) {
return ZonedDateTime.of(2010, 6, 30, 12, 30, 0, 0, ZoneId.of("Europe/Paris"));
}
};
DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(field).toFormatter().withZone(ZoneId.of("Europe/London"));
f.parse("1234567890");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册