--- old/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java 2016-07-25 08:43:01.643025446 +0300 +++ new/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java 2016-07-25 08:43:01.465025446 +0300 @@ -928,7 +928,24 @@ * second if non-zero, no colon *
  • {@code +HH:mm:ss} - hour, with minute if non-zero or with minute and * second if non-zero, with colon + *
  • {@code +H} - hour only, ignoring minute and second + *
  • {@code +Hmm} - hour, with minute if non-zero, ignoring second, no colon + *
  • {@code +H:mm} - hour, with minute if non-zero, ignoring second, with colon + *
  • {@code +HMM} - hour and minute, ignoring second, no colon + *
  • {@code +H:MM} - hour and minute, ignoring second, with colon + *
  • {@code +HMMss} - hour and minute, with second if non-zero, no colon + *
  • {@code +H:MM:ss} - hour and minute, with second if non-zero, with colon + *
  • {@code +HMMSS} - hour, minute and second, no colon + *
  • {@code +H:MM:SS} - hour, minute and second, with colon + *
  • {@code +Hmmss} - hour, with minute if non-zero or with minute and + * second if non-zero, no colon + *
  • {@code +H:mm:ss} - hour, with minute if non-zero or with minute and + * second if non-zero, with colon * + * 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 --- old/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java 2016-07-25 08:43:02.217025446 +0300 +++ new/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java 2016-07-25 08:43:02.030025446 +0300 @@ -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 --- old/test/java/time/tck/java/time/format/TCKOffsetPrinterParser.java 2016-07-25 08:43:02.756025446 +0300 +++ new/test/java/time/tck/java/time/format/TCKOffsetPrinterParser.java 2016-07-25 08:43:02.570025446 +0300 @@ -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"}, }; } --- old/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java 2016-07-25 08:43:03.277025446 +0300 +++ new/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java 2016-07-25 08:43:03.092025446 +0300 @@ -484,8 +484,6 @@ {"HH:MM:ss"}, {"HHMMSS"}, {"HH:MM:SS"}, - {"+H"}, - {"+HMM"}, {"+HHM"}, {"+A"}, }; --- old/test/java/time/test/java/time/format/TestZoneOffsetParser.java 2016-07-25 08:43:03.899025446 +0300 +++ new/test/java/time/test/java/time/format/TestZoneOffsetParser.java 2016-07-25 08:43:03.712025446 +0300 @@ -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},