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 |