mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-17 18:30:27 +00:00
8025828: Late binding of Chronology to appendValueReduced
Add a listener to the parseContext called when the Chronology changes Reviewed-by: sherman
This commit is contained in:
parent
a3fda5b9bc
commit
d7f0eeb825
@ -557,12 +557,13 @@ public final class DateTimeFormatterBuilder {
|
||||
* a two digit year parse will be in the range 1950-01-01 to 2049-12-31.
|
||||
* Only the year would be extracted from the date, thus a base date of
|
||||
* 1950-08-25 would also parse to the range 1950-01-01 to 2049-12-31.
|
||||
* This behaviour is necessary to support fields such as week-based-year
|
||||
* This behavior is necessary to support fields such as week-based-year
|
||||
* or other calendar systems where the parsed value does not align with
|
||||
* standard ISO years.
|
||||
* <p>
|
||||
* The exact behavior is as follows. Parse the full set of fields and
|
||||
* determine the effective chronology. Then convert the base date to the
|
||||
* determine the effective chronology using the last chronology if
|
||||
* it appears more than once. Then convert the base date to the
|
||||
* effective chronology. Then extract the specified field from the
|
||||
* chronology-specific base date and use it to determine the
|
||||
* {@code baseValue} used below.
|
||||
@ -2809,9 +2810,19 @@ public final class DateTimeFormatterBuilder {
|
||||
int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) {
|
||||
int baseValue = this.baseValue;
|
||||
if (baseDate != null) {
|
||||
// TODO: effective chrono is inaccurate at this point
|
||||
Chronology chrono = context.getEffectiveChronology();
|
||||
baseValue = chrono.date(baseDate).get(field);
|
||||
|
||||
// In case the Chronology is changed later, add a callback when/if it changes
|
||||
final long initialValue = value;
|
||||
context.addChronoChangedListener(
|
||||
(_unused) -> {
|
||||
/* Repeat the set of the field using the current Chronology
|
||||
* The success/error position is ignored because the value is
|
||||
* intentionally being overwritten.
|
||||
*/
|
||||
setValue(context, initialValue, errorPos, successPos);
|
||||
});
|
||||
}
|
||||
int parseLen = successPos - errorPos;
|
||||
if (parseLen == minWidth && value >= 0) {
|
||||
|
||||
@ -61,7 +61,6 @@
|
||||
*/
|
||||
package java.time.format;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.ZoneId;
|
||||
import java.time.chrono.Chronology;
|
||||
import java.time.chrono.IsoChronology;
|
||||
@ -69,6 +68,7 @@ import java.time.temporal.TemporalField;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Context object used during date and time parsing.
|
||||
@ -105,6 +105,10 @@ final class DateTimeParseContext {
|
||||
* The list of parsed data.
|
||||
*/
|
||||
private final ArrayList<Parsed> parsed = new ArrayList<>();
|
||||
/**
|
||||
* List of Consumers<Chronology> to be notified if the Chronology changes.
|
||||
*/
|
||||
private ArrayList<Consumer<Chronology>> chronoListeners = null;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the context.
|
||||
@ -354,12 +358,36 @@ final class DateTimeParseContext {
|
||||
* <p>
|
||||
* This stores the chronology that has been parsed.
|
||||
* No validation is performed other than ensuring it is not null.
|
||||
* <p>
|
||||
* The list of listeners is copied and cleared so that each
|
||||
* listener is called only once. A listener can add itself again
|
||||
* if it needs to be notified of future changes.
|
||||
*
|
||||
* @param chrono the parsed chronology, not null
|
||||
*/
|
||||
void setParsed(Chronology chrono) {
|
||||
Objects.requireNonNull(chrono, "chrono");
|
||||
currentParsed().chrono = chrono;
|
||||
if (chronoListeners != null && !chronoListeners.isEmpty()) {
|
||||
Consumer[] tmp = new Consumer[1];
|
||||
Consumer<Chronology>[] listeners = chronoListeners.toArray(tmp);
|
||||
chronoListeners.clear();
|
||||
for (Consumer<Chronology> l : listeners) {
|
||||
l.accept(chrono);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Consumer<Chronology> to the list of listeners to be notified
|
||||
* if the Chronology changes.
|
||||
* @param listener a Consumer<Chronology> to be called when Chronology changes
|
||||
*/
|
||||
void addChronoChangedListener(Consumer<Chronology> listener) {
|
||||
if (chronoListeners == null) {
|
||||
chronoListeners = new ArrayList<Consumer<Chronology>>();
|
||||
}
|
||||
chronoListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -78,10 +78,12 @@ import java.time.chrono.IsoChronology;
|
||||
import java.time.chrono.JapaneseChronology;
|
||||
import java.time.chrono.MinguoChronology;
|
||||
import java.time.chrono.ThaiBuddhistChronology;
|
||||
import java.time.chrono.ThaiBuddhistDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.TemporalQueries;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
@ -443,6 +445,52 @@ public class TestReducedParser extends AbstractTestPrinterParser {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_reducedWithLateChronoChange() {
|
||||
ThaiBuddhistDate date = ThaiBuddhistDate.of(2543, 1, 1);
|
||||
DateTimeFormatter df
|
||||
= new DateTimeFormatterBuilder()
|
||||
.appendValueReduced(YEAR, 2, 2, LocalDate.of(2000, 1, 1))
|
||||
.appendLiteral(" ")
|
||||
.appendChronologyId()
|
||||
.toFormatter();
|
||||
int expected = date.get(YEAR);
|
||||
String input = df.format(date);
|
||||
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
TemporalAccessor parsed = df.parseUnresolved(input, pos);
|
||||
assertEquals(pos.getIndex(), input.length(), "Input not parsed completely");
|
||||
assertEquals(pos.getErrorIndex(), -1, "Error index should be -1 (no-error)");
|
||||
int actual = parsed.get(YEAR);
|
||||
assertEquals(actual, expected,
|
||||
String.format("Wrong date parsed, chrono: %s, input: %s",
|
||||
parsed.query(TemporalQueries.chronology()), input));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_reducedWithLateChronoChangeTwice() {
|
||||
DateTimeFormatter df
|
||||
= new DateTimeFormatterBuilder()
|
||||
.appendValueReduced(YEAR, 2, 2, LocalDate.of(2000, 1, 1))
|
||||
.appendLiteral(" ")
|
||||
.appendChronologyId()
|
||||
.appendLiteral(" ")
|
||||
.appendChronologyId()
|
||||
.toFormatter();
|
||||
int expected = 2044;
|
||||
String input = "44 ThaiBuddhist ISO";
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
TemporalAccessor parsed = df.parseUnresolved(input, pos);
|
||||
assertEquals(pos.getIndex(), input.length(), "Input not parsed completely: " + pos);
|
||||
assertEquals(pos.getErrorIndex(), -1, "Error index should be -1 (no-error)");
|
||||
int actual = parsed.get(YEAR);
|
||||
assertEquals(actual, expected,
|
||||
String.format("Wrong date parsed, chrono: %s, input: %s",
|
||||
parsed.query(TemporalQueries.chronology()), input));
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Class to structure the test data
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user