changeset 15152:0881ab3faeb4

8066806: java.time.format.DateTimeFormatter cannot parse an offset with single digit hour Summary: Added support for single digit hour in offset Reviewed-by: rriggs, scolebourne
author ntv
date Thu, 28 Jul 2016 10:28:21 +0000
parents 88271955d26b
children 2316ff4c0a83
files src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java test/java/time/tck/java/time/format/TCKOffsetPrinterParser.java test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java test/java/time/test/java/time/format/TestZoneOffsetParser.java
diffstat 5 files changed, 907 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java	Wed Jul 27 14:27:46 2016 -0700
+++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java	Thu Jul 28 10:28:21 2016 +0000
@@ -928,7 +928,24 @@
      * second if non-zero, no colon
      * <li>{@code +HH:mm:ss} - hour, with minute if non-zero or with minute and
      * second if non-zero, with colon
+     * <li>{@code +H} - hour only, ignoring minute and second
+     * <li>{@code +Hmm} - hour, with minute if non-zero, ignoring second, no colon
+     * <li>{@code +H:mm} - hour, with minute if non-zero, ignoring second, with colon
+     * <li>{@code +HMM} - hour and minute, ignoring second, no colon
+     * <li>{@code +H:MM} - hour and minute, ignoring second, with colon
+     * <li>{@code +HMMss} - hour and minute, with second if non-zero, no colon
+     * <li>{@code +H:MM:ss} - hour and minute, with second if non-zero, with colon
+     * <li>{@code +HMMSS} - hour, minute and second, no colon
+     * <li>{@code +H:MM:SS} - hour, minute and second, with colon
+     * <li>{@code +Hmmss} - hour, with minute if non-zero or with minute and
+     * second if non-zero, no colon
+     * <li>{@code +H:mm:ss} - hour, with minute if non-zero or with minute and
+     * second if non-zero, with colon
      * </ul>
+     * Patterns containing "HH" will format and parse a two digit hour,
+     * zero-padded if necessary. Patterns containing "H" will format with no
+     * zero-padding, and parse either one or two digits.
+     * In lenient mode, the parser will be greedy and parse the maximum digits possible.
      * The "no offset" text controls what text is printed when the total amount of
      * the offset fields to be output is zero.
      * Example values would be 'Z', '+00:00', 'UTC' or 'GMT'.
