# HG changeset patch # User sherman # Date 1425751863 28800 # Node ID f567c5137990c4ac3c247d8464590a09d0c3c840 # Parent 6f7f029a6b63080415e9fc9c5e956f160872146d 8074406: DateTimeFormatter.appendZoneOrOffsetId() fails to resolve a ZoneOffset for OffsetDateTime Summary: to support resolve OffsetDateTime from DTF.appendZoneOrOffset() Reviewed-by: rriggs diff -r 6f7f029a6b63 -r f567c5137990 jdk/src/java.base/share/classes/java/time/format/Parsed.java --- a/jdk/src/java.base/share/classes/java/time/format/Parsed.java Fri Mar 06 16:00:58 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/time/format/Parsed.java Sat Mar 07 10:11:03 2015 -0800 @@ -216,7 +216,16 @@ 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 diff -r 6f7f029a6b63 -r f567c5137990 jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java --- a/jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java Fri Mar 06 16:00:58 2015 -0800 +++ b/jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java Sat Mar 07 10:11:03 2015 -0800 @@ -152,144 +152,181 @@ @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(); + test(text, expectedIndex, expectedErrorIndex, expected, isZoneOffset); + } + + @Test(dataProvider="parseSuccess") + public void test_ZoneId_parseSuccess_prefix( + String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset) + { + builder.appendZoneId(); + pos.setIndex(3); + test("XXX" + text, + expectedIndex + 3, + expectedErrorIndex >= 0 ? expectedErrorIndex + 3 : expectedErrorIndex, + expected, isZoneOffset); + } + + @Test(dataProvider="parseSuccess") + public void test_ZoneId_parseSuccess_suffix( + String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset) + { builder.appendZoneId(); + test(text + "XXX", expectedIndex, expectedErrorIndex, expected, isZoneOffset); + } + + @Test(dataProvider="parseSuccess") + public void test_ZoneId_parseSuccess_caseSensitive( + String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset) + { + builder.parseCaseSensitive().appendZoneId(); + + 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_ZoneId_parseSuccess_caseInsensitive( + String text, int expectedIndex, int expectedErrorIndex, ZoneId expected, boolean isZoneOffset) + { + builder.parseCaseInsensitive().appendZoneId(); + 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) { - 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(dataProvider="parseSuccess") - public void test_parseSuccess_prefix(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) { - 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(dataProvider="parseSuccess") - public void test_parseSuccess_suffix(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) { - 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(dataProvider="parseSuccess") - public void test_parseSuccess_caseSensitive(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) { - 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 - 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(dataProvider="parseSuccess") - public void test_parseSuccess_caseInsensitive(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) { - 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); - 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); }