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;
}
}
}
}