@@ -3475,13 +3492,15 @@
      */
     static final class OffsetIdPrinterParser implements DateTimePrinterParser {
         static final String[] PATTERNS = new String[] {
-            "+HH", "+HHmm", "+HH:mm", "+HHMM", "+HH:MM", "+HHMMss", "+HH:MM:ss", "+HHMMSS", "+HH:MM:SS", "+HHmmss", "+HH:mm:ss",
+                "+HH", "+HHmm", "+HH:mm", "+HHMM", "+HH:MM", "+HHMMss", "+HH:MM:ss", "+HHMMSS", "+HH:MM:SS", "+HHmmss", "+HH:mm:ss",
+                "+H",  "+Hmm",  "+H:mm",  "+HMM",  "+H:MM",  "+HMMss",  "+H:MM:ss",  "+HMMSS",  "+H:MM:SS",  "+Hmmss",  "+H:mm:ss",
         };  // order used in pattern builder
         static final OffsetIdPrinterParser INSTANCE_ID_Z = new OffsetIdPrinterParser("+HH:MM:ss", "Z");
         static final OffsetIdPrinterParser INSTANCE_ID_ZERO = new OffsetIdPrinterParser("+HH:MM:ss", "0");
 
         private final String noOffsetText;
         private final int type;
+        private final int style;
 
         /**
          * Constructor.
@@ -3493,6 +3512,7 @@
             Objects.requireNonNull(pattern, "pattern");
             Objects.requireNonNull(noOffsetText, "noOffsetText");
             this.type = checkPattern(pattern);
+            this.style = type % 11;
             this.noOffsetText = noOffsetText;
         }
 
@@ -3505,6 +3525,14 @@
             throw new IllegalArgumentException("Invalid zone offset pattern: " + pattern);
         }
 
+        private boolean isPaddedHour() {
+            return type < 11;
+        }
+
+        private boolean isColon() {
+            return style > 0 && (style % 2) == 0;
+        }
+
         @Override
         public boolean format(DateTimePrintContext context, StringBuilder buf) {
             Long offsetSecs = context.getValue(OFFSET_SECONDS);
@@ -3520,15 +3548,17 @@
                 int absSeconds = Math.abs(totalSecs % 60);
                 int bufPos = buf.length();
                 int output = absHours;
-                buf.append(totalSecs < 0 ? "-" : "+")
-                    .append((char) (absHours / 10 + '0')).append((char) (absHours % 10 + '0'));
-                if ((type >= 3 && type < 9) || (type >= 9 && absSeconds > 0) || (type >= 1 && absMinutes > 0)) {
-                    buf.append((type % 2) == 0 ? ":" : "")
-                        .append((char) (absMinutes / 10 + '0')).append((char) (absMinutes % 10 + '0'));
+                buf.append(totalSecs < 0 ? "-" : "+");
+                if (isPaddedHour() || absHours >= 10) {
+                    formatZeroPad(false, absHours, buf);
+                } else {
+                    buf.append((char) (absHours + '0'));
+                }
+                if ((style >= 3 && style <= 8) || (style >= 9 && absSeconds > 0) || (style >= 1 && absMinutes > 0)) {
+                    formatZeroPad(isColon(), absMinutes, buf);
                     output += absMinutes;
-                    if (type == 7  || type == 8 || (type >= 5 && absSeconds > 0)) {
-                        buf.append((type % 2) == 0 ? ":" : "")
-                            .append((char) (absSeconds / 10 + '0')).append((char) (absSeconds % 10 + '0'));
+                    if (style == 7 || style == 8 || (style >= 5 && absSeconds > 0)) {
+                        formatZeroPad(isColon(), absSeconds, buf);
                         output += absSeconds;
                     }
                 }
@@ -3540,19 +3570,16 @@
             return true;
         }
 
+        private void formatZeroPad(boolean colon, int value, StringBuilder buf) {
+            buf.append(colon ? ":" : "")
+                    .append((char) (value / 10 + '0'))
+                    .append((char) (value % 10 + '0'));
+        }
+
         @Override
         public int parse(DateTimeParseContext context, CharSequence text, int position) {
             int length = text.length();
             int noOffsetLen = noOffsetText.length();
-            int parseType = type;
-            if (context.isStrict() == false) {
-                if ((parseType > 0 && (parseType % 2) == 0) ||
-                    (parseType == 0 && length > position + 3 && text.charAt(position + 3) == ':')) {
-                    parseType = 10;
-                } else {
-                    parseType = 9;
-                }
-            }
             if (noOffsetLen == 0) {
                 if (position == length) {
                     return context.setParsedField(OFFSET_SECONDS, 0, position, position);
@@ -3571,12 +3598,87 @@
             if (sign == '+' || sign == '-') {
                 // starts
                 int negative = (sign == '-' ? -1 : 1);
+                boolean isColon = isColon();
+                boolean paddedHour = isPaddedHour();
                 int[] array = new int[4];
                 array[0] = position + 1;
-                if ((parseNumber(array, 1, text, true, parseType) ||
-                        parseNumber(array, 2, text, parseType >= 3 && parseType < 9, parseType) ||
-                        parseNumber(array, 3, text, parseType == 7 || parseType == 8, parseType)) == false) {
-                    // success
+                int parseType = type;
+                // select parse type when lenient
+                if (!context.isStrict()) {
+                    if (paddedHour) {
+                        if (isColon || (parseType == 0 && length > position + 3 && text.charAt(position + 3) == ':')) {
+                            isColon = true; // needed in cases like ("+HH", "+01:01")
+                            parseType = 10;
+                        } else {
+                            parseType = 9;
+                        }
+                    } else {
+                        if (isColon || (parseType == 11 && length > position + 3 && (text.charAt(position + 2) == ':' || text.charAt(position + 3) == ':'))) {
+                            isColon = true;
+                            parseType = 21;  // needed in cases like ("+H", "+1:01")
+                        } else {
+                            parseType = 20;
+                        }
+                    }
+                }
+                // parse according to the selected pattern
+                switch (parseType) {
+                    case 0: // +HH
+                    case 11: // +H
+                        parseHour(text, paddedHour, array);
+                        break;
+                    case 1: // +HHmm
+                    case 2: // +HH:mm
+                    case 13: // +H:mm
+                        parseHour(text, paddedHour, array);
+                        parseMinute(text, isColon, false, array);
+                        break;
+                    case 3: // +HHMM
+                    case 4: // +HH:MM
+                    case 15: // +H:MM
+                        parseHour(text, paddedHour, array);
+                        parseMinute(text, isColon, true, array);
+                        break;
+                    case 5: // +HHMMss
+                    case 6: // +HH:MM:ss
+                    case 17: // +H:MM:ss
+                        parseHour(text, paddedHour, array);
+                        parseMinute(text, isColon, true, array);
+                        parseSecond(text, isColon, false, array);
+                        break;
+                    case 7: // +HHMMSS
+                    case 8: // +HH:MM:SS
+                    case 19: // +H:MM:SS
+                        parseHour(text, paddedHour, array);
+                        parseMinute(text, isColon, true, array);
+                        parseSecond(text, isColon, true, array);
+                        break;
+                    case 9: // +HHmmss
+                    case 10: // +HH:mm:ss
+                    case 21: // +H:mm:ss
+                        parseHour(text, paddedHour, array);
+                        parseOptionalMinuteSecond(text, isColon, array);
+                        break;
+                    case 12: // +Hmm
+                        parseVariableWidthDigits(text, 1, 4, array);
+                        break;
+                    case 14: // +HMM
+                        parseVariableWidthDigits(text, 3, 4, array);
+                        break;
+                    case 16: // +HMMss
+                        parseVariableWidthDigits(text, 3, 6, array);
+                        break;
+                    case 18: // +HMMSS
+                        parseVariableWidthDigits(text, 5, 6, array);
+                        break;
+                    case 20: // +Hmmss
+                        parseVariableWidthDigits(text, 1, 6, array);
+                        break;
+                }
+                if (array[0] > 0) {
+                    if (array[1] > 23 || array[2] > 59 || array[3] > 59) {
+                        throw new DateTimeException("Value out of range: Hour[0-23], Minute[0-59], Second[0-59]");
+                    }
                     long offsetSecs = negative * (array[1] * 3600L + array[2] * 60L + array[3]);
                     return context.setParsedField(OFFSET_SECONDS, offsetSecs, position, array[0]);
                 }
@@ -3588,42 +3690,118 @@
             return ~position;
         }
 
-        /**
-         * Parse a two digit zero-prefixed number.
-         *
-         * @param array  the array of parsed data, 0=pos,1=hours,2=mins,3=secs, not null
-         * @param arrayIndex  the index to parse the value into
-         * @param parseText  the offset ID, not null
-         * @param required  whether this number is required
-         * @param parseType the offset pattern type
-         * @return true if an error occurred
-         */
-        private boolean parseNumber(int[] array, int arrayIndex, CharSequence parseText, boolean required, int parseType) {
-            if ((parseType + 3) / 2 < arrayIndex) {
-                return false;  // ignore seconds/minutes
+        private void parseHour(CharSequence parseText, boolean paddedHour, int[] array) {
+            if (paddedHour) {
+                // parse two digits
+                if (!parseDigits(parseText, false, 1, array)) {
+                    array[0] = ~array[0];
+                }
+            } else {
+                // parse one or two digits
+                parseVariableWidthDigits(parseText, 1, 2, array);
             }
+        }
+
+        private void parseMinute(CharSequence parseText, boolean isColon, boolean mandatory, int[] array) {
+            if (!parseDigits(parseText, isColon, 2, array)) {
+                if (mandatory) {
+                    array[0] = ~array[0];
+                }
+            }
+        }
+
+        private void parseSecond(CharSequence parseText, boolean isColon, boolean mandatory, int[] array) {
+            if (!parseDigits(parseText, isColon, 3, array)) {
+                if (mandatory) {
+                    array[0] = ~array[0];
+                }
+            }
+        }
+
+        private void parseOptionalMinuteSecond(CharSequence parseText, boolean isColon, int[] array) {
+            if (parseDigits(parseText, isColon, 2, array)) {
+                parseDigits(parseText, isColon, 3, array);
+            }
+        }
+
+        private boolean parseDigits(CharSequence parseText, boolean isColon, int arrayIndex, int[] array) {
             int pos = array[0];
-            if ((parseType % 2) == 0 && arrayIndex > 1) {
+            if (pos < 0) {
+                return true;
+            }
+            if (isColon && arrayIndex != 1) { //  ':' will precede only in case of minute/second
                 if (pos + 1 > parseText.length() || parseText.charAt(pos) != ':') {
-                    return required;
+                    return false;
                 }
                 pos++;
             }
             if (pos + 2 > parseText.length()) {
-                return required;
+                return false;
             }
             char ch1 = parseText.charAt(pos++);
             char ch2 = parseText.charAt(pos++);
             if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') {
-                return required;
+                return false;
             }
             int value = (ch1 - 48) * 10 + (ch2 - 48);
             if (value < 0 || value > 59) {
-                return required;
+                return false;
             }
             array[arrayIndex] = value;
             array[0] = pos;
-            return false;
+            return true;
+        }
+
+        private void parseVariableWidthDigits(CharSequence parseText, int minDigits, int maxDigits, int[] array) {
+            // scan the text to find the available number of digits up to maxDigits
+            // so long as the number available is minDigits or more, the input is valid
+            // then parse the number of available digits
+            int pos = array[0];
+            int available = 0;
+            char[] chars = new char[maxDigits];
+            for (int i = 0; i < maxDigits; i++) {
+                if (pos + 1  > parseText.length()) {
+                    break;
+                }
+                char ch = parseText.charAt(pos++);
+                if (ch < '0' || ch > '9') {
+                    pos--;
+                    break;
+                }
+                chars[i] = ch;
+                available++;
+            }
+            if (available < minDigits) {
+                array[0] = ~array[0];
+                return;
+            }
+            switch (available) {
+                case 1:
+                    array[1] = (chars[0] - 48);
+                    break;
+                case 2:
+                    array[1] = ((chars[0] - 48) * 10 + (chars[1] - 48));
+                    break;
+                case 3:
+                    array[1] = (chars[0] - 48);
+                    array[2] = ((chars[1] - 48) * 10 + (chars[2] - 48));
+                    break;
+                case 4:
+                    array[1] = ((chars[0] - 48) * 10 + (chars[1] - 48));
+                    array[2] = ((chars[2] - 48) * 10 + (chars[3] - 48));
+                    break;
+                case 5:
+                    array[1] = (chars[0] - 48);
+                    array[2] = ((chars[1] - 48) * 10 + (chars[2] - 48));
+                    array[3] = ((chars[3] - 48) * 10 + (chars[4] - 48));
+                    break;
+                case 6:
+                    array[1] = ((chars[0] - 48) * 10 + (chars[1] - 48));
+                    array[2] = ((chars[2] - 48) * 10 + (chars[3] - 48));
+                    array[3] = ((chars[4] - 48) * 10 + (chars[5] - 48));
+                    break;
+            }
+            array[0] = pos;
         }
 
         @Override
--- a/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java	Wed Jul 27 14:27:46 2016 -0700
+++ b/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java	Thu Jul 28 10:28:21 2016 +0000
@@ -407,6 +407,127 @@
                 {"+HH:mm:ss", 2, 0, 45, "+02:00:45"},
                 {"+HH:mm:ss", 2, 30, 45, "+02:30:45"},
 
+                {"+H", 2, 0, 0, "+2"},
+                {"+H", -2, 0, 0, "-2"},
+                {"+H", 2, 30, 0, "+2"},
+                {"+H", 2, 0, 45, "+2"},
+                {"+H", 2, 30, 45, "+2"},
+                {"+H", 12, 0, 0, "+12"},
+                {"+H", -12, 0, 0, "-12"},
+                {"+H", 12, 30, 0, "+12"},
+                {"+H", 12, 0, 45, "+12"},
+                {"+H", 12, 30, 45, "+12"},
+
+                {"+Hmm", 2, 0, 0, "+2"},
+                {"+Hmm", -2, 0, 0, "-2"},
+                {"+Hmm", 2, 30, 0, "+230"},
+                {"+Hmm", 2, 0, 45, "+2"},
+                {"+Hmm", 2, 30, 45, "+230"},
+                {"+Hmm", 12, 0, 0, "+12"},
+                {"+Hmm", -12, 0, 0, "-12"},
+                {"+Hmm", 12, 30, 0, "+1230"},
+                {"+Hmm", 12, 0, 45, "+12"},
+                {"+Hmm", 12, 30, 45, "+1230"},
+
+                {"+H:mm", 2, 0, 0, "+2"},
+                {"+H:mm", -2, 0, 0, "-2"},
+                {"+H:mm", 2, 30, 0, "+2:30"},
+                {"+H:mm", 2, 0, 45, "+2"},
+                {"+H:mm", 2, 30, 45, "+2:30"},
+                {"+H:mm", 12, 0, 0, "+12"},
+                {"+H:mm", -12, 0, 0, "-12"},
+                {"+H:mm", 12, 30, 0, "+12:30"},
+                {"+H:mm", 12, 0, 45, "+12"},
+                {"+H:mm", 12, 30, 45, "+12:30"},
+
+                {"+HMM", 2, 0, 0, "+200"},
+                {"+HMM", -2, 0, 0, "-200"},
+                {"+HMM", 2, 30, 0, "+230"},
+                {"+HMM", 2, 0, 45, "+200"},
+                {"+HMM", 2, 30, 45, "+230"},
+                {"+HMM", 12, 0, 0, "+1200"},
+                {"+HMM", -12, 0, 0, "-1200"},
+                {"+HMM", 12, 30, 0, "+1230"},
+                {"+HMM", 12, 0, 45, "+1200"},
+                {"+HMM", 12, 30, 45, "+1230"},
+
+                {"+H:MM", 2, 0, 0, "+2:00"},
+                {"+H:MM", -2, 0, 0, "-2:00"},
+                {"+H:MM", 2, 30, 0, "+2:30"},
+                {"+H:MM", 2, 0, 45, "+2:00"},
+                {"+H:MM", 2, 30, 45, "+2:30"},
+                {"+H:MM", 12, 0, 0, "+12:00"},
+                {"+H:MM", -12, 0, 0, "-12:00"},
+                {"+H:MM", 12, 30, 0, "+12:30"},
+                {"+H:MM", 12, 0, 45, "+12:00"},
+                {"+H:MM", 12, 30, 45, "+12:30"},
+
+                {"+HMMss", 2, 0, 0, "+200"},
+                {"+HMMss", -2, 0, 0, "-200"},
+                {"+HMMss", 2, 30, 0, "+230"},
+                {"+HMMss", 2, 0, 45, "+20045"},
+                {"+HMMss", 2, 30, 45, "+23045"},
+                {"+HMMss", 12, 0, 0, "+1200"},
+                {"+HMMss", -12, 0, 0, "-1200"},
+                {"+HMMss", 12, 30, 0, "+1230"},
+                {"+HMMss", 12, 0, 45, "+120045"},
+                {"+HMMss", 12, 30, 45, "+123045"},
+
+                {"+H:MM:ss", 2, 0, 0, "+2:00"},
+                {"+H:MM:ss", -2, 0, 0, "-2:00"},
+                {"+H:MM:ss", 2, 30, 0, "+2:30"},
+                {"+H:MM:ss", 2, 0, 45, "+2:00:45"},
+                {"+H:MM:ss", 2, 30, 45, "+2:30:45"},
+                {"+H:MM:ss", 12, 0, 0, "+12:00"},
+                {"+H:MM:ss", -12, 0, 0, "-12:00"},
+                {"+H:MM:ss", 12, 30, 0, "+12:30"},
+                {"+H:MM:ss", 12, 0, 45, "+12:00:45"},
+                {"+H:MM:ss", 12, 30, 45, "+12:30:45"},
+
+                {"+HMMSS", 2, 0, 0, "+20000"},
+                {"+HMMSS", -2, 0, 0, "-20000"},
+                {"+HMMSS", 2, 30, 0, "+23000"},
+                {"+HMMSS", 2, 0, 45, "+20045"},
+                {"+HMMSS", 2, 30, 45, "+23045"},
+                {"+HMMSS", 12, 0, 0, "+120000"},
+                {"+HMMSS", -12, 0, 0, "-120000"},
+                {"+HMMSS", 12, 30, 0, "+123000"},
+                {"+HMMSS", 12, 0, 45, "+120045"},
+                {"+HMMSS", 12, 30, 45, "+123045"},
+
+                {"+H:MM:SS", 2, 0, 0, "+2:00:00"},
+                {"+H:MM:SS", -2, 0, 0, "-2:00:00"},
+                {"+H:MM:SS", 2, 30, 0, "+2:30:00"},
+                {"+H:MM:SS", 2, 0, 45, "+2:00:45"},
+                {"+H:MM:SS", 2, 30, 45, "+2:30:45"},
+                {"+H:MM:SS", 12, 0, 0, "+12:00:00"},
+                {"+H:MM:SS", -12, 0, 0, "-12:00:00"},
+                {"+H:MM:SS", 12, 30, 0, "+12:30:00"},
+                {"+H:MM:SS", 12, 0, 45, "+12:00:45"},
+                {"+H:MM:SS", 12, 30, 45, "+12:30:45"},
+
+                {"+Hmmss", 2, 0, 0, "+2"},
+                {"+Hmmss", -2, 0, 0, "-2"},
+                {"+Hmmss", 2, 30, 0, "+230"},
+                {"+Hmmss", 2, 0, 45, "+20045"},
+                {"+Hmmss", 2, 30, 45, "+23045"},
+                {"+Hmmss", 12, 0, 0, "+12"},
+                {"+Hmmss", -12, 0, 0, "-12"},
+                {"+Hmmss", 12, 30, 0, "+1230"},
+                {"+Hmmss", 12, 0, 45, "+120045"},
+                {"+Hmmss", 12, 30, 45, "+123045"},
+
+                {"+H:mm:ss", 2, 0, 0, "+2"},
+                {"+H:mm:ss", -2, 0, 0, "-2"},
+                {"+H:mm:ss", 2, 30, 0, "+2:30"},
+                {"+H:mm:ss", 2, 0, 45, "+2:00:45"},
+                {"+H:mm:ss", 2, 30, 45, "+2:30:45"},
+                {"+H:mm:ss", 12, 0, 0, "+12"},
+                {"+H:mm:ss", -12, 0, 0, "-12"},
+                {"+H:mm:ss", 12, 30, 0, "+12:30"},
+                {"+H:mm:ss", 12, 0, 45, "+12:00:45"},
+                {"+H:mm:ss", 12, 30, 45, "+12:30:45"},
+
 
         };
     }
@@ -437,8 +558,6 @@
             {"HH:MM:ss"},
             {"HHMMSS"},
             {"HH:MM:SS"},
-            {"+H"},
-            {"+HMM"},
             {"+HHM"},
             {"+A"},
         };
