/**
   * Parses time hh:mm:ss.sss and time zone if any
   *
   * @param start
   * @param end
   * @param data
   * @exception RuntimeException
   */
  protected void getTime(String buffer, int start, int end, DateTimeData data)
      throws RuntimeException {

    int stop = start + 2;

    // get hours (hh)
    data.hour = parseInt(buffer, start, stop);

    // get minutes (mm)

    if (buffer.charAt(stop++) != ':') {
      throw new RuntimeException("Error in parsing time zone");
    }
    start = stop;
    stop = stop + 2;
    data.minute = parseInt(buffer, start, stop);

    // get seconds (ss)
    if (buffer.charAt(stop++) != ':') {
      throw new RuntimeException("Error in parsing time zone");
    }

    // find UTC sign if any
    int sign = findUTCSign(buffer, start, end);

    // get seconds (ms)
    start = stop;
    stop = sign < 0 ? end : sign;
    data.second = parseSecond(buffer, start, stop);

    // parse UTC time zone (hh:mm)
    if (sign > 0) {
      getTimeZone(buffer, data, sign, end);
    }
  }
 private void cloneDate(DateTimeData finalValue, DateTimeData tempDate) {
   tempDate.year = finalValue.year;
   tempDate.month = finalValue.month;
   tempDate.day = finalValue.day;
   tempDate.hour = finalValue.hour;
   tempDate.minute = finalValue.minute;
   tempDate.second = finalValue.second;
   tempDate.utc = finalValue.utc;
   tempDate.timezoneHr = finalValue.timezoneHr;
   tempDate.timezoneMin = finalValue.timezoneMin;
 }
 /**
  * Resets object representation of date/time
  *
  * @param data date/time object
  */
 protected void resetDateObj(DateTimeData data) {
   data.year = 0;
   data.month = 0;
   data.day = 0;
   data.hour = 0;
   data.minute = 0;
   data.second = 0;
   data.utc = 0;
   data.timezoneHr = 0;
   data.timezoneMin = 0;
 }
  private DateTimeData addDuration(DateTimeData date, DateTimeData addto, DateTimeData duration) {

    // REVISIT: some code could be shared between normalize() and this method,
    //         however is it worth moving it? The structures are different...
    //

    resetDateObj(duration);
    // add months (may be modified additionaly below)
    int temp = addto.month + date.month;
    duration.month = modulo(temp, 1, 13);
    int carry = fQuotient(temp, 1, 13);

    // add years (may be modified additionaly below)
    duration.year = addto.year + date.year + carry;

    // add seconds
    double dtemp = addto.second + date.second;
    carry = (int) Math.floor(dtemp / 60);
    duration.second = dtemp - carry * 60;

    // add minutes
    temp = addto.minute + date.minute + carry;
    carry = fQuotient(temp, 60);
    duration.minute = mod(temp, 60, carry);

    // add hours
    temp = addto.hour + date.hour + carry;
    carry = fQuotient(temp, 24);
    duration.hour = mod(temp, 24, carry);

    duration.day = addto.day + date.day + carry;

    while (true) {

      temp = maxDayInMonthFor(duration.year, duration.month);
      if (duration.day < 1) { // original duration was negative
        duration.day = duration.day + maxDayInMonthFor(duration.year, duration.month - 1);
        carry = -1;
      } else if (duration.day > temp) {
        duration.day = duration.day - temp;
        carry = 1;
      } else {
        break;
      }
      temp = duration.month + carry;
      duration.month = modulo(temp, 1, 13);
      duration.year = duration.year + fQuotient(temp, 1, 13);
    }

    duration.utc = 'Z';
    return duration;
  }
  /**
   * Parses, validates and computes normalized version of duration object
   *
   * @param str The lexical representation of duration object PnYn MnDTnH nMnS
   * @param durationType
   * @return normalized date representation
   * @exception SchemaDateTimeException Invalid lexical representation
   */
  protected DateTimeData parse(String str, int durationType) throws SchemaDateTimeException {
    int len = str.length();
    DateTimeData date = new DateTimeData(str, this);

    int start = 0;
    char c = str.charAt(start++);
    if (c != 'P' && c != '-') {
      throw new SchemaDateTimeException();
    } else {
      date.utc = (c == '-') ? '-' : 0;
      if (c == '-' && str.charAt(start++) != 'P') {
        throw new SchemaDateTimeException();
      }
    }

    int negate = 1;
    // negative duration
    if (date.utc == '-') {
      negate = -1;
    }
    // at least one number and designator must be seen after P
    boolean designator = false;

    int endDate = indexOf(str, start, len, 'T');
    if (endDate == -1) {
      endDate = len;
    } else if (durationType == YEARMONTHDURATION_TYPE) {
      throw new SchemaDateTimeException();
    }

    // find 'Y'
    int end = indexOf(str, start, endDate, 'Y');
    if (end != -1) {

      if (durationType == DAYTIMEDURATION_TYPE) {
        throw new SchemaDateTimeException();
      }

      // scan year
      date.year = negate * parseInt(str, start, end);
      start = end + 1;
      designator = true;
    }

    end = indexOf(str, start, endDate, 'M');
    if (end != -1) {

      if (durationType == DAYTIMEDURATION_TYPE) {
        throw new SchemaDateTimeException();
      }

      // scan month
      date.month = negate * parseInt(str, start, end);
      start = end + 1;
      designator = true;
    }

    end = indexOf(str, start, endDate, 'D');
    if (end != -1) {

      if (durationType == YEARMONTHDURATION_TYPE) {
        throw new SchemaDateTimeException();
      }

      // scan day
      date.day = negate * parseInt(str, start, end);
      start = end + 1;
      designator = true;
    }

    if (len == endDate && start != len) {
      throw new SchemaDateTimeException();
    }
    if (len != endDate) {

      // scan hours, minutes, seconds
      // REVISIT: can any item include a decimal fraction or only seconds?
      //

      end = indexOf(str, ++start, len, 'H');
      if (end != -1) {
        // scan hours
        date.hour = negate * parseInt(str, start, end);
        start = end + 1;
        designator = true;
      }

      end = indexOf(str, start, len, 'M');
      if (end != -1) {
        // scan min
        date.minute = negate * parseInt(str, start, end);
        start = end + 1;
        designator = true;
      }

      end = indexOf(str, start, len, 'S');
      if (end != -1) {
        // scan seconds
        date.second = negate * parseSecond(str, start, end);
        start = end + 1;
        designator = true;
      }
      // no additional data shouls appear after last item
      // P1Y1M1DT is illigal value as well
      if (start != len || str.charAt(--start) == 'T') {
        throw new SchemaDateTimeException();
      }
    }

    if (!designator) {
      throw new SchemaDateTimeException();
    }

    return date;
  }