8074406: DateTimeFormatter.appendZoneOrOffsetId() fails to resolve a ZoneOffset for OffsetDateTime
authorsherman
Sat, 07 Mar 2015 10:11:03 -0800
changeset 29372 f567c5137990
parent 29371 6f7f029a6b63
child 29373 8f41a04d1a02
8074406: DateTimeFormatter.appendZoneOrOffsetId() fails to resolve a ZoneOffset for OffsetDateTime Summary: to support resolve OffsetDateTime from DTF.appendZoneOrOffset() Reviewed-by: rriggs
jdk/src/java.base/share/classes/java/time/format/Parsed.java
jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.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
--- 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);
         }