@@ -1188,6 +1307,146 @@
             {"+HH:mm:ss", "+01", 3600},
             {"+HH:mm:ss", "+01:01", 3660},
             {"+HH:mm:ss", "+01:01:01", 3661},
+
+            {"+H", "+1", 3600},
+            {"+H", "+101", 3660},
+            {"+H", "+10101", 3661},
+            {"+H", "+1:01", 3660},
+            {"+H", "+1:01:01", 3661},
+            {"+H", "+01", 3600},
+            {"+H", "+0101", 3660},
+            {"+H", "+010101", 3661},
+            {"+H", "+01:01", 3660},
+            {"+H", "+01:01:01", 3661},
+            {"+Hmm", "+1", 3600},
+            {"+Hmm", "+101", 3660},
+            {"+Hmm", "+10101", 3661},
+            {"+Hmm", "+01", 3600},
+            {"+Hmm", "+0101", 3660},
+            {"+Hmm", "+010101", 3661},
+            {"+H:mm", "+1", 3600},
+            {"+H:mm", "+1:01", 3660},
+            {"+H:mm", "+1:01:01", 3661},
+            {"+H:mm", "+01", 3600},
+            {"+H:mm", "+01:01", 3660},
+            {"+H:mm", "+01:01:01", 3661},
+            {"+HMM", "+1", 3600},
+            {"+HMM", "+101", 3660},
+            {"+HMM", "+10101", 3661},
+            {"+HMM", "+01", 3600},
+            {"+HMM", "+0101", 3660},
+            {"+HMM", "+010101", 3661},
+            {"+H:MM", "+1", 3600},
+            {"+H:MM", "+1:01", 3660},
+            {"+H:MM", "+1:01:01", 3661},
+            {"+H:MM", "+01", 3600},
+            {"+H:MM", "+01:01", 3660},
+            {"+H:MM", "+01:01:01", 3661},
+            {"+HMMss", "+1", 3600},
+            {"+HMMss", "+101", 3660},
+            {"+HMMss", "+10101", 3661},
+            {"+HMMss", "+01", 3600},
+            {"+HMMss", "+0101", 3660},
+            {"+HMMss", "+010101", 3661},
+            {"+H:MM:ss", "+1", 3600},
+            {"+H:MM:ss", "+1:01", 3660},
+            {"+H:MM:ss", "+1:01:01", 3661},
+            {"+H:MM:ss", "+01", 3600},
+            {"+H:MM:ss", "+01:01", 3660},
+            {"+H:MM:ss", "+01:01:01", 3661},
+            {"+HMMSS", "+1", 3600},
+            {"+HMMSS", "+101", 3660},
+            {"+HMMSS", "+10101", 3661},
+            {"+HMMSS", "+01", 3600},
+            {"+HMMSS", "+0101", 3660},
+            {"+HMMSS", "+010101", 3661},
+            {"+H:MM:SS", "+1", 3600},
+            {"+H:MM:SS", "+1:01", 3660},
+            {"+H:MM:SS", "+1:01:01", 3661},
+            {"+H:MM:SS", "+01", 3600},
+            {"+H:MM:SS", "+01:01", 3660},
+            {"+H:MM:SS", "+01:01:01", 3661},
+            {"+Hmmss", "+1", 3600},
+            {"+Hmmss", "+101", 3660},
+            {"+Hmmss", "+10101", 3661},
+            {"+Hmmss", "+01", 3600},
+            {"+Hmmss", "+0101", 3660},
+            {"+Hmmss", "+010101", 3661},
+            {"+H:mm:ss", "+1", 3600},
+            {"+H:mm:ss", "+1:01", 3660},
+            {"+H:mm:ss", "+1:01:01", 3661},
+            {"+H:mm:ss", "+01", 3600},
+            {"+H:mm:ss", "+01:01", 3660},
+            {"+H:mm:ss", "+01:01:01", 3661},
+        };
+    }
+
+    @DataProvider(name="strictDoubleDigitHourOffsetParseData")
+    Object[][] data_strictDoubleDigitHour_offset_parse() {
+        return new Object[][] {
+            {"+HH", "+01", 3600},
+            {"+HHmm", "+01", 3600},
+            {"+HHmm", "+0101", 3660},
+            {"+HH:mm", "+01", 3600},
+            {"+HH:mm", "+01:01", 3660},
+            {"+HHMM", "+0101", 3660},
+            {"+HH:MM", "+01:01", 3660},
+            {"+HHMMss", "+0101", 3660},
+            {"+HHMMss", "+010101", 3661},
+            {"+HH:MM:ss", "+01:01", 3660},
+            {"+HH:MM:ss", "+01:01:01", 3661},
+            {"+HHMMSS", "+010101", 3661},
+            {"+HH:MM:SS", "+01:01:01", 3661},
+            {"+HHmmss", "+01", 3600},
+            {"+HHmmss", "+0101", 3660},
+            {"+HHmmss", "+010101", 3661},
+            {"+HH:mm:ss", "+01", 3600},
+            {"+HH:mm:ss", "+01:01", 3660},
+            {"+HH:mm:ss", "+01:01:01", 3661},
+        };
+    }
+
+    @DataProvider(name="strictSingleDigitHourOffsetParseData")
+    Object[][] data_strictSingleDigitHour_offset_parse() {
+        return new Object[][] {
+            {"+H", "+01", 3600},
+            {"+H", "+1", 3600},
+            {"+Hmm", "+01", 3600},
+            {"+Hmm", "+0101", 3660},
+            {"+Hmm", "+1", 3600},
+            {"+Hmm", "+101", 3660},
+            {"+H:mm", "+01", 3600},
+            {"+H:mm", "+01:01", 3660},
+            {"+H:mm", "+1", 3600},
+            {"+H:mm", "+1:01", 3660},
+            {"+HMM", "+0101", 3660},
+            {"+HMM", "+101", 3660},
+            {"+H:MM", "+01:01", 3660},
+            {"+H:MM", "+1:01", 3660},
+            {"+HMMss", "+0101", 3660},
+            {"+HMMss", "+010101", 3661},
+            {"+HMMss", "+101", 3660},
+            {"+HMMss", "+10101", 3661},
+            {"+H:MM:ss", "+01:01", 3660},
+            {"+H:MM:ss", "+01:01:01", 3661},
+            {"+H:MM:ss", "+1:01", 3660},
+            {"+H:MM:ss", "+1:01:01", 3661},
+            {"+HMMSS", "+010101", 3661},
+            {"+HMMSS", "+10101", 3661},
+            {"+H:MM:SS", "+01:01:01", 3661},
+            {"+H:MM:SS", "+1:01:01", 3661},
+            {"+Hmmss", "+01", 3600},
+            {"+Hmmss", "+0101", 3660},
+            {"+Hmmss", "+010101", 3661},
+            {"+Hmmss", "+1", 3600},
+            {"+Hmmss", "+101", 3660},
+            {"+Hmmss", "+10101", 3661},
+            {"+H:mm:ss", "+01", 3600},
+            {"+H:mm:ss", "+01:01", 3660},
+            {"+H:mm:ss", "+01:01:01", 3661},
+            {"+H:mm:ss", "+1", 3600},
+            {"+H:mm:ss", "+1:01", 3660},
+            {"+H:mm:ss", "+1:01:01", 3661},
         };
     }
 
