8074406: DateTimeFormatter.appendZoneOrOffsetId() fails to resolve a ZoneOffset for OffsetDateTime
To support resolve OffsetDateTime from DTF.appendZoneOrOffset() Reviewed-by: rriggs
This commit is contained in:
parent
f9a9e68c05
commit
011b337a51
@ -216,7 +216,16 @@ final class Parsed implements TemporalAccessor {
|
||||
return (R) (date != null ? LocalDate.from(date) : null);
|
||||
} else if (query == TemporalQueries.localTime()) {
|
||||
return (R) time;
|
||||
} else if (query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
|
||||
} else if (query == TemporalQueries.offset()) {
|
||||
Long offsetSecs = fieldValues.get(OFFSET_SECONDS);
|
||||
if (offsetSecs != null) {
|
||||
return (R) ZoneOffset.ofTotalSeconds(offsetSecs.intValue());
|
||||
}
|
||||
if (zone instanceof ZoneOffset) {
|
||||
return (R)zone;
|
||||
}
|
||||
return query.queryFrom(this);
|
||||
} else if (query == TemporalQueries.zone()) {
|
||||
return query.queryFrom(this);
|
||||
} else if (query == TemporalQueries.precision()) {
|
||||
return null; // not a complete date/time
|
||||
|
@ -152,144 +152,181 @@ public class TCKZoneIdPrinterParser {
|
||||
@DataProvider(name="parseSuccess")
|
||||
Object[][] data_parseSuccess() {
|
||||
return new Object[][] {
|
||||
{"Z", 1, -1, ZoneId.of("Z")},
|
||||
{"UTC", 3, -1, ZoneId.of("UTC")},
|
||||
{"UT", 2, -1, ZoneId.of("UT")},
|
||||
{"GMT", 3, -1, ZoneId.of("GMT")},
|
||||
{"Z", 1, -1, ZoneId.of("Z"), true},
|
||||
{"UTC", 3, -1, ZoneId.of("UTC"), false},
|
||||
{"UT", 2, -1, ZoneId.of("UT"), false},
|
||||
{"GMT", 3, -1, ZoneId.of("GMT"), false},
|
||||
|
||||
{"+00:00", 6, -1, ZoneOffset.UTC},
|
||||
{"UTC+00:00", 9, -1, ZoneId.of("UTC")},
|
||||
{"UT+00:00", 8, -1, ZoneId.of("UT")},
|
||||
{"GMT+00:00", 9, -1, ZoneId.of("GMT")},
|
||||
{"-00:00", 6, -1, ZoneOffset.UTC},
|
||||
{"UTC-00:00", 9, -1, ZoneId.of("UTC")},
|
||||
{"UT-00:00", 8, -1, ZoneId.of("UT")},
|
||||
{"GMT-00:00", 9, -1, ZoneId.of("GMT")},
|
||||
{"+00:00", 6, -1, ZoneOffset.UTC, true},
|
||||
{"UTC+00:00", 9, -1, ZoneId.of("UTC"), false},
|
||||
{"UT+00:00", 8, -1, ZoneId.of("UT"), false},
|
||||
{"GMT+00:00", 9, -1, ZoneId.of("GMT"), false},
|
||||
{"-00:00", 6, -1, ZoneOffset.UTC, true},
|
||||
{"UTC-00:00", 9, -1, ZoneId.of("UTC"), false},
|
||||
{"UT-00:00", 8, -1, ZoneId.of("UT"), false},
|
||||
{"GMT-00:00", 9, -1, ZoneId.of("GMT"), false},
|
||||
|
||||
{"+01:30", 6, -1, ZoneOffset.ofHoursMinutes(1, 30)},
|
||||
{"UTC+01:30", 9, -1, ZoneId.of("UTC+01:30")},
|
||||
{"UT+02:30", 8, -1, ZoneId.of("UT+02:30")},
|
||||
{"GMT+03:30", 9, -1, ZoneId.of("GMT+03:30")},
|
||||
{"-01:30", 6, -1, ZoneOffset.ofHoursMinutes(-1, -30)},
|
||||
{"UTC-01:30", 9, -1, ZoneId.of("UTC-01:30")},
|
||||
{"UT-02:30", 8, -1, ZoneId.of("UT-02:30")},
|
||||
{"GMT-03:30", 9, -1, ZoneId.of("GMT-03:30")},
|
||||
{"+01:30", 6, -1, ZoneOffset.ofHoursMinutes(1, 30), true},
|
||||
{"UTC+01:30", 9, -1, ZoneId.of("UTC+01:30"), false},
|
||||
{"UT+02:30", 8, -1, ZoneId.of("UT+02:30"), false},
|
||||
{"GMT+03:30", 9, -1, ZoneId.of("GMT+03:30"), false},
|
||||
{"-01:30", 6, -1, ZoneOffset.ofHoursMinutes(-1, -30), true},
|
||||
{"UTC-01:30", 9, -1, ZoneId.of("UTC-01:30"), false},
|
||||
{"UT-02:30", 8, -1, ZoneId.of("UT-02:30"), false},
|
||||
{"GMT-03:30", 9, -1, ZoneId.of("GMT-03:30"), false},
|
||||
|
||||
// fallback to UTC
|
||||
{"UTC-01:WW", 3, -1, ZoneId.of("UTC")},
|
||||
{"UT-02:WW", 2, -1, ZoneId.of("UT")},
|
||||
{"GMT-03:WW", 3, -1, ZoneId.of("GMT")},
|
||||
{"Z0", 1, -1, ZoneOffset.UTC},
|
||||
{"UTC1", 3, -1, ZoneId.of("UTC")},
|
||||
{"UTC-01:WW", 3, -1, ZoneId.of("UTC"), false},
|
||||
{"UT-02:WW", 2, -1, ZoneId.of("UT"), false},
|
||||
{"GMT-03:WW", 3, -1, ZoneId.of("GMT"), false},
|
||||
{"Z0", 1, -1, ZoneOffset.UTC, true},
|
||||
{"UTC1", 3, -1, ZoneId.of("UTC"), false},
|
||||
|
||||
// Z not parsed as zero
|
||||
{"UTCZ", 3, -1, ZoneId.of("UTC")},
|
||||
{"UTZ", 2, -1, ZoneId.of("UT")},
|
||||
{"GMTZ", 3, -1, ZoneId.of("GMT")},
|
||||
{"UTCZ", 3, -1, ZoneId.of("UTC"), false},
|
||||
{"UTZ", 2, -1, ZoneId.of("UT"), false},
|
||||
{"GMTZ", 3, -1, ZoneId.of("GMT"), false},
|
||||
|
||||
// 0 not parsed
|
||||
{"UTC0", 3, -1, ZoneId.of("UTC")},
|
||||
{"UT0", 2, -1, ZoneId.of("UT")},
|
||||
{"UTC0", 3, -1, ZoneId.of("UTC"), false},
|
||||
{"UT0", 2, -1, ZoneId.of("UT"), false},
|
||||
|
||||
// fail to parse
|
||||
{"", 0, 0, null},
|
||||
{"A", 0, 0, null},
|
||||
{"UZ", 0, 0, null},
|
||||
{"GMA", 0, 0, null},
|
||||
{"0", 0, 0, null},
|
||||
{"+", 0, 0, null},
|
||||
{"-", 0, 0, null},
|
||||
{"", 0, 0, null, false},
|
||||
{"A", 0, 0, null, false},
|
||||
{"UZ", 0, 0, null, false},
|
||||
{"GMA", 0, 0, null, false},
|
||||
{"0", 0, 0, null, false},
|
||||
{"+", 0, 0, null, false},
|
||||
{"-", 0, 0, null, false},
|
||||
|
||||
// zone IDs
|
||||
{"Europe/London", 13, -1, ZoneId.of("Europe/London")},
|
||||
{"America/New_York", 16, -1, ZoneId.of("America/New_York")},
|
||||
{"America/Bogusville", 0, 0, null},
|
||||
{"Europe/London", 13, -1, ZoneId.of("Europe/London"), false},
|
||||
{"America/New_York", 16, -1, ZoneId.of("America/New_York"), false},
|
||||
{"America/Bogusville", 0, 0, null, false},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_parseSuccess_plain(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
|
||||
public void test_ZoneId_parseSuccess_plain(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.appendZoneId();
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(text, pos);
|
||||
assertEquals(pos.getErrorIndex(), expectedErrorIndex, "Incorrect error index parsing: " + text);
|
||||
assertEquals(pos.getIndex(), expectedIndex, "Incorrect index parsing: " + text);
|
||||
if (expected != null) {
|
||||
assertEquals(parsed.query(TemporalQueries.zoneId()), expected, "Incorrect zoneId parsing: " + text);
|
||||
assertEquals(parsed.query(TemporalQueries.offset()), null, "Incorrect offset parsing: " + text);
|
||||
assertEquals(parsed.query(TemporalQueries.zone()), expected, "Incorrect zone parsing: " + text);
|
||||
} else {
|
||||
assertEquals(parsed, null);
|
||||
}
|
||||
test(text, expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_parseSuccess_prefix(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
|
||||
public void test_ZoneId_parseSuccess_prefix(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.appendZoneId();
|
||||
pos.setIndex(3);
|
||||
String prefixText = "XXX" + text;
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(prefixText, pos);
|
||||
assertEquals(pos.getErrorIndex(), expectedErrorIndex >= 0 ? expectedErrorIndex + 3 : expectedErrorIndex, "Incorrect error index parsing: " + prefixText);
|
||||
assertEquals(pos.getIndex(), expectedIndex + 3, "Incorrect index parsing: " + prefixText);
|
||||
if (expected != null) {
|
||||
assertEquals(parsed.query(TemporalQueries.zoneId()), expected, "Incorrect zoneId parsing: " + prefixText);
|
||||
assertEquals(parsed.query(TemporalQueries.offset()), null, "Incorrect offset parsing: " + prefixText);
|
||||
assertEquals(parsed.query(TemporalQueries.zone()), expected, "Incorrect zone parsing: " + prefixText);
|
||||
} else {
|
||||
assertEquals(parsed, null);
|
||||
}
|
||||
test("XXX" + text,
|
||||
expectedIndex + 3,
|
||||
expectedErrorIndex >= 0 ? expectedErrorIndex + 3 : expectedErrorIndex,
|
||||
expected, isZoneOffset);
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_parseSuccess_suffix(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
|
||||
public void test_ZoneId_parseSuccess_suffix(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.appendZoneId();
|
||||
String suffixText = text + "XXX";
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(suffixText, pos);
|
||||
assertEquals(pos.getErrorIndex(), expectedErrorIndex, "Incorrect error index parsing: " + suffixText);
|
||||
assertEquals(pos.getIndex(), expectedIndex, "Incorrect index parsing: " + suffixText);
|
||||
if (expected != null) {
|
||||
assertEquals(parsed.query(TemporalQueries.zoneId()), expected, "Incorrect zoneId parsing: " + suffixText);
|
||||
assertEquals(parsed.query(TemporalQueries.offset()), null, "Incorrect offset parsing: " + suffixText);
|
||||
assertEquals(parsed.query(TemporalQueries.zone()), expected, "Incorrect zone parsing: " + suffixText);
|
||||
} else {
|
||||
assertEquals(parsed, null);
|
||||
}
|
||||
test(text + "XXX", expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_parseSuccess_caseSensitive(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
|
||||
public void test_ZoneId_parseSuccess_caseSensitive(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.parseCaseSensitive().appendZoneId();
|
||||
String lcText = text.toLowerCase(Locale.ENGLISH);
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(lcText, pos);
|
||||
|
||||
if (text.matches("[^A-Z]*[A-Z].*")) { // if input has letters
|
||||
String lcText = text.toLowerCase(Locale.ENGLISH);
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(lcText, pos);
|
||||
assertEquals(pos.getErrorIndex() >= 0, true);
|
||||
assertEquals(pos.getIndex(), 0);
|
||||
assertEquals(parsed, null);
|
||||
} else {
|
||||
// case sensitive made no difference
|
||||
assertEquals(pos.getIndex(), expectedIndex, "Incorrect index parsing: " + lcText);
|
||||
assertEquals(pos.getErrorIndex(), expectedErrorIndex, "Incorrect error index parsing: " + lcText);
|
||||
if (expected != null) {
|
||||
assertEquals(parsed.query(TemporalQueries.zoneId()), expected);
|
||||
assertEquals(parsed.query(TemporalQueries.offset()), null);
|
||||
assertEquals(parsed.query(TemporalQueries.zone()), expected);
|
||||
} else {
|
||||
assertEquals(parsed, null);
|
||||
}
|
||||
test(text.toLowerCase(Locale.ENGLISH), expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_parseSuccess_caseInsensitive(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
|
||||
public void test_ZoneId_parseSuccess_caseInsensitive(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.parseCaseInsensitive().appendZoneId();
|
||||
String lcText = text.toLowerCase(Locale.ENGLISH);
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(lcText, pos);
|
||||
assertEquals(pos.getErrorIndex(), expectedErrorIndex, "Incorrect error index parsing: " + lcText);
|
||||
assertEquals(pos.getIndex(), expectedIndex, "Incorrect index parsing: " + lcText);
|
||||
test(text.toLowerCase(Locale.ENGLISH), expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_ZoneOrOffsetId_parseSuccess_plain(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.appendZoneOrOffsetId();
|
||||
test(text, expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_ZoneOrOffsetId_parseSuccess_prefix(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.appendZoneOrOffsetId();
|
||||
pos.setIndex(3);
|
||||
test("XXX" + text,
|
||||
expectedIndex + 3,
|
||||
expectedErrorIndex >= 0 ? expectedErrorIndex + 3 : expectedErrorIndex,
|
||||
expected, isZoneOffset);
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_ZoneOrOffsetId_parseSuccess_suffix(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.appendZoneOrOffsetId();
|
||||
test(text + "XXX", expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_ZoneOrOffsetId_parseSuccess_caseSensitive(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.parseCaseSensitive().appendZoneOrOffsetId();
|
||||
if (text.matches("[^A-Z]*[A-Z].*")) { // if input has letters
|
||||
String lcText = text.toLowerCase(Locale.ENGLISH);
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(lcText, pos);
|
||||
assertEquals(pos.getErrorIndex() >= 0, true);
|
||||
assertEquals(pos.getIndex(), 0);
|
||||
assertEquals(parsed, null);
|
||||
} else {
|
||||
test(text.toLowerCase(Locale.ENGLISH), expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider="parseSuccess")
|
||||
public void test_ZoneOrOffsetIdparseSuccess_caseInsensitive(
|
||||
String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset)
|
||||
{
|
||||
builder.parseCaseInsensitive().appendZoneOrOffsetId();
|
||||
test(text.toLowerCase(Locale.ENGLISH), expectedIndex, expectedErrorIndex, expected, isZoneOffset);
|
||||
}
|
||||
|
||||
private void test(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected,
|
||||
boolean isZoneOffset) {
|
||||
TemporalAccessor parsed = builder.toFormatter().parseUnresolved(text, pos);
|
||||
assertEquals(pos.getErrorIndex(), expectedErrorIndex, "Incorrect error index parsing: " + text);
|
||||
assertEquals(pos.getIndex(), expectedIndex, "Incorrect index parsing: " + text);
|
||||
if (expected != null) {
|
||||
ZoneId zid = parsed.query(TemporalQueries.zoneId());
|
||||
assertEquals(parsed.query(TemporalQueries.zoneId()), expected, "Incorrect zoneId parsing: " + lcText);
|
||||
assertEquals(parsed.query(TemporalQueries.offset()), null, "Incorrect offset parsing: " + lcText);
|
||||
assertEquals(parsed.query(TemporalQueries.zone()), expected, "Incorrect zone parsing: " + lcText);
|
||||
assertEquals(parsed.query(TemporalQueries.zoneId()),
|
||||
expected,
|
||||
"Incorrect zoneId parsing: " + text);
|
||||
assertEquals(parsed.query(TemporalQueries.offset()),
|
||||
isZoneOffset ? expected : null,
|
||||
"Incorrect offset parsing: " + text);
|
||||
assertEquals(parsed.query(TemporalQueries.zone()),
|
||||
expected,
|
||||
"Incorrect zone parsing: " + text);
|
||||
} else {
|
||||
assertEquals(parsed, null);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user