< prev index next >

src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java

Print this page




 911      * hour digits is a colon or not.
 912      * If the offset cannot be parsed then an exception is thrown unless
 913      * the section of the formatter is optional.
 914      * <p>
 915      * The format of the offset is controlled by a pattern which must be one
 916      * of the following:
 917      * <ul>
 918      * <li>{@code +HH} - hour only, ignoring minute and second
 919      * <li>{@code +HHmm} - hour, with minute if non-zero, ignoring second, no colon
 920      * <li>{@code +HH:mm} - hour, with minute if non-zero, ignoring second, with colon
 921      * <li>{@code +HHMM} - hour and minute, ignoring second, no colon
 922      * <li>{@code +HH:MM} - hour and minute, ignoring second, with colon
 923      * <li>{@code +HHMMss} - hour and minute, with second if non-zero, no colon
 924      * <li>{@code +HH:MM:ss} - hour and minute, with second if non-zero, with colon
 925      * <li>{@code +HHMMSS} - hour, minute and second, no colon
 926      * <li>{@code +HH:MM:SS} - hour, minute and second, with colon
 927      * <li>{@code +HHmmss} - hour, with minute if non-zero or with minute and
 928      * second if non-zero, no colon
 929      * <li>{@code +HH:mm:ss} - hour, with minute if non-zero or with minute and
 930      * second if non-zero, with colon













 931      * </ul>




 932      * The "no offset" text controls what text is printed when the total amount of
 933      * the offset fields to be output is zero.
 934      * Example values would be 'Z', '+00:00', 'UTC' or 'GMT'.
 935      * Three formats are accepted for parsing UTC - the "no offset" text, and the
 936      * plus and minus versions of zero defined by the pattern.
 937      *
 938      * @param pattern  the pattern to use, not null
 939      * @param noOffsetText  the text to use when the offset is zero, not null
 940      * @return this, for chaining, not null
 941      * @throws IllegalArgumentException if the pattern is invalid
 942      */
 943     public DateTimeFormatterBuilder appendOffset(String pattern, String noOffsetText) {
 944         appendInternal(new OffsetIdPrinterParser(pattern, noOffsetText));
 945         return this;
 946     }
 947 
 948     /**
 949      * Appends the localized zone offset, such as 'GMT+01:00', to the formatter.
 950      * <p>
 951      * This appends a localized zone offset to the builder, the format of the


3459                 return ~position;
3460             }
3461             int successPos = pos;
3462             successPos = context.setParsedField(INSTANT_SECONDS, instantSecs, position, successPos);
3463             return context.setParsedField(NANO_OF_SECOND, nano, position, successPos);
3464         }
3465 
3466         @Override
3467         public String toString() {
3468             return "Instant()";
3469         }
3470     }
3471 
3472     //-----------------------------------------------------------------------
3473     /**
3474      * Prints or parses an offset ID.
3475      */
3476     static final class OffsetIdPrinterParser implements DateTimePrinterParser {
3477         static final String[] PATTERNS = new String[] {
3478             "+HH", "+HHmm", "+HH:mm", "+HHMM", "+HH:MM", "+HHMMss", "+HH:MM:ss", "+HHMMSS", "+HH:MM:SS", "+HHmmss", "+HH:mm:ss",

3479         };  // order used in pattern builder
3480         static final OffsetIdPrinterParser INSTANCE_ID_Z = new OffsetIdPrinterParser("+HH:MM:ss", "Z");
3481         static final OffsetIdPrinterParser INSTANCE_ID_ZERO = new OffsetIdPrinterParser("+HH:MM:ss", "0");
3482 
3483         private final String noOffsetText;
3484         private final int type;

3485 
3486         /**
3487          * Constructor.
3488          *
3489          * @param pattern  the pattern
3490          * @param noOffsetText  the text to use for UTC, not null
3491          */
3492         OffsetIdPrinterParser(String pattern, String noOffsetText) {
3493             Objects.requireNonNull(pattern, "pattern");
3494             Objects.requireNonNull(noOffsetText, "noOffsetText");
3495             this.type = checkPattern(pattern);

3496             this.noOffsetText = noOffsetText;
3497         }
3498 
3499         private int checkPattern(String pattern) {
3500             for (int i = 0; i < PATTERNS.length; i++) {
3501                 if (PATTERNS[i].equals(pattern)) {
3502                     return i;
3503                 }
3504             }
3505             throw new IllegalArgumentException("Invalid zone offset pattern: " + pattern);
3506         }
3507 








3508         @Override
3509         public boolean format(DateTimePrintContext context, StringBuilder buf) {
3510             Long offsetSecs = context.getValue(OFFSET_SECONDS);
3511             if (offsetSecs == null) {
3512                 return false;
3513             }
3514             int totalSecs = Math.toIntExact(offsetSecs);
3515             if (totalSecs == 0) {
3516                 buf.append(noOffsetText);
3517             } else {
3518                 int absHours = Math.abs((totalSecs / 3600) % 100);  // anything larger than 99 silently dropped
3519                 int absMinutes = Math.abs((totalSecs / 60) % 60);
3520                 int absSeconds = Math.abs(totalSecs % 60);
3521                 int bufPos = buf.length();
3522                 int output = absHours;
3523                 buf.append(totalSecs < 0 ? "-" : "+")
3524                     .append((char) (absHours / 10 + '0')).append((char) (absHours % 10 + '0'));
3525                 if ((type >= 3 && type < 9) || (type >= 9 && absSeconds > 0) || (type >= 1 && absMinutes > 0)) {
3526                     buf.append((type % 2) == 0 ? ":" : "")
3527                         .append((char) (absMinutes / 10 + '0')).append((char) (absMinutes % 10 + '0'));



3528                     output += absMinutes;
3529                     if (type == 7  || type == 8 || (type >= 5 && absSeconds > 0)) {
3530                         buf.append((type % 2) == 0 ? ":" : "")
3531                             .append((char) (absSeconds / 10 + '0')).append((char) (absSeconds % 10 + '0'));
3532                         output += absSeconds;
3533                     }
3534                 }
3535                 if (output == 0) {
3536                     buf.setLength(bufPos);
3537                     buf.append(noOffsetText);
3538                 }
3539             }
3540             return true;
3541         }
3542 






3543         @Override
3544         public int parse(DateTimeParseContext context, CharSequence text, int position) {
3545             int length = text.length();
3546             int noOffsetLen = noOffsetText.length();
3547             int parseType = type;
3548             if (context.isStrict() == false) {
3549                 if ((parseType > 0 && (parseType % 2) == 0) ||
3550                     (parseType == 0 && length > position + 3 && text.charAt(position + 3) == ':')) {
3551                     parseType = 10;
3552                 } else {
3553                     parseType = 9;
3554                 }
3555             }
3556             if (noOffsetLen == 0) {
3557                 if (position == length) {
3558                     return context.setParsedField(OFFSET_SECONDS, 0, position, position);
3559                 }
3560             } else {
3561                 if (position == length) {
3562                     return ~position;
3563                 }
3564                 if (context.subSequenceEquals(text, position, noOffsetText, 0, noOffsetLen)) {
3565                     return context.setParsedField(OFFSET_SECONDS, 0, position, position + noOffsetLen);
3566                 }
3567             }
3568 
3569             // parse normal plus/minus offset
3570             char sign = text.charAt(position);  // IOOBE if invalid position
3571             if (sign == '+' || sign == '-') {
3572                 // starts
3573                 int negative = (sign == '-' ? -1 : 1);


3574                 int[] array = new int[4];
3575                 array[0] = position + 1;
3576                 if ((parseNumber(array, 1, text, true, parseType) ||
3577                         parseNumber(array, 2, text, parseType >= 3 && parseType < 9, parseType) ||
3578                         parseNumber(array, 3, text, parseType == 7 || parseType == 8, parseType)) == false) {
3579                     // success









































































3580                     long offsetSecs = negative * (array[1] * 3600L + array[2] * 60L + array[3]);
3581                     return context.setParsedField(OFFSET_SECONDS, offsetSecs, position, array[0]);
3582                 }
3583             }
3584             // handle special case of empty no offset text
3585             if (noOffsetLen == 0) {
3586                 return context.setParsedField(OFFSET_SECONDS, 0, position, position);
3587             }
3588             return ~position;
3589         }
3590 
3591         /**
3592          * Parse a two digit zero-prefixed number.
3593          *
3594          * @param array  the array of parsed data, 0=pos,1=hours,2=mins,3=secs, not null
3595          * @param arrayIndex  the index to parse the value into
3596          * @param parseText  the offset ID, not null
3597          * @param required  whether this number is required
3598          * @param parseType the offset pattern type
3599          * @return true if an error occurred
3600          */
3601         private boolean parseNumber(int[] array, int arrayIndex, CharSequence parseText, boolean required, int parseType) {
3602             if ((parseType + 3) / 2 < arrayIndex) {
3603                 return false;  // ignore seconds/minutes













3604             }








3605             int pos = array[0];
3606             if ((parseType % 2) == 0 && arrayIndex > 1) {



3607                 if (pos + 1 > parseText.length() || parseText.charAt(pos) != ':') {
3608                     return required;
3609                 }
3610                 pos++;
3611             }
3612             if (pos + 2 > parseText.length()) {
3613                 return required;
3614             }
3615             char ch1 = parseText.charAt(pos++);
3616             char ch2 = parseText.charAt(pos++);
3617             if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') {
3618                 return required;
3619             }
3620             int value = (ch1 - 48) * 10 + (ch2 - 48);
3621             if (value < 0 || value > 59) {
3622                 return required;
3623             }
3624             array[arrayIndex] = value;
3625             array[0] = pos;
3626             return false;




















































3627         }
3628 
3629         @Override
3630         public String toString() {
3631             String converted = noOffsetText.replace("'", "''");
3632             return "Offset(" + PATTERNS[type] + ",'" + converted + "')";
3633         }
3634     }
3635 
3636     //-----------------------------------------------------------------------
3637     /**
3638      * Prints or parses an offset ID.
3639      */
3640     static final class LocalizedOffsetIdPrinterParser implements DateTimePrinterParser {
3641         private final TextStyle style;
3642 
3643         /**
3644          * Constructor.
3645          *
3646          * @param style  the style, not null




 911      * hour digits is a colon or not.
 912      * If the offset cannot be parsed then an exception is thrown unless
 913      * the section of the formatter is optional.
 914      * <p>
 915      * The format of the offset is controlled by a pattern which must be one
 916      * of the following:
 917      * <ul>
 918      * <li>{@code +HH} - hour only, ignoring minute and second
 919      * <li>{@code +HHmm} - hour, with minute if non-zero, ignoring second, no colon
 920      * <li>{@code +HH:mm} - hour, with minute if non-zero, ignoring second, with colon
 921      * <li>{@code +HHMM} - hour and minute, ignoring second, no colon
 922      * <li>{@code +HH:MM} - hour and minute, ignoring second, with colon
 923      * <li>{@code +HHMMss} - hour and minute, with second if non-zero, no colon
 924      * <li>{@code +HH:MM:ss} - hour and minute, with second if non-zero, with colon
 925      * <li>{@code +HHMMSS} - hour, minute and second, no colon
 926      * <li>{@code +HH:MM:SS} - hour, minute and second, with colon
 927      * <li>{@code +HHmmss} - hour, with minute if non-zero or with minute and
 928      * second if non-zero, no colon
 929      * <li>{@code +HH:mm:ss} - hour, with minute if non-zero or with minute and
 930      * second if non-zero, with colon
 931      * <li>{@code +H} - hour only, ignoring minute and second
 932      * <li>{@code +Hmm} - hour, with minute if non-zero, ignoring second, no colon
 933      * <li>{@code +H:mm} - hour, with minute if non-zero, ignoring second, with colon
 934      * <li>{@code +HMM} - hour and minute, ignoring second, no colon
 935      * <li>{@code +H:MM} - hour and minute, ignoring second, with colon
 936      * <li>{@code +HMMss} - hour and minute, with second if non-zero, no colon
 937      * <li>{@code +H:MM:ss} - hour and minute, with second if non-zero, with colon
 938      * <li>{@code +HMMSS} - hour, minute and second, no colon
 939      * <li>{@code +H:MM:SS} - hour, minute and second, with colon
 940      * <li>{@code +Hmmss} - hour, with minute if non-zero or with minute and
 941      * second if non-zero, no colon
 942      * <li>{@code +H:mm:ss} - hour, with minute if non-zero or with minute and
 943      * second if non-zero, with colon
 944      * </ul>
 945      * Patterns containing "HH" will format and parse a two digit hour,
 946      * zero-padded if necessary. Patterns containing "H" will format with no
 947      * zero-padding, and parse either one or two digits.
 948      * In lenient mode, the parser will be greedy and parse the maximum digits possible.
 949      * The "no offset" text controls what text is printed when the total amount of
 950      * the offset fields to be output is zero.
 951      * Example values would be 'Z', '+00:00', 'UTC' or 'GMT'.
 952      * Three formats are accepted for parsing UTC - the "no offset" text, and the
 953      * plus and minus versions of zero defined by the pattern.
 954      *
 955      * @param pattern  the pattern to use, not null
 956      * @param noOffsetText  the text to use when the offset is zero, not null
 957      * @return this, for chaining, not null
 958      * @throws IllegalArgumentException if the pattern is invalid
 959      */
 960     public DateTimeFormatterBuilder appendOffset(String pattern, String noOffsetText) {
 961         appendInternal(new OffsetIdPrinterParser(pattern, noOffsetText));
 962         return this;
 963     }
 964 
 965     /**
 966      * Appends the localized zone offset, such as 'GMT+01:00', to the formatter.
 967      * <p>
 968      * This appends a localized zone offset to the builder, the format of the


3476                 return ~position;
3477             }
3478             int successPos = pos;
3479             successPos = context.setParsedField(INSTANT_SECONDS, instantSecs, position, successPos);
3480             return context.setParsedField(NANO_OF_SECOND, nano, position, successPos);
3481         }
3482 
3483         @Override
3484         public String toString() {
3485             return "Instant()";
3486         }
3487     }
3488 
3489     //-----------------------------------------------------------------------
3490     /**
3491      * Prints or parses an offset ID.
3492      */
3493     static final class OffsetIdPrinterParser implements DateTimePrinterParser {
3494         static final String[] PATTERNS = new String[] {
3495                 "+HH", "+HHmm", "+HH:mm", "+HHMM", "+HH:MM", "+HHMMss", "+HH:MM:ss", "+HHMMSS", "+HH:MM:SS", "+HHmmss", "+HH:mm:ss",
3496                 "+H",  "+Hmm",  "+H:mm",  "+HMM",  "+H:MM",  "+HMMss",  "+H:MM:ss",  "+HMMSS",  "+H:MM:SS",  "+Hmmss",  "+H:mm:ss",
3497         };  // order used in pattern builder
3498         static final OffsetIdPrinterParser INSTANCE_ID_Z = new OffsetIdPrinterParser("+HH:MM:ss", "Z");
3499         static final OffsetIdPrinterParser INSTANCE_ID_ZERO = new OffsetIdPrinterParser("+HH:MM:ss", "0");
3500 
3501         private final String noOffsetText;
3502         private final int type;
3503         private final int style;
3504 
3505         /**
3506          * Constructor.
3507          *
3508          * @param pattern  the pattern
3509          * @param noOffsetText  the text to use for UTC, not null
3510          */
3511         OffsetIdPrinterParser(String pattern, String noOffsetText) {
3512             Objects.requireNonNull(pattern, "pattern");
3513             Objects.requireNonNull(noOffsetText, "noOffsetText");
3514             this.type = checkPattern(pattern);
3515             this.style = type % 11;
3516             this.noOffsetText = noOffsetText;
3517         }
3518 
3519         private int checkPattern(String pattern) {
3520             for (int i = 0; i < PATTERNS.length; i++) {
3521                 if (PATTERNS[i].equals(pattern)) {
3522                     return i;
3523                 }
3524             }
3525             throw new IllegalArgumentException("Invalid zone offset pattern: " + pattern);
3526         }
3527 
3528         private boolean isPaddedHour() {
3529             return type < 11;
3530         }
3531 
3532         private boolean isColon() {
3533             return style > 0 && (style % 2) == 0;
3534         }
3535 
3536         @Override
3537         public boolean format(DateTimePrintContext context, StringBuilder buf) {
3538             Long offsetSecs = context.getValue(OFFSET_SECONDS);
3539             if (offsetSecs == null) {
3540                 return false;
3541             }
3542             int totalSecs = Math.toIntExact(offsetSecs);
3543             if (totalSecs == 0) {
3544                 buf.append(noOffsetText);
3545             } else {
3546                 int absHours = Math.abs((totalSecs / 3600) % 100);  // anything larger than 99 silently dropped
3547                 int absMinutes = Math.abs((totalSecs / 60) % 60);
3548                 int absSeconds = Math.abs(totalSecs % 60);
3549                 int bufPos = buf.length();
3550                 int output = absHours;
3551                 buf.append(totalSecs < 0 ? "-" : "+");
3552                 if (isPaddedHour() || absHours >= 10) {
3553                     formatZeroPad(false, absHours, buf);
3554                 } else {
3555                     buf.append((char) (absHours + '0'));
3556                 }
3557                 if ((style >= 3 && style <= 8) || (style >= 9 && absSeconds > 0) || (style >= 1 && absMinutes > 0)) {
3558                     formatZeroPad(isColon(), absMinutes, buf);
3559                     output += absMinutes;
3560                     if (style == 7 || style == 8 || (style >= 5 && absSeconds > 0)) {
3561                         formatZeroPad(isColon(), absSeconds, buf);

3562                         output += absSeconds;
3563                     }
3564                 }
3565                 if (output == 0) {
3566                     buf.setLength(bufPos);
3567                     buf.append(noOffsetText);
3568                 }
3569             }
3570             return true;
3571         }
3572 
3573         private void formatZeroPad(boolean colon, int value, StringBuilder buf) {
3574             buf.append(colon ? ":" : "")
3575                     .append((char) (value / 10 + '0'))
3576                     .append((char) (value % 10 + '0'));
3577         }
3578 
3579         @Override
3580         public int parse(DateTimeParseContext context, CharSequence text, int position) {
3581             int length = text.length();
3582             int noOffsetLen = noOffsetText.length();









3583             if (noOffsetLen == 0) {
3584                 if (position == length) {
3585                     return context.setParsedField(OFFSET_SECONDS, 0, position, position);
3586                 }
3587             } else {
3588                 if (position == length) {
3589                     return ~position;
3590                 }
3591                 if (context.subSequenceEquals(text, position, noOffsetText, 0, noOffsetLen)) {
3592                     return context.setParsedField(OFFSET_SECONDS, 0, position, position + noOffsetLen);
3593                 }
3594             }
3595 
3596             // parse normal plus/minus offset
3597             char sign = text.charAt(position);  // IOOBE if invalid position
3598             if (sign == '+' || sign == '-') {
3599                 // starts
3600                 int negative = (sign == '-' ? -1 : 1);
3601                 boolean isColon = isColon();
3602                 boolean paddedHour = isPaddedHour();
3603                 int[] array = new int[4];
3604                 array[0] = position + 1;
3605                 int parseType = type;
3606                 // select parse type when lenient
3607                 if (!context.isStrict()) {
3608                     if (paddedHour) {
3609                         if (isColon || (parseType == 0 && length > position + 3 && text.charAt(position + 3) == ':')) {
3610                             isColon = true; // needed in cases like ("+HH", "+01:01")
3611                             parseType = 10;
3612                         } else {
3613                             parseType = 9;
3614                         }
3615                     } else {
3616                         if (isColon || (parseType == 11 && length > position + 3 && (text.charAt(position + 2) == ':' || text.charAt(position + 3) == ':'))) {
3617                             isColon = true;
3618                             parseType = 21;  // needed in cases like ("+H", "+1:01")
3619                         } else {
3620                             parseType = 20;
3621                         }
3622                     }
3623                 }
3624                 // parse according to the selected pattern
3625                 switch (parseType) {
3626                     case 0: // +HH
3627                     case 11: // +H
3628                         parseHour(text, paddedHour, array);
3629                         break;
3630                     case 1: // +HHmm
3631                     case 2: // +HH:mm
3632                     case 13: // +H:mm
3633                         parseHour(text, paddedHour, array);
3634                         parseMinute(text, isColon, false, array);
3635                         break;
3636                     case 3: // +HHMM
3637                     case 4: // +HH:MM
3638                     case 15: // +H:MM
3639                         parseHour(text, paddedHour, array);
3640                         parseMinute(text, isColon, true, array);
3641                         break;
3642                     case 5: // +HHMMss
3643                     case 6: // +HH:MM:ss
3644                     case 17: // +H:MM:ss
3645                         parseHour(text, paddedHour, array);
3646                         parseMinute(text, isColon, true, array);
3647                         parseSecond(text, isColon, false, array);
3648                         break;
3649                     case 7: // +HHMMSS
3650                     case 8: // +HH:MM:SS
3651                     case 19: // +H:MM:SS
3652                         parseHour(text, paddedHour, array);
3653                         parseMinute(text, isColon, true, array);
3654                         parseSecond(text, isColon, true, array);
3655                         break;
3656                     case 9: // +HHmmss
3657                     case 10: // +HH:mm:ss
3658                     case 21: // +H:mm:ss
3659                         parseHour(text, paddedHour, array);
3660                         parseOptionalMinuteSecond(text, isColon, array);
3661                         break;
3662                     case 12: // +Hmm
3663                         parseVariableWidthDigits(text, 1, 4, array);
3664                         break;
3665                     case 14: // +HMM
3666                         parseVariableWidthDigits(text, 3, 4, array);
3667                         break;
3668                     case 16: // +HMMss
3669                         parseVariableWidthDigits(text, 3, 6, array);
3670                         break;
3671                     case 18: // +HMMSS
3672                         parseVariableWidthDigits(text, 5, 6, array);
3673                         break;
3674                     case 20: // +Hmmss
3675                         parseVariableWidthDigits(text, 1, 6, array);
3676                         break;
3677                 }
3678                 if (array[0] > 0) {
3679                     if (array[1] > 23 || array[2] > 59 || array[3] > 59) {
3680                         throw new DateTimeException("Value out of range: Hour[0-23], Minute[0-59], Second[0-59]");
3681                     }
3682                     long offsetSecs = negative * (array[1] * 3600L + array[2] * 60L + array[3]);
3683                     return context.setParsedField(OFFSET_SECONDS, offsetSecs, position, array[0]);
3684                 }
3685             }
3686             // handle special case of empty no offset text
3687             if (noOffsetLen == 0) {
3688                 return context.setParsedField(OFFSET_SECONDS, 0, position, position);
3689             }
3690             return ~position;
3691         }
3692 
3693         private void parseHour(CharSequence parseText, boolean paddedHour, int[] array) {
3694             if (paddedHour) {
3695                 // parse two digits
3696                 if (!parseDigits(parseText, false, 1, array)) {
3697                     array[0] = ~array[0];
3698                 }
3699             } else {
3700                 // parse one or two digits
3701                 parseVariableWidthDigits(parseText, 1, 2, array);
3702             }
3703         }
3704 
3705         private void parseMinute(CharSequence parseText, boolean isColon, boolean mandatory, int[] array) {
3706             if (!parseDigits(parseText, isColon, 2, array)) {
3707                 if (mandatory) {
3708                     array[0] = ~array[0];
3709                 }
3710             }
3711         }
3712 
3713         private void parseSecond(CharSequence parseText, boolean isColon, boolean mandatory, int[] array) {
3714             if (!parseDigits(parseText, isColon, 3, array)) {
3715                 if (mandatory) {
3716                     array[0] = ~array[0];
3717                 }
3718             }
3719         }
3720 
3721         private void parseOptionalMinuteSecond(CharSequence parseText, boolean isColon, int[] array) {
3722             if (parseDigits(parseText, isColon, 2, array)) {
3723                 parseDigits(parseText, isColon, 3, array);
3724             }
3725         }
3726 
3727         private boolean parseDigits(CharSequence parseText, boolean isColon, int arrayIndex, int[] array) {
3728             int pos = array[0];
3729             if (pos < 0) {
3730                 return true;
3731             }
3732             if (isColon && arrayIndex != 1) { //  ':' will precede only in case of minute/second
3733                 if (pos + 1 > parseText.length() || parseText.charAt(pos) != ':') {
3734                     return false;
3735                 }
3736                 pos++;
3737             }
3738             if (pos + 2 > parseText.length()) {
3739                 return false;
3740             }
3741             char ch1 = parseText.charAt(pos++);
3742             char ch2 = parseText.charAt(pos++);
3743             if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') {
3744                 return false;
3745             }
3746             int value = (ch1 - 48) * 10 + (ch2 - 48);
3747             if (value < 0 || value > 59) {
3748                 return false;
3749             }
3750             array[arrayIndex] = value;
3751             array[0] = pos;
3752             return true;
3753         }
3754 
3755         private void parseVariableWidthDigits(CharSequence parseText, int minDigits, int maxDigits, int[] array) {
3756             // scan the text to find the available number of digits up to maxDigits
3757             // so long as the number available is minDigits or more, the input is valid
3758             // then parse the number of available digits
3759             int pos = array[0];
3760             int available = 0;
3761             char[] chars = new char[maxDigits];
3762             for (int i = 0; i < maxDigits; i++) {
3763                 if (pos + 1  > parseText.length()) {
3764                     break;
3765                 }
3766                 char ch = parseText.charAt(pos++);
3767                 if (ch < '0' || ch > '9') {
3768                     pos--;
3769                     break;
3770                 }
3771                 chars[i] = ch;
3772                 available++;
3773             }
3774             if (available < minDigits) {
3775                 array[0] = ~array[0];
3776                 return;
3777             }
3778             switch (available) {
3779                 case 1:
3780                     array[1] = (chars[0] - 48);
3781                     break;
3782                 case 2:
3783                     array[1] = ((chars[0] - 48) * 10 + (chars[1] - 48));
3784                     break;
3785                 case 3:
3786                     array[1] = (chars[0] - 48);
3787                     array[2] = ((chars[1] - 48) * 10 + (chars[2] - 48));
3788                     break;
3789                 case 4:
3790                     array[1] = ((chars[0] - 48) * 10 + (chars[1] - 48));
3791                     array[2] = ((chars[2] - 48) * 10 + (chars[3] - 48));
3792                     break;
3793                 case 5:
3794                     array[1] = (chars[0] - 48);
3795                     array[2] = ((chars[1] - 48) * 10 + (chars[2] - 48));
3796                     array[3] = ((chars[3] - 48) * 10 + (chars[4] - 48));
3797                     break;
3798                 case 6:
3799                     array[1] = ((chars[0] - 48) * 10 + (chars[1] - 48));
3800                     array[2] = ((chars[2] - 48) * 10 + (chars[3] - 48));
3801                     array[3] = ((chars[4] - 48) * 10 + (chars[5] - 48));
3802                     break;
3803             }
3804             array[0] = pos;
3805         }
3806 
3807         @Override
3808         public String toString() {
3809             String converted = noOffsetText.replace("'", "''");
3810             return "Offset(" + PATTERNS[type] + ",'" + converted + "')";
3811         }
3812     }
3813 
3814     //-----------------------------------------------------------------------
3815     /**
3816      * Prints or parses an offset ID.
3817      */
3818     static final class LocalizedOffsetIdPrinterParser implements DateTimePrinterParser {
3819         private final TextStyle style;
3820 
3821         /**
3822          * Constructor.
3823          *
3824          * @param style  the style, not null


< prev index next >