@@ -1203,22 +1462,226 @@
                      3600);
     }
 
+    @Test(dataProvider="strictDoubleDigitHourOffsetParseData")
+    public void test_strictDoubleDigitHour_offset_parse_1(String pattern, String offset, int offsetSeconds) {
+        assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z").toFormatter()
+                .parse(offset).get(OFFSET_SECONDS), offsetSeconds);
+    }
+
+    @Test(dataProvider="strictDoubleDigitHourOffsetParseData")
+    public void test_strictDoubleDigitHour_offset_parse_2(String pattern, String offset, int offsetSeconds) {
+        assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z")
+                .appendLiteral("text").toFormatter().parse(offset + "text").get(OFFSET_SECONDS), offsetSeconds);
+    }
+
+    @Test(dataProvider="strictSingleDigitHourOffsetParseData")
+    public void test_strictSingleDigitHour_offset_parse_1(String pattern, String offset, int offsetSeconds) {
+        assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z").toFormatter()
+                .parse(offset).get(OFFSET_SECONDS), offsetSeconds);
+    }
+
+    @Test(dataProvider="strictSingleDigitHourOffsetParseData")
+    public void test_strictSingleDigitHour_offset_parse_2(String pattern, String offset, int offsetSeconds) {
+        assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z")
+                .appendLiteral("text").toFormatter().parse(offset + "text").get(OFFSET_SECONDS), offsetSeconds);
+    }
+
+    @DataProvider(name="strictOffsetAdjacentParseValidPatternData")
+    Object[][] data_strict_offset_adjacentParse_validPattern() {
+        return new Object[][] {
+            {"+HH", "+01", 3600},
+            {"+HHmm", "+0101", 3660},
+            {"+HH:mm", "+01", 3600},
+            {"+HH:mm", "+01:01", 3660},
+            {"+HHMM", "+0101", 3660},
+            {"+HH:MM", "+01:01", 3660},
+            {"+HHMMss", "+010101", 3661},
+            {"+HH:MM:ss", "+01:01", 3660},
+            {"+HH:MM:ss", "+01:01:01", 3661},
+            {"+HHMMSS", "+010101", 3661},
+            {"+HH:MM:SS", "+01:01:01", 3661},
+            {"+HHmmss", "+010101", 3661},
+            {"+HH:mm:ss", "+01", 3600},
+            {"+HH:mm:ss", "+01:01", 3660},
+            {"+HH:mm:ss", "+01:01:01", 3661},
+
+            {"+H", "+01", 3600},
+            {"+Hmm", "+0101", 3660},
+            {"+H:mm", "+01", 3600},
+            {"+H:mm", "+01:01", 3660},
+            {"+H:mm", "+1:01", 3660},
+            {"+HMM", "+0101", 3660},
+            {"+H:MM", "+01:01", 3660},
+            {"+H:MM", "+1:01", 3660},
+            {"+HMMss", "+010101", 3661},
+            {"+H:MM:ss", "+01:01", 3660},
+            {"+H:MM:ss", "+01:01:01", 3661},
+            {"+H:MM:ss", "+1:01", 3660},
+            {"+H:MM:ss", "+1:01:01", 3661},
+            {"+HMMSS", "+010101", 3661},
+            {"+H:MM:SS", "+01:01:01", 3661},
+            {"+H:MM:SS", "+1:01:01", 3661},
+            {"+Hmmss", "+010101", 3661},
+            {"+H:mm:ss", "+01", 3600},
+            {"+H:mm:ss", "+01:01", 3660},
+            {"+H:mm:ss", "+01:01:01", 3661},
+            {"+H:mm:ss", "+1:01", 3660},
+            {"+H:mm:ss", "+1:01:01", 3661},
+        };
+    }
+
+    @Test(dataProvider="strictOffsetAdjacentParseValidPatternData")
+    public void test_strict_offset_adjacentValidPattern_parse(String pattern, String offset, int offsetSeconds) {
+        TemporalAccessor tmp = new DateTimeFormatterBuilder().appendOffset(pattern, "Z")
+                .appendValue(HOUR_OF_DAY, 2).toFormatter().parse(offset + "12");
+        assertEquals(tmp.get(OFFSET_SECONDS), offsetSeconds);
+        assertEquals(tmp.get(HOUR_OF_DAY), 12);
+    }
+
+    @DataProvider(name="strictOffsetAdjacentParseInvalidPatternData")
+    Object[][] data_strict_offset_adjacentParse_invalidPattern() {
+        return new Object[][] {
+            {"+HHmm", "+01", 3600},
+            {"+HHMMss", "+0101", 3660},
+            {"+HHmmss", "+01", 3600},
+            {"+HHmmss", "+0101", 3660},
+            {"+H", "+1", 3600},
+            {"+Hmm", "+01", 3600},
+            {"+H:mm", "+1", 3600},
+            {"+Hmm", "+1", 3600},
+            {"+Hmm", "+101", 3660},
+            {"+HMM", "+101", 3660},
+            {"+HMMss", "+0101", 3660},
+            {"+HMMss", "+101", 3660},
+            {"+HMMss", "+10101", 3661},
+            {"+HMMSS", "+10101", 3661},
+            {"+Hmmss", "+01", 3600},
+            {"+Hmmss", "+0101", 3660},
+            {"+Hmmss", "+1", 3600},
+            {"+Hmmss", "+101", 3660},
+            {"+Hmmss", "+10101", 3661},
+            {"+H:mm:ss", "+1", 3600},
+        };
+    }
+
+    @Test(dataProvider="strictOffsetAdjacentParseInvalidPatternData", expectedExceptions=DateTimeParseException.class)
+    public void test_strict_offset_adjacentInvalidPattern_parse(String pattern, String offset, int offsetSeconds) {
+       new DateTimeFormatterBuilder().appendOffset(pattern, "Z").appendValue(HOUR_OF_DAY, 2)
+               .toFormatter().parse(offset + "12");
+    }
+
+    @DataProvider(name="lenientOffsetAdjacentParseValidPatternData")
+    Object[][] data_lenient_offset_adjacentParse_validPattern() {
+        return new Object[][] {
+            {"+HH:mm", "+01", 3600},
+            {"+HH:mm", "+01:01", 3660},
+            {"+HH:MM", "+01:01", 3660},
+            {"+HH:MM:ss", "+01:01", 3660},
+            {"+HH:MM:ss", "+01:01:01", 3661},
+            {"+HHMMSS", "+010101", 3661},
+            {"+HH:MM:SS", "+01:01:01", 3661},
+            {"+HHmmss", "+010101", 3661},
+            {"+HH:mm:ss", "+01", 3600},
+            {"+HH:mm:ss", "+01:01", 3660},
+            {"+HH:mm:ss", "+01:01:01", 3661},
+            {"+H:mm", "+01", 3600},
+            {"+H:mm", "+01:01", 3660},
+            {"+H:mm", "+1:01", 3660},
+            {"+H:MM", "+01:01", 3660},
+            {"+H:MM", "+1:01", 3660},
+            {"+HMMss", "+010101", 3661},
+            {"+H:MM:ss", "+01:01", 3660},
+            {"+H:MM:ss", "+01:01:01", 3661},
+            {"+H:MM:ss", "+1:01", 3660},
+            {"+H:MM:ss", "+1:01:01", 3661},
+            {"+HMMSS", "+010101", 3661},
+            {"+H:MM:SS", "+01:01:01", 3661},
+            {"+H:MM:SS", "+1:01:01", 3661},
+            {"+Hmmss", "+010101", 3661},
+            {"+H:mm:ss", "+01", 3600},
+            {"+H:mm:ss", "+01:01", 3660},
+            {"+H:mm:ss", "+01:01:01", 3661},
+            {"+H:mm:ss", "+1:01", 3660},
+            {"+H:mm:ss", "+1:01:01", 3661},
+        };
+    }
+
+    @Test(dataProvider="lenientOffsetAdjacentParseValidPatternData")
+    public void test_lenient_offset_adjacentValidPattern_parse(String pattern, String offset, int offsetSeconds) {
+        TemporalAccessor tmp = new DateTimeFormatterBuilder().parseLenient()
+                .appendOffset(pattern, "Z").appendValue(HOUR_OF_DAY, 2).toFormatter().parse(offset + "12");
+        assertEquals(tmp.get(OFFSET_SECONDS), offsetSeconds);
+        assertEquals(tmp.get(HOUR_OF_DAY), 12);
+    }
+
+    @Test
+    public void test_lenient_offset_adjacentValidPattern_parse1() {
+        TemporalAccessor tmp = new DateTimeFormatterBuilder().parseLenient()
+                .appendOffset("+HMMSS", "Z").appendValue(HOUR_OF_DAY, 2).toFormatter().parse("+10101" + "12");
+        //Equivalent to +101011. In lenient mode, offset will parse upto 6 digit if possible.
+        //It will take 1 digit from HOUR_OF_DAY.
+        assertEquals(tmp.get(OFFSET_SECONDS), 36611);
+        assertEquals(tmp.get(HOUR_OF_DAY), 2);
+    }
+
+  @DataProvider(name="lenientOffsetAdjacentParseInvalidPatternData")
+    Object[][] data_lenient_offset_adjacentParse_invalidPattern() {
+        return new Object[][] {
+            {"+HH", "+01", 3600},
+            {"+HHmm", "+0101", 3660},
+            {"+HHMM", "+0101", 3660},
+            {"+H", "+01", 3600},
+            {"+Hmm", "+0101", 3660},
+            {"+HMM", "+0101", 3660},
+        };
+    }
+
+    @Test(dataProvider="lenientOffsetAdjacentParseInvalidPatternData", expectedExceptions=DateTimeParseException.class)
+    public void test_lenient_offset_adjacentInvalidPattern_parse(String pattern, String offset, int offsetSeconds) {
+       new DateTimeFormatterBuilder().parseLenient().appendOffset(pattern, "Z")
+               .appendValue(HOUR_OF_DAY, 2).toFormatter().parse(offset + "12");
+    }
+
+    @DataProvider(name="badValues")
+    Object[][] data_badOffsetValues() {
+        return new Object[][] {
+            {"+HH", "+24"},
+            {"+HHMM", "-1361"},
+            {"+HH:MM:ss", "+13:12:66"},
+            {"+HH:MM:SS", "+24:60:60"},
+            {"+HHMMSS", "369999"},
+            {"+H:MM", "+28:12"},
+        };
+    }
+
+    @Test(dataProvider="badValues", expectedExceptions=DateTimeParseException.class)
+    public void test_badOffset_parse(String pattern, String offset) {
+        new DateTimeFormatterBuilder().appendOffset(pattern, "Z").toFormatter().parse(offset);
+    }
+
     @Test(expectedExceptions=DateTimeParseException.class)
     public void test_strict_appendOffsetId() {
-        assertEquals(new DateTimeFormatterBuilder().appendOffsetId().toFormatter().parse("+01").get(OFFSET_SECONDS),
-                     3600);
+        new DateTimeFormatterBuilder().appendOffsetId().toFormatter().parse("+01");
     }
 
     @Test(expectedExceptions=DateTimeParseException.class)
     public void test_strict_appendOffset_1() {
-        assertEquals(new DateTimeFormatterBuilder().appendOffset("+HH:MM:ss", "Z").toFormatter().parse("+01").get(OFFSET_SECONDS),
-                     3600);
+        new DateTimeFormatterBuilder().appendOffset("+HH:MM:ss", "Z").toFormatter().parse("+01");
     }
 
     @Test(expectedExceptions=DateTimeParseException.class)
     public void test_strict_appendOffset_2() {
-        assertEquals(new DateTimeFormatterBuilder().appendOffset("+HHMMss", "Z").toFormatter().parse("+01").get(OFFSET_SECONDS),
-                     3600);
+        new DateTimeFormatterBuilder().appendOffset("+HHMMss", "Z").toFormatter().parse("+01");
+    }
+
+    @Test(expectedExceptions=DateTimeParseException.class)
+    public void test_strict_appendOffset_3() {
+        new DateTimeFormatterBuilder().appendOffset("+H:MM:ss", "Z").toFormatter().parse("+1");
+    }
+
+    @Test(expectedExceptions=DateTimeParseException.class)
+    public void test_strict_appendOffset_4() {
+        new DateTimeFormatterBuilder().appendOffset("+HMMss", "Z").toFormatter().parse("+1");
     }
 
     @Test
