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; }
/** * If timezone present - normalize dateTime [E Adding durations to dateTimes] * * @param date CCYY-MM-DDThh:mm:ss+03 */ protected void normalize(DateTimeData date) { // REVISIT: we have common code in addDuration() for durations // should consider reorganizing it. // // add minutes (from time zone) int negate = -1; if (DEBUG) { System.out.println("==>date.minute" + date.minute); System.out.println("==>date.timezoneMin" + date.timezoneMin); } int temp = date.minute + negate * date.timezoneMin; int carry = fQuotient(temp, 60); date.minute = mod(temp, 60, carry); if (DEBUG) { System.out.println("==>carry: " + carry); } // add hours temp = date.hour + negate * date.timezoneHr + carry; carry = fQuotient(temp, 24); date.hour = mod(temp, 24, carry); if (DEBUG) { System.out.println("==>date.hour" + date.hour); System.out.println("==>carry: " + carry); } date.day = date.day + carry; while (true) { temp = maxDayInMonthFor(date.year, date.month); if (date.day < 1) { date.day = date.day + maxDayInMonthFor(date.year, date.month - 1); carry = -1; } else if (date.day > temp) { date.day = date.day - temp; carry = 1; } else { break; } temp = date.month + carry; date.month = modulo(temp, 1, 13); date.year = date.year + fQuotient(temp, 1, 13); if (date.year == 0 && !Constants.SCHEMA_1_1_SUPPORT) { date.year = (date.timezoneHr < 0 || date.timezoneMin < 0) ? 1 : -1; } } date.utc = 'Z'; }
/** * Parses date CCYY-MM * * @param buffer * @param start start position * @param end end position * @param date * @exception RuntimeException */ protected int getYearMonth(String buffer, int start, int end, DateTimeData date) throws RuntimeException { if (buffer.charAt(0) == '-') { // REVISIT: date starts with preceding '-' sign // do we have to do anything with it? // start++; } int i = indexOf(buffer, start, end, '-'); if (i == -1) throw new RuntimeException("Year separator is missing or misplaced"); int length = i - start; if (length < 4) { throw new RuntimeException("Year must have 'CCYY' format"); } else if (length > 4 && buffer.charAt(start) == '0') { throw new RuntimeException( "Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden"); } date.year = parseIntYear(buffer, i); if (buffer.charAt(i) != '-') { throw new RuntimeException("CCYY must be followed by '-' sign"); } start = ++i; i = start + 2; date.month = parseInt(buffer, start, i); return i; // fStart points right after the MONTH }
/** * Validates given date/time object accoring to W3C PR Schema [D.1 ISO 8601 Conventions] * * @param data */ protected void validateDateTime(DateTimeData data) { // REVISIT: should we throw an exception for not valid dates // or reporting an error message should be sufficient? /** XML Schema 1.1 - RQ-123: Allow year 0000 in date related types. */ if (!Constants.SCHEMA_1_1_SUPPORT && data.year == 0) { throw new RuntimeException("The year \"0000\" is an illegal year value"); } if (data.month < 1 || data.month > 12) { throw new RuntimeException("The month must have values 1 to 12"); } // validate days if (data.day > maxDayInMonthFor(data.year, data.month) || data.day < 1) { throw new RuntimeException("The day must have values 1 to 31"); } // validate hours if (data.hour > 23 || data.hour < 0) { if (data.hour == 24 && data.minute == 0 && data.second == 0) { data.hour = 0; if (++data.day > maxDayInMonthFor(data.year, data.month)) { data.day = 1; if (++data.month > 12) { data.month = 1; if (Constants.SCHEMA_1_1_SUPPORT) { ++data.year; } else if (++data.year == 0) { data.year = 1; } } } } else { throw new RuntimeException("Hour must have values 0-23, unless 24:00:00"); } } // validate if (data.minute > 59 || data.minute < 0) { throw new RuntimeException("Minute must have values 0-59"); } // validate if (data.second >= 60 || data.second < 0) { throw new RuntimeException("Second must have values 0-59"); } // validate if (data.timezoneHr > 14 || data.timezoneHr < -14) { throw new RuntimeException("Time zone should have range -14:00 to +14:00"); } else { if ((data.timezoneHr == 14 || data.timezoneHr == -14) && data.timezoneMin != 0) throw new RuntimeException("Time zone should have range -14:00 to +14:00"); else if (data.timezoneMin > 59 || data.timezoneMin < -59) throw new RuntimeException("Minute must have values 0-59"); } }
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; }
/** * 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; }