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:
Xueming Shen 2015-03-07 10:11:03 -08:00
parent f9a9e68c05
commit 011b337a51
2 changed files with 144 additions and 98 deletions

View File

@ -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

View File

@ -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);
}