--- a/test/java/time/tck/java/time/format/TCKOffsetPrinterParser.java	Wed Jul 27 14:27:46 2016 -0700
+++ b/test/java/time/tck/java/time/format/TCKOffsetPrinterParser.java	Thu Jul 28 10:28:21 2016 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -93,6 +93,14 @@
     private static final ZoneOffset OFFSET_M000045 = ZoneOffset.ofHoursMinutesSeconds(0, 0, -45);
     private static final LocalDateTime DT_2012_06_30_12_30_40 = LocalDateTime.of(2012, 6, 30, 12, 30, 40);
 
+    private static final ZoneOffset OFFSET_P1100 = ZoneOffset.ofHours(11);
+    private static final ZoneOffset OFFSET_P1123 = ZoneOffset.ofHoursMinutes(11, 23);
+    private static final ZoneOffset OFFSET_P1023 = ZoneOffset.ofHoursMinutes(10, 23);
+    private static final ZoneOffset OFFSET_P112345 = ZoneOffset.ofHoursMinutesSeconds(11, 23, 45);
+    private static final ZoneOffset OFFSET_P100045 = ZoneOffset.ofHoursMinutesSeconds(10, 0, 45);
+    private static final ZoneOffset OFFSET_M1100 = ZoneOffset.ofHours(-11);
+    private static final ZoneOffset OFFSET_M1123 = ZoneOffset.ofHoursMinutes(-11, -23);
+    private static final ZoneOffset OFFSET_M112345 = ZoneOffset.ofHoursMinutesSeconds(-11, -23, -45);
     private DateTimeFormatterBuilder builder;
 
     @BeforeMethod
@@ -223,6 +231,212 @@
                 {"+HHmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-0023"},
                 {"+HHmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-012345"},
                 {"+HHmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-000045"},
+
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+1"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+1"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "Z"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+1"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P000045, "Z"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-1"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-1"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "Z"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-1"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "Z"},
+
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+1"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+123"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+023"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+123"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P000045, "Z"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-1"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-123"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-023"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-123"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "Z"},
+
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+100"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+123"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+023"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+123"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P000045, "Z"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-100"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-123"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-023"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-123"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "Z"},
+
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+1:00"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+1:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+0:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+1:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P000045, "Z"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-1:00"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-1:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-0:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-1:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "Z"},
+
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+100"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+123"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+023"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+12345"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P000045, "+00045"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-100"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-123"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-023"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-12345"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-00045"},
+
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+1:00"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+1:23"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+0:23"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+1:23:45"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-0:00:45"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-1:00"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-1:23"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-0:23"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-1:23:45"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-0:00:45"},
+
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+10000"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+12300"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+02300"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+12345"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-00045"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-10000"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-12300"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-02300"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-12345"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-00045"},
+
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+1:00:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+1:23:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+0:23:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+1:23:45"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-0:00:45"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-1:00:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-1:23:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-0:23:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-1:23:45"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-0:00:45"},
+
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+1"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+1:23"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+0:23"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+1:23:45"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-0:00:45"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-1"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-1:23"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-0:23"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-1:23:45"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-0:00:45"},
+
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_UTC, "Z"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0100, "+1"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0123, "+123"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P0023, "+023"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P012345, "+12345"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P000045, "+00045"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0100, "-1"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0123, "-123"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M0023, "-023"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M012345, "-12345"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M000045, "-00045"},
+
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+11"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+11"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+10"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+11"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_P100045, "+10"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-11"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-11"},
+                {"+H", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-11"},
+
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+11"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+1123"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+1023"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+1123"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_P100045, "+10"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-11"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-1123"},
+                {"+Hmm", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-1123"},
+
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+1100"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+1123"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+1023"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+1123"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_P100045, "+1000"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-1100"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-1123"},
+                {"+HMM", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-1123"},
+
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+11:00"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+11:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+10:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+11:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_P100045, "+10:00"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-11:00"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-11:23"},
+                {"+H:MM", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-11:23"},
+
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+1100"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+1123"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+1023"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+112345"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_P100045, "+100045"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-1100"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-1123"},
+                {"+HMMss", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-112345"},
+
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+11:00"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+11:23"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+10:23"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+11:23:45"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-11:00"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-11:23"},
+                {"+H:MM:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-11:23:45"},
+
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+110000"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+112300"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+102300"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+112345"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-110000"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-112300"},
+                {"+HMMSS", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-112345"},
+
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+11:00:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+11:23:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+10:23:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+11:23:45"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-11:00:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-11:23:00"},
+                {"+H:MM:SS", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-11:23:45"},
+
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+11"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+11:23"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+10:23"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+11:23:45"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-11"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-11:23"},
+                {"+H:mm:ss", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-11:23:45"},
+
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1100, "+11"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1123, "+1123"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P1023, "+1023"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P112345, "+112345"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_P100045, "+100045"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1100, "-11"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M1123, "-1123"},
+                {"+Hmmss", "Z", DT_2012_06_30_12_30_40, OFFSET_M112345, "-112345"},
         };
     }
 
--- a/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java	Wed Jul 27 14:27:46 2016 -0700
+++ b/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java	Thu Jul 28 10:28:21 2016 +0000
@@ -484,8 +484,6 @@
             {"HH:MM:ss"},
             {"HHMMSS"},
             {"HH:MM:SS"},
-            {"+H"},
-            {"+HMM"},
             {"+HHM"},
             {"+A"},
         };
--- a/test/java/time/test/java/time/format/TestZoneOffsetParser.java	Wed Jul 27 14:27:46 2016 -0700
+++ b/test/java/time/test/java/time/format/TestZoneOffsetParser.java	Thu Jul 28 10:28:21 2016 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -296,7 +296,7 @@
     @DataProvider(name="bigOffsets")
     Object[][] provider_bigOffsets() {
         return new Object[][] {
-            {"+HH", "+59", 59 * 3600},
+            {"+HH", "+19", 19 * 3600},
             {"+HH", "-19", -(19 * 3600)},
 
             {"+HHMM", "+1801", 18 * 3600 + 1 * 60},