changeset 7762:5a1d4618329c

Bugid: Issue #262 Normalizing UTC time-zone to offset Z causes problems - Revised ZoneIdPrinterParser to match the parsing behavior of ZoneId.of. - Added ZoneId.ofOffset(prefix, offset) - Updated tests
author rriggs
date Tue, 11 Jun 2013 09:25:51 -0400
parents b80297d05eb9
children ab19cc860f04
files src/share/classes/java/time/ZoneId.java src/share/classes/java/time/ZoneRegion.java src/share/classes/java/time/format/DateTimeFormatterBuilder.java test/java/time/tck/java/time/TCKZoneId.java test/java/time/tck/java/time/TCKZonedDateTime.java test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java
diffstat 6 files changed, 207 insertions(+), 113 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/time/ZoneId.java	Wed Jun 05 16:19:37 2013 -0400
+++ b/src/share/classes/java/time/ZoneId.java	Tue Jun 11 09:25:51 2013 -0400
@@ -401,6 +401,36 @@
     }
 
     /**
+     * Obtains an instance of {@code ZoneId} wrapping an offset.
+     * <p>
+     * If the prefix is "GMT", "UTC", or "UT" a {@code ZoneId}
+     * with the prefix and the non-zero offset is returned.
+     * If the prefix is empty {@code ""} the {@code ZoneOffset} is returned.
+     *
+     * @param prefix  the time-zone ID, not null
+     * @param offset  the offset, not null
+     * @return the zone ID, not null
+     * @throws IllegalArgumentException if the prefix is not one of
+     *     "GMT", "UTC", or "UT", or ""
+     */
+    public static ZoneId ofOffset(String prefix, ZoneOffset offset) {
+        Objects.requireNonNull(prefix, "prefix");
+        Objects.requireNonNull(offset, "offset");
+        if (prefix.length() == 0) {
+            return offset;
+        }
+
+        if (!prefix.equals("GMT") && !prefix.equals("UTC") && !prefix.equals("UT")) {
+             throw new IllegalArgumentException("prefix should be GMT, UTC or UT, is: " + prefix);
+        }
+
+        if (offset.getTotalSeconds() != 0) {
+            prefix = prefix.concat(offset.getId());
+        }
+        return new ZoneRegion(prefix, offset.getRules());
+    }
+
+    /**
      * Parses the ID, taking a flag to indicate whether {@code ZoneRulesException}
      * should be thrown or not, used in deserialization.
      *
@@ -433,7 +463,7 @@
     private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) {
         String prefix = zoneId.substring(0, prefixLength);
         if (zoneId.length() == prefixLength) {
-            return ZoneRegion.ofPrefixedOffset(prefix, ZoneOffset.UTC);
+            return ofOffset(prefix, ZoneOffset.UTC);
         }
         if (zoneId.charAt(prefixLength) != '+' && zoneId.charAt(prefixLength) != '-') {
             return ZoneRegion.ofId(zoneId, checkAvailable);  // drop through to ZoneRulesProvider
@@ -441,9 +471,9 @@
         try {
             ZoneOffset offset = ZoneOffset.of(zoneId.substring(prefixLength));
             if (offset == ZoneOffset.UTC) {
-                return ZoneRegion.ofPrefixedOffset(prefix, offset);
+                return ofOffset(prefix, offset);
             }
-            return ZoneRegion.ofPrefixedOffset(prefix + offset.toString(), offset);
+            return ofOffset(prefix, offset);
         } catch (DateTimeException ex) {
             throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex);
         }
--- a/src/share/classes/java/time/ZoneRegion.java	Wed Jun 05 16:19:37 2013 -0400
+++ b/src/share/classes/java/time/ZoneRegion.java	Tue Jun 11 09:25:51 2013 -0400
@@ -153,19 +153,6 @@
         }
     }
 
-    /**
-     * Obtains an instance of {@code ZoneId} wrapping an offset.
-     * <p>
-     * For example, zone IDs like 'UTC', 'GMT', 'UT' and 'UTC+01:30' will be setup here.
-     *
-     * @param zoneId  the time-zone ID, not null
-     * @param offset  the offset, not null
-     * @return the zone ID, not null
-     */
-    static ZoneRegion ofPrefixedOffset(String zoneId, ZoneOffset offset) {
-        return new ZoneRegion(zoneId, offset.getRules());
-    }
-
     //-------------------------------------------------------------------------
     /**
      * Constructor.
--- a/src/share/classes/java/time/format/DateTimeFormatterBuilder.java	Wed Jun 05 16:19:37 2013 -0400
+++ b/src/share/classes/java/time/format/DateTimeFormatterBuilder.java	Tue Jun 11 09:25:51 2013 -0400
@@ -77,7 +77,6 @@
 import java.math.RoundingMode;
 import java.text.ParsePosition;
 import java.time.DateTimeException;
-import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -962,12 +961,9 @@
      * <pre>
      *   "Europe/London"           -- ZoneId.of("Europe/London")
      *   "Z"                       -- ZoneOffset.UTC
-     *   "UT"                      -- ZoneOffset.UTC
-     *   "UTC"                     -- ZoneOffset.UTC
-     *   "GMT"                     -- ZoneOffset.UTC
-     *   "UT0"                     -- ZoneOffset.UTC
-     *   "UTC0"                    -- ZoneOffset.UTC
-     *   "GMT0"                    -- ZoneOffset.UTC
+     *   "UT"                      -- ZoneId.of("UT")
+     *   "UTC"                     -- ZoneId.of("UTC")
+     *   "GMT"                     -- ZoneId.of("GMT")
      *   "+01:30"                  -- ZoneOffset.of("+01:30")
      *   "UT+01:30"                -- ZoneOffset.of("+01:30")
      *   "UTC+01:30"               -- ZoneOffset.of("+01:30")
@@ -1016,12 +1012,9 @@
      * <pre>
      *   "Europe/London"           -- ZoneId.of("Europe/London")
      *   "Z"                       -- ZoneOffset.UTC
-     *   "UT"                      -- ZoneOffset.UTC
-     *   "UTC"                     -- ZoneOffset.UTC
-     *   "GMT"                     -- ZoneOffset.UTC
-     *   "UT0"                     -- ZoneOffset.UTC
-     *   "UTC0"                    -- ZoneOffset.UTC
-     *   "GMT0"                    -- ZoneOffset.UTC
+     *   "UT"                      -- ZoneId.of("UT")
+     *   "UTC"                     -- ZoneId.of("UTC")
+     *   "GMT"                     -- ZoneId.of("GMT")
      *   "+01:30"                  -- ZoneOffset.of("+01:30")
      *   "UT+01:30"                -- ZoneOffset.of("+01:30")
      *   "UTC+01:30"               -- ZoneOffset.of("+01:30")
@@ -1077,16 +1070,13 @@
      * <pre>
      *   "Europe/London"           -- ZoneId.of("Europe/London")
      *   "Z"                       -- ZoneOffset.UTC
-     *   "UT"                      -- ZoneOffset.UTC
-     *   "UTC"                     -- ZoneOffset.UTC
-     *   "GMT"                     -- ZoneOffset.UTC
-     *   "UT0"                     -- ZoneOffset.UTC
-     *   "UTC0"                    -- ZoneOffset.UTC
-     *   "GMT0"                    -- ZoneOffset.UTC
+     *   "UT"                      -- ZoneId.of("UT")
+     *   "UTC"                     -- ZoneId.of("UTC")
+     *   "GMT"                     -- ZoneId.of("GMT")
      *   "+01:30"                  -- ZoneOffset.of("+01:30")
-     *   "UT+01:30"                -- ZoneOffset.of("+01:30")
-     *   "UTC+01:30"               -- ZoneOffset.of("+01:30")
-     *   "GMT+01:30"               -- ZoneOffset.of("+01:30")
+     *   "UT+01:30"                -- ZoneOffset.of("UT+01:30")
+     *   "UTC+01:30"               -- ZoneOffset.of("UTC+01:30")
+     *   "GMT+01:30"               -- ZoneOffset.of("GMT+01:30")
      * </pre>
      * <p>
      * Note that this method is is identical to {@code appendZoneId()} except
@@ -3756,17 +3746,17 @@
             // handle fixed time-zone IDs
             char nextChar = text.charAt(position);
             if (nextChar == '+' || nextChar == '-') {
-                return parseOffsetBased(context, text, position, OffsetIdPrinterParser.INSTANCE_ID_Z);
+                return parseOffsetBased(context, text, position, position, OffsetIdPrinterParser.INSTANCE_ID_Z);
             } else if (length >= position + 2) {
                 char nextNextChar = text.charAt(position + 1);
                 if (context.charEquals(nextChar, 'U') && context.charEquals(nextNextChar, 'T')) {
                     if (length >= position + 3 && context.charEquals(text.charAt(position + 2), 'C')) {
-                        return parseOffsetBased(context, text, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
+                        return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
                     }
-                    return parseOffsetBased(context, text, position + 2, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
+                    return parseOffsetBased(context, text, position, position + 2, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
                 } else if (context.charEquals(nextChar, 'G') && length >= position + 3 &&
                         context.charEquals(nextNextChar, 'M') && context.charEquals(text.charAt(position + 2), 'T')) {
-                    return parseOffsetBased(context, text, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
+                    return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
                 }
             }
 
@@ -3785,20 +3775,49 @@
             return ppos.getIndex();
         }
 
-        private int parseOffsetBased(DateTimeParseContext context, CharSequence text, int position, OffsetIdPrinterParser parser) {
+        /**
+         * Parse an offset following a prefix and set the ZoneId if it is valid.
+         * To matching the parsing of ZoneId.of the values are not normalized
+         * to ZoneOffsets.
+         *
+         * @param context the parse context
+         * @param text the input text
+         * @param prefixPos start of the prefix
+         * @param position start of text after the prefix
+         * @param parser parser for the value after the prefix
+         * @return the position after the parse
+         */
+        private int parseOffsetBased(DateTimeParseContext context, CharSequence text, int prefixPos, int position, OffsetIdPrinterParser parser) {
+            String prefix = text.toString().substring(prefixPos, position).toUpperCase();
+            if (position >= text.length()) {
+                context.setParsed(ZoneId.of(prefix));
+                return position;
+            }
+
+            // '0' or 'Z' after prefix is not part of a valid ZoneId; use bare prefix
+            if (text.charAt(position) == '0' ||
+                context.charEquals(text.charAt(position), 'Z')) {
+                context.setParsed(ZoneId.of(prefix));
+                return position;
+            }
+
             DateTimeParseContext newContext = context.copy();
             int endPos = parser.parse(newContext, text, position);
-            if (endPos < 0) {
-                if (parser == OffsetIdPrinterParser.INSTANCE_ID_Z) {
-                    return ~position;
+            try {
+                if (endPos < 0) {
+                    if (parser == OffsetIdPrinterParser.INSTANCE_ID_Z) {
+                        return ~prefixPos;
+                    }
+                    context.setParsed(ZoneId.of(prefix));
+                    return position;
                 }
-                context.setParsed(ZoneOffset.UTC);
-                return position;
+                int offset = (int) newContext.getParsed(OFFSET_SECONDS).longValue();
+                ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(offset);
+                context.setParsed(ZoneId.ofOffset(prefix, zoneOffset));
+                return endPos;
+            } catch (DateTimeException dte) {
+                return ~prefixPos;
             }
-            int offset = (int) newContext.getParsed(OFFSET_SECONDS).longValue();
-            ZoneId zone = ZoneOffset.ofTotalSeconds(offset);
-            context.setParsed(zone);
-            return endPos;
         }
 
         @Override
--- a/test/java/time/tck/java/time/TCKZoneId.java	Wed Jun 05 16:19:37 2013 -0400
+++ b/test/java/time/tck/java/time/TCKZoneId.java	Tue Jun 11 09:25:51 2013 -0400
@@ -465,6 +465,53 @@
     }
 
     //-----------------------------------------------------------------------
+    @DataProvider(name="prefixValid")
+    Object[][] data_prefixValid() {
+        return new Object[][] {
+                {"GMT", "+01:00"},
+                {"UTC", "+01:00"},
+                {"UT", "+01:00"},
+                {"", "+01:00"},
+        };
+    }
+
+    @Test(dataProvider="prefixValid")
+    public void test_prefixOfOffset(String prefix, String offset) {
+        ZoneOffset zoff = ZoneOffset.of(offset);
+        ZoneId zoneId = ZoneId.ofOffset(prefix, zoff);
+        assertEquals(zoneId.getId(), prefix + zoff.getId(), "in correct id for : " + prefix + ", zoff: " + zoff);
+
+    }
+
+    //-----------------------------------------------------------------------
+    @DataProvider(name="prefixInvalid")
+    Object[][] data_prefixInvalid() {
+        return new Object[][] {
+                {"GM", "+01:00"},
+                {"U", "+01:00"},
+                {"UTC0", "+01:00"},
+                {"A", "+01:00"},
+        };
+    }
+
+    @Test(dataProvider="prefixInvalid", expectedExceptions=java.lang.IllegalArgumentException.class)
+    public void test_invalidPrefixOfOffset(String prefix, String offset) {
+        ZoneOffset zoff = ZoneOffset.of(offset);
+        ZoneId zoneId = ZoneId.ofOffset(prefix, zoff);
+        fail("should have thrown an exception for prefix: " + prefix);
+    }
+
+    @Test(expectedExceptions=java.lang.NullPointerException.class)
+    public void test_nullPrefixOfOffset() {
+        ZoneId.ofOffset(null, ZoneOffset.ofTotalSeconds(1));
+    }
+
+    @Test(expectedExceptions=java.lang.NullPointerException.class)
+    public void test_nullOffsetOfOffset() {
+        ZoneId.ofOffset("GMT", null);
+    }
+
+    //-----------------------------------------------------------------------
     @DataProvider(name="offsetBasedValidOther")
     Object[][] data_offsetBasedValidOther() {
         return new Object[][] {
--- a/test/java/time/tck/java/time/TCKZonedDateTime.java	Wed Jun 05 16:19:37 2013 -0400
+++ b/test/java/time/tck/java/time/TCKZonedDateTime.java	Tue Jun 11 09:25:51 2013 -0400
@@ -791,17 +791,18 @@
     @DataProvider(name="parseAdditional")
     Object[][] data_parseAdditional() {
         return new Object[][] {
-                {"2012-06-30T12:30:40Z[GMT]", 2012, 6, 30, 12, 30, 40, 0, "Z"},
-                {"2012-06-30T12:30:40Z[UT]", 2012, 6, 30, 12, 30, 40, 0, "Z"},
-                {"2012-06-30T12:30:40Z[UTC]", 2012, 6, 30, 12, 30, 40, 0, "Z"},
+                {"2012-06-30T12:30:40Z[GMT]", 2012, 6, 30, 12, 30, 40, 0, "GMT"},
+                {"2012-06-30T12:30:40Z[UT]", 2012, 6, 30, 12, 30, 40, 0, "UT"},
+                {"2012-06-30T12:30:40Z[UTC]", 2012, 6, 30, 12, 30, 40, 0, "UTC"},
+                {"2012-06-30T12:30:40+01:00[Z]", 2012, 6, 30, 12, 30, 40, 0, "Z"},
                 {"2012-06-30T12:30:40+01:00[+01:00]", 2012, 6, 30, 12, 30, 40, 0, "+01:00"},
-                {"2012-06-30T12:30:40+01:00[GMT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "+01:00"},
-                {"2012-06-30T12:30:40+01:00[UT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "+01:00"},
-                {"2012-06-30T12:30:40+01:00[UTC+01:00]", 2012, 6, 30, 12, 30, 40, 0, "+01:00"},
+                {"2012-06-30T12:30:40+01:00[GMT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "GMT+01:00"},
+                {"2012-06-30T12:30:40+01:00[UT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "UT+01:00"},
+                {"2012-06-30T12:30:40+01:00[UTC+01:00]", 2012, 6, 30, 12, 30, 40, 0, "UTC+01:00"},
                 {"2012-06-30T12:30:40-01:00[-01:00]", 2012, 6, 30, 12, 30, 40, 0, "-01:00"},
-                {"2012-06-30T12:30:40-01:00[GMT-01:00]", 2012, 6, 30, 12, 30, 40, 0, "-01:00"},
-                {"2012-06-30T12:30:40-01:00[UT-01:00]", 2012, 6, 30, 12, 30, 40, 0, "-01:00"},
-                {"2012-06-30T12:30:40-01:00[UTC-01:00]", 2012, 6, 30, 12, 30, 40, 0, "-01:00"},
+                {"2012-06-30T12:30:40-01:00[GMT-01:00]", 2012, 6, 30, 12, 30, 40, 0, "GMT-01:00"},
+                {"2012-06-30T12:30:40-01:00[UT-01:00]", 2012, 6, 30, 12, 30, 40, 0, "UT-01:00"},
+                {"2012-06-30T12:30:40-01:00[UTC-01:00]", 2012, 6, 30, 12, 30, 40, 0, "UTC-01:00"},
                 {"2012-06-30T12:30:40+01:00[Europe/London]", 2012, 6, 30, 12, 30, 40, 0, "Europe/London"},
         };
     }
--- a/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java	Wed Jun 05 16:19:37 2013 -0400
+++ b/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java	Tue Jun 11 09:25:51 2013 -0400
@@ -60,16 +60,20 @@
 package tck.java.time.format;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 
 import java.text.ParsePosition;
+import java.time.DateTimeException;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
 import java.time.temporal.TemporalAccessor;
 import java.time.temporal.TemporalQuery;
 import java.util.Locale;
+import java.util.Objects;
 
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.DataProvider;
@@ -148,43 +152,44 @@
     @DataProvider(name="parseSuccess")
     Object[][] data_parseSuccess() {
         return new Object[][] {
-                {"Z", 1, -1, ZoneOffset.UTC},
-                {"UTC", 3, -1, ZoneOffset.UTC},
-                {"UT", 2, -1, ZoneOffset.UTC},
-                {"GMT", 3, -1, ZoneOffset.UTC},
-                {"UTC0", 4, -1, ZoneOffset.UTC},
-                {"UT0", 3, -1, ZoneOffset.UTC},
-                {"GMT0", 4, -1, ZoneOffset.UTC},
+                {"Z", 1, -1, ZoneId.of("Z")},
+                {"UTC", 3, -1, ZoneId.of("UTC")},
+                {"UT", 2, -1, ZoneId.of("UT")},
+                {"GMT", 3, -1, ZoneId.of("GMT")},
 
                 {"+00:00", 6, -1, ZoneOffset.UTC},
-                {"UTC+00:00", 9, -1, ZoneOffset.UTC},
-                {"UT+00:00", 8, -1, ZoneOffset.UTC},
-                {"GMT+00:00", 9, -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, ZoneOffset.UTC},
-                {"UT-00:00", 8, -1, ZoneOffset.UTC},
-                {"GMT-00:00", 9, -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")},
 
                 {"+01:30", 6, -1, ZoneOffset.ofHoursMinutes(1, 30)},
-                {"UTC+01:30", 9, -1, ZoneOffset.ofHoursMinutes(1, 30)},
-                {"UT+02:30", 8, -1, ZoneOffset.ofHoursMinutes(2, 30)},
-                {"GMT+03:30", 9, -1, ZoneOffset.ofHoursMinutes(3, 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, ZoneOffset.ofHoursMinutes(-1, -30)},
-                {"UT-02:30", 8, -1, ZoneOffset.ofHoursMinutes(-2, -30)},
-                {"GMT-03:30", 9, -1, ZoneOffset.ofHoursMinutes(-3, -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")},
 
                 // fallback to UTC
-                {"UTC-01:WW", 3, -1, ZoneOffset.UTC},
-                {"UT-02:WW", 2, -1, ZoneOffset.UTC},
-                {"GMT-03:WW", 3, -1, ZoneOffset.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, ZoneOffset.UTC},
+                {"UTC1", 3, -1, ZoneId.of("UTC")},
 
                 // Z not parsed as zero
-                {"UTCZ", 3, -1, ZoneOffset.UTC},
-                {"UTZ", 2, -1, ZoneOffset.UTC},
-                {"GMTZ", 3, -1, ZoneOffset.UTC},
+                {"UTCZ", 3, -1, ZoneId.of("UTC")},
+                {"UTZ", 2, -1, ZoneId.of("UT")},
+                {"GMTZ", 3, -1, ZoneId.of("GMT")},
+
+                // 0 not parsed
+                {"UTC0", 3, -1, ZoneId.of("UTC")},
+                {"UT0", 2, -1, ZoneId.of("UT")},
 
                 // fail to parse
                 {"", 0, 0, null},
@@ -206,12 +211,12 @@
     public void test_parseSuccess_plain(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
         builder.appendZoneId();
         TemporalAccessor parsed = builder.toFormatter().parseUnresolved(text, pos);
-        assertEquals(pos.getErrorIndex(), expectedErrorIndex);
-        assertEquals(pos.getIndex(), expectedIndex);
+        assertEquals(pos.getErrorIndex(), expectedErrorIndex, "Incorrect error index parsing: " + text);
+        assertEquals(pos.getIndex(), expectedIndex, "Incorrect index parsing: " + text);
         if (expected != null) {
-            assertEquals(parsed.query(TemporalQuery.zoneId()), expected);
-            assertEquals(parsed.query(TemporalQuery.offset()), null);
-            assertEquals(parsed.query(TemporalQuery.zone()), expected);
+            assertEquals(parsed.query(TemporalQuery.zoneId()), expected, "Incorrect zoneId parsing: " + text);
+            assertEquals(parsed.query(TemporalQuery.offset()), null, "Incorrect offset parsing: " + text);
+            assertEquals(parsed.query(TemporalQuery.zone()), expected, "Incorrect zone parsing: " + text);
         } else {
             assertEquals(parsed, null);
         }
@@ -221,13 +226,14 @@
     public void test_parseSuccess_prefix(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
         builder.appendZoneId();
         pos.setIndex(3);
-        TemporalAccessor parsed = builder.toFormatter().parseUnresolved("XXX" + text, pos);
-        assertEquals(pos.getErrorIndex(), expectedErrorIndex >= 0  ? expectedErrorIndex + 3 : expectedErrorIndex);
-        assertEquals(pos.getIndex(), expectedIndex + 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(TemporalQuery.zoneId()), expected);
-            assertEquals(parsed.query(TemporalQuery.offset()), null);
-            assertEquals(parsed.query(TemporalQuery.zone()), expected);
+            assertEquals(parsed.query(TemporalQuery.zoneId()), expected, "Incorrect zoneId parsing: " + prefixText);
+            assertEquals(parsed.query(TemporalQuery.offset()), null, "Incorrect offset parsing: " + prefixText);
+            assertEquals(parsed.query(TemporalQuery.zone()), expected, "Incorrect zone parsing: " + prefixText);
         } else {
             assertEquals(parsed, null);
         }
@@ -236,13 +242,14 @@
     @Test(dataProvider="parseSuccess")
     public void test_parseSuccess_suffix(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
         builder.appendZoneId();
-        TemporalAccessor parsed = builder.toFormatter().parseUnresolved(text + "XXX", pos);
-        assertEquals(pos.getErrorIndex(), expectedErrorIndex);
-        assertEquals(pos.getIndex(), expectedIndex);
+        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(TemporalQuery.zoneId()), expected);
-            assertEquals(parsed.query(TemporalQuery.offset()), null);
-            assertEquals(parsed.query(TemporalQuery.zone()), expected);
+            assertEquals(parsed.query(TemporalQuery.zoneId()), expected, "Incorrect zoneId parsing: " + suffixText);
+            assertEquals(parsed.query(TemporalQuery.offset()), null, "Incorrect offset parsing: " + suffixText);
+            assertEquals(parsed.query(TemporalQuery.zone()), expected, "Incorrect zone parsing: " + suffixText);
         } else {
             assertEquals(parsed, null);
         }
@@ -251,15 +258,16 @@
     @Test(dataProvider="parseSuccess")
     public void test_parseSuccess_caseSensitive(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
         builder.parseCaseSensitive().appendZoneId();
-        TemporalAccessor parsed = builder.toFormatter().parseUnresolved(text.toLowerCase(Locale.ENGLISH), pos);
+        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);
-            assertEquals(pos.getErrorIndex(), expectedErrorIndex);
+            assertEquals(pos.getIndex(), expectedIndex, "Incorrect index parsing: " + lcText);
+            assertEquals(pos.getErrorIndex(), expectedErrorIndex, "Incorrect error index parsing: " + lcText);
             if (expected != null) {
                 assertEquals(parsed.query(TemporalQuery.zoneId()), expected);
                 assertEquals(parsed.query(TemporalQuery.offset()), null);
@@ -273,13 +281,15 @@
     @Test(dataProvider="parseSuccess")
     public void test_parseSuccess_caseInsensitive(String text, int expectedIndex, int expectedErrorIndex, ZoneId expected) {
         builder.parseCaseInsensitive().appendZoneId();
-        TemporalAccessor parsed = builder.toFormatter().parseUnresolved(text.toLowerCase(Locale.ENGLISH), pos);
-        assertEquals(pos.getErrorIndex(), expectedErrorIndex);
-        assertEquals(pos.getIndex(), expectedIndex);
+        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) {
-            assertEquals(parsed.query(TemporalQuery.zoneId()), expected);
-            assertEquals(parsed.query(TemporalQuery.offset()), null);
-            assertEquals(parsed.query(TemporalQuery.zone()), expected);
+            ZoneId zid = parsed.query(TemporalQuery.zoneId());
+            assertEquals(parsed.query(TemporalQuery.zoneId()), expected, "Incorrect zoneId parsing: " + lcText);
+            assertEquals(parsed.query(TemporalQuery.offset()), null, "Incorrect offset parsing: " + lcText);
+            assertEquals(parsed.query(TemporalQuery.zone()), expected, "Incorrect zone parsing: " + lcText);
         } else {
             assertEquals(parsed, null);
         }