public void add()

in jena-core/src/main/java/org/apache/jena/ext/xerces/jaxp/datatype/XMLGregorianCalendarImpl.java [1864:2111]


    public void add(Duration duration) {

        /*
         * Extracted from
         * http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
         * to ensure implemented properly. See spec for definitions of methods
         * used in algorithm.
         *
         * Given a dateTime S and a duration D, specifies how to compute a
         * dateTime E where E is the end of the time period with start S and
         * duration D i.e. E = S + D.
         *
         * The following is the precise specification.
         * These steps must be followed in the same order.
         * If a field in D is not specified, it is treated as if it were zero.
         * If a field in S is not specified, it is treated in the calculation
         * as if it were the minimum allowed value in that field, however,
         * after the calculation is concluded, the corresponding field in
         * E is removed (set to unspecified).
         *
         * Months (may be modified additionally below)
         *  temp := S[month] + D[month]
         *  E[month] := modulo(temp, 1, 13)
         *  carry := fQuotient(temp, 1, 13)
         */

        boolean fieldUndefined[] = {
                false,
                false,
                false,
                false,
                false,
                false
        };

        int signum = duration.getSign();

        int startMonth = getMonth();
        if (startMonth == DatatypeConstants.FIELD_UNDEFINED) {
            startMonth = MIN_FIELD_VALUE[MONTH];
            fieldUndefined[MONTH] = true;
        }

        BigInteger dMonths = sanitize(duration.getField(DatatypeConstants.MONTHS), signum);
        BigInteger temp = BigInteger.valueOf(startMonth).add(dMonths);
        setMonth(temp.subtract(BigInteger.ONE).mod(TWELVE).intValue() + 1);
        BigInteger carry =
            new BigDecimal(temp.subtract(BigInteger.ONE)).divide(new BigDecimal(TWELVE), RoundingMode.FLOOR).toBigInteger();

        /* Years (may be modified additionally below)
         *  E[year] := S[year] + D[year] + carry
         */
        BigInteger startYear = getEonAndYear();
        if (startYear == null) {
            fieldUndefined[YEAR] = true;
            startYear = BigInteger.ZERO;
        }
        BigInteger dYears = sanitize(duration.getField(DatatypeConstants.YEARS), signum);
        BigInteger endYear = startYear.add(dYears).add(carry);
        setYear(endYear);

        /* Zone
         *  E[zone] := S[zone]
         *
         * no-op since adding to this, not to a new end point.
         */

        /* Seconds
         *  temp := S[second] + D[second]
         *  E[second] := modulo(temp, 60)
         *  carry := fQuotient(temp, 60)
         */
        BigDecimal startSeconds;
        if (getSecond() == DatatypeConstants.FIELD_UNDEFINED) {
            fieldUndefined[SECOND] = true;
            startSeconds = DECIMAL_ZERO;
        }
        else {
            // seconds + fractionalSeconds
            startSeconds = getSeconds();
        }

        // Duration seconds is SECONDS + FRACTIONALSECONDS.
        BigDecimal dSeconds = DurationImpl.sanitize((BigDecimal) duration.getField(DatatypeConstants.SECONDS), signum);
        BigDecimal tempBD = startSeconds.add(dSeconds);
        BigDecimal fQuotient =
            new BigDecimal(new BigDecimal(tempBD.toBigInteger()).divide(DECIMAL_SIXTY, RoundingMode.FLOOR).toBigInteger());
        BigDecimal endSeconds = tempBD.subtract(fQuotient.multiply(DECIMAL_SIXTY));

        carry = fQuotient.toBigInteger();
        setSecond(endSeconds.intValue());
        BigDecimal tempFracSeconds = endSeconds.subtract(new BigDecimal(BigInteger.valueOf(getSecond())));
        if (tempFracSeconds.compareTo(DECIMAL_ZERO) < 0) {
            setFractionalSecond(DECIMAL_ONE.add(tempFracSeconds));
            if (getSecond() == 0) {
                setSecond(59);
                carry = carry.subtract(BigInteger.ONE);
            }
            else {
                setSecond(getSecond() - 1);
            }
        }
        else {
            setFractionalSecond(tempFracSeconds);
        }

        /* Minutes
         *  temp := S[minute] + D[minute] + carry
         *  E[minute] := modulo(temp, 60)
         *  carry := fQuotient(temp, 60)
         */
        int startMinutes = getMinute();
        if (startMinutes == DatatypeConstants.FIELD_UNDEFINED) {
            fieldUndefined[MINUTE] = true;
            startMinutes = MIN_FIELD_VALUE[MINUTE];
        }
        BigInteger dMinutes = sanitize(duration.getField(DatatypeConstants.MINUTES), signum);

        temp = BigInteger.valueOf(startMinutes).add(dMinutes).add(carry);
        setMinute(temp.mod(SIXTY).intValue());
        carry = new BigDecimal(temp).divide(DECIMAL_SIXTY, RoundingMode.FLOOR).toBigInteger();

        /* Hours
         *  temp := S[hour] + D[hour] + carry
         *  E[hour] := modulo(temp, 24)
         *  carry := fQuotient(temp, 24)
         */
        int startHours = getHour();
        if (startHours == DatatypeConstants.FIELD_UNDEFINED) {
            fieldUndefined[HOUR] = true;
            startHours = MIN_FIELD_VALUE[HOUR];
        }
        BigInteger dHours = sanitize(duration.getField(DatatypeConstants.HOURS), signum);

        temp = BigInteger.valueOf(startHours).add(dHours).add(carry);
        setHour(temp.mod(TWENTY_FOUR).intValue());
        carry = new BigDecimal(temp).divide(new BigDecimal(TWENTY_FOUR), RoundingMode.FLOOR).toBigInteger();

        /* Days
         *  if S[day] > maximumDayInMonthFor(E[year], E[month])
         *       + tempDays := maximumDayInMonthFor(E[year], E[month])
         *  else if S[day] < 1
         *       + tempDays := 1
         *  else
         *       + tempDays := S[day]
         *  E[day] := tempDays + D[day] + carry
         *  START LOOP
         *       + IF E[day] < 1
         *             # E[day] := E[day] +
         *                 maximumDayInMonthFor(E[year], E[month] - 1)
         *             # carry := -1
         *       + ELSE IF E[day] > maximumDayInMonthFor(E[year], E[month])
         *             # E[day] :=
         *                    E[day] - maximumDayInMonthFor(E[year], E[month])
         *             # carry := 1
         *       + ELSE EXIT LOOP
         *       + temp := E[month] + carry
         *       + E[month] := modulo(temp, 1, 13)
         *       + E[year] := E[year] + fQuotient(temp, 1, 13)
         *       + GOTO START LOOP
         */
        BigInteger tempDays;
        int startDay = getDay();
        if (startDay == DatatypeConstants.FIELD_UNDEFINED) {
            fieldUndefined[DAY] = true;
            startDay = MIN_FIELD_VALUE[DAY];
        }
        BigInteger dDays = sanitize(duration.getField(DatatypeConstants.DAYS), signum);
        int maxDayInMonth = maximumDayInMonthFor(getEonAndYear(), getMonth());
        if (startDay > maxDayInMonth) {
            tempDays =  BigInteger.valueOf(maxDayInMonth);
        }
        else if (startDay < 1) {
            tempDays = BigInteger.ONE;
        }
        else {
            tempDays = BigInteger.valueOf(startDay);
        }
        BigInteger endDays = tempDays.add(dDays).add(carry);
        int monthCarry;
        int intTemp;
        while (true) {
            if (endDays.compareTo(BigInteger.ONE) < 0) {
                // calculate days in previous month, watch for month roll over
                BigInteger mdimf = null;
                if (month >= 2) {
                    mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth() - 1));
                }
                else {
                    // roll over to December of previous year
                    mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear().subtract(BigInteger.valueOf(1)), 12));
                }
                endDays = endDays.add(mdimf);
                monthCarry = -1;
            }
            else if (endDays.compareTo(BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(), getMonth()))) > 0) {
                endDays = endDays.add(BigInteger.valueOf(-maximumDayInMonthFor(getEonAndYear(), getMonth())));
                monthCarry = 1;
            }
            else {
                break;
            }

            intTemp = getMonth() + monthCarry;
            int endMonth = (intTemp - 1) % (13 - 1);
            int quotient;
            if (endMonth < 0) {
                endMonth = (13 - 1) + endMonth + 1;
                quotient = BigDecimal.valueOf(intTemp - 1).divide(new BigDecimal(TWELVE), RoundingMode.UP).intValue();
            }
            else {
                quotient = (intTemp - 1) / (13 - 1);
                endMonth += 1;
            }
            setMonth(endMonth);
            if (quotient != 0)  {
                setYear(getEonAndYear().add(BigInteger.valueOf(quotient)));
            }
        }
        setDay(endDays.intValue());

        // set fields that where undefined before this addition, back to undefined.
        for (int i = YEAR; i <= SECOND; i++) {
            if (fieldUndefined[i]) {
                switch (i) {
                    case YEAR:
                        setYear(DatatypeConstants.FIELD_UNDEFINED);
                        break;
                    case MONTH:
                        setMonth(DatatypeConstants.FIELD_UNDEFINED);
                        break;
                    case DAY:
                        setDay(DatatypeConstants.FIELD_UNDEFINED);
                        break;
                    case HOUR:
                        setHour(DatatypeConstants.FIELD_UNDEFINED);
                        break;
                    case MINUTE:
                        setMinute(DatatypeConstants.FIELD_UNDEFINED);
                        break;
                    case SECOND:
                        setSecond(DatatypeConstants.FIELD_UNDEFINED);
                        setFractionalSecond(null);
                        break;
                }
            }
        }
    }