public GDate()

in src/main/java/org/apache/xmlbeans/GDate.java [74:313]


    public GDate(CharSequence string) {
        // first trim XML whitespace
        int len = string.length();
        int start = 0;
        while (len > 0 && isSpace(string.charAt(len - 1))) {
            len -= 1;
        }
        while (start < len && isSpace(string.charAt(start))) {
            start += 1;
        }

        // pick optional timezone off the end
        if (len - start >= 1 && string.charAt(len - 1) == 'Z') {
            _bits |= HAS_TIMEZONE;
            len -= 1;
        } else if (len - start >= 6) {
            timezone:
            {
                int tzsign;
                int tzhour;
                int tzminute;

                if (string.charAt(len - 3) != ':') {
                    break timezone;
                }

                switch (string.charAt(len - 6)) {
                    case '-':
                        tzsign = -1;
                        break;
                    case '+':
                        tzsign = 1;
                        break;
                    default:
                        break timezone;
                }

                tzhour = twoDigit(string, len - 5);
                tzminute = twoDigit(string, len - 2);
                if (tzhour > 14) {
                    throw new IllegalArgumentException("time zone hour must be two digits between -14 and +14");
                }
                if (tzminute > 59) {
                    throw new IllegalArgumentException("time zone minute must be two digits between 00 and 59");
                }
                _bits |= HAS_TIMEZONE;
                _tzsign = tzsign;
                _tzh = tzhour;
                _tzm = tzminute;
                len -= 6;
            }
        }

        // pick date fields off the beginning if it doesn't look like a time
        if (start < len && (start + 2 >= len || string.charAt(start + 2) != ':')) {
            scandate:
            {
                // parse year sign
                boolean negyear = false;
                if (string.charAt(start) == '-') {
                    negyear = true;
                    start += 1;
                }

                // scan year digits
                int value = 0;
                int digits = -start;
                char ch;
                boolean startsWithZero = start < len && digitVal(string.charAt(start)) == 0;

                for (; ; ) {
                    ch = start < len ? string.charAt(start) : '\0';
                    if (!isDigit(ch)) {
                        break;
                    }

                    if (startsWithZero && start + digits >= 4) {
                        throw new IllegalArgumentException("year value starting with zero must be 4 or less digits: " + string);
                    }

                    value = value * 10 + digitVal(ch);
                    start += 1;
                }
                digits += start;
                if (digits > 9) {
                    throw new IllegalArgumentException("year too long (up to 9 digits)");
                } else if (digits >= 4) {
                    _bits |= HAS_YEAR;
                    _CY = negyear ? -value : value;
                    if (_CY == 0) {
                        throw new IllegalArgumentException("year must not be zero");
                    }
                } else if (digits > 0) {
                    throw new IllegalArgumentException("year must be four digits (may pad with zeroes, e.g., 0560)");
                }

                if (_CY > MAX_YEAR) {
                    throw new IllegalArgumentException("year value not supported: too big, must be less than " + MAX_YEAR);
                }

                if (_CY < MIN_YEAR) {
                    throw new IllegalArgumentException("year values not supported: too small, must be bigger than " + MIN_YEAR);
                }

                // hyphen introduces a month
                if (ch != '-') {
                    if (negyear && !hasYear()) {
                        throw new IllegalArgumentException(); // a single minus
                    } else {
                        break scandate;
                    }
                }
                start += 1;

                // two-digit month
                if (len - start >= 2) {
                    value = twoDigit(string, start);
                    if (value >= 1 && value <= 12) {
                        _bits |= HAS_MONTH;
                        _M = value;
                        start += 2;
                    }
                }

                // hyphen introduces a day
                ch = start < len ? string.charAt(start) : '\0';
                if (ch != '-') {
                    if (!hasMonth()) {
                        throw new IllegalArgumentException(); // minus after a year
                    } else {
                        break scandate;
                    }
                }
                start += 1;

                // two-digit day
                if (len - start >= 2) {
                    value = twoDigit(string, start);
                    if (value >= 1 && value <= 31) {
                        _bits |= HAS_DAY;
                        _D = value;
                        start += 2;
                    }
                }

                if (!hasDay()) {
                    // error in the original schema spec permits an extra '-' here
                    if (hasMonth() && !hasYear()) {
                        ch = start < len ? string.charAt(start) : '\0';
                        if (ch == '-') {
                            start += 1;
                            break scandate;
                        }
                    }
                    throw new IllegalArgumentException(); // minus after a month
                }
            }
        }

        // time
        if (start < len) {
            if (hasYear() || hasMonth() || hasDay()) {
                if (string.charAt(start) != 'T') {
                    throw new IllegalArgumentException("date and time must be separated by 'T'");
                }
                start += 1;
            }

            if (len < start + 8 || string.charAt(start + 2) != ':' || string.charAt(start + 5) != ':') {
                throw new IllegalArgumentException();
            }

            int h = twoDigit(string, start);
            if (h > 24) {
                throw new IllegalArgumentException("hour must be between 00 and 23");
            }
            int m = twoDigit(string, start + 3);
            if (m >= 60) {
                throw new IllegalArgumentException("minute must be between 00 and 59");
            }
            int s = twoDigit(string, start + 6);
            if (s >= 60) {
                throw new IllegalArgumentException("second must be between 00 and 59");
            }

            start += 8;

            BigDecimal fs = _zero;
            if (start < len) {
                if (string.charAt(start) != '.') {
                    throw new IllegalArgumentException();
                }
                if (start + 1 < len) {
                    for (int i = start + 1; i < len; i++) {
                        if (!isDigit(string.charAt(i))) {
                            throw new IllegalArgumentException();
                        }
                    }
                    try {
                        fs = new BigDecimal(string.subSequence(start, len).toString());
                    } catch (Throwable e) {
                        if (ExceptionUtil.isFatal(e)) {
                            ExceptionUtil.rethrow(e);
                        }
                        throw new IllegalArgumentException();
                    }
                }
            }

            _bits |= HAS_TIME;
            _h = h;
            _m = m;
            _s = s;
            _fs = fs;
        }

        if (hasTime() && _h == 24) {
            if (_m != 0 || _s != 0 || _fs.compareTo(_zero) != 0) {
                throw new IllegalArgumentException("if hour is 24, minutes, seconds and fraction must be 0");
            } else {   // normalize to next day if it has date or at least has day
                if (hasDate()) {
                    GDateBuilder gdb = new GDateBuilder(_CY, _M, _D, _h, _m, _s, _fs, _tzsign, _tzh, _tzm);
                    gdb.normalize24h();

                    _D = gdb.getDay();
                    _M = gdb.getMonth();
                    _CY = gdb.getYear();
                    _h = 0;
                } else if (hasDay()) // if no date only days increment
                {
                    _D++;
                    _h = 0;
                }
            }
        }

        if (!isValid()) {
            throw new IllegalArgumentException("invalid date");
        }
    }