/** * Factory method: create a dateTime value given a date and a time. * * @param date the date * @param time the time * @return the dateTime with the given components. If either component is null, returns null * @throws XPathException if the timezones are both present and inconsistent */ public static DateTimeValue makeDateTimeValue(DateValue date, TimeValue time) throws XPathException { if (date == null || time == null) { return null; } DayTimeDurationValue tz1 = (DayTimeDurationValue) date.getComponent(Component.TIMEZONE); DayTimeDurationValue tz2 = (DayTimeDurationValue) time.getComponent(Component.TIMEZONE); boolean zoneSpecified = (tz1 != null || tz2 != null); if (tz1 != null && tz2 != null && !tz1.equals(tz2)) { XPathException err = new XPathException("Supplied date and time are in different timezones"); err.setErrorCode("FORG0008"); throw err; } DateTimeValue v = new DateTimeValue(); v.year = (int) ((Int64Value) date.getComponent(Component.YEAR_ALLOWING_ZERO)).longValue(); v.month = (byte) ((Int64Value) date.getComponent(Component.MONTH)).longValue(); v.day = (byte) ((Int64Value) date.getComponent(Component.DAY)).longValue(); v.hour = (byte) ((Int64Value) time.getComponent(Component.HOURS)).longValue(); v.minute = (byte) ((Int64Value) time.getComponent(Component.MINUTES)).longValue(); final BigDecimal secs = ((DecimalValue) time.getComponent(Component.SECONDS)).getDecimalValue(); v.second = (byte) secs.intValue(); v.microsecond = secs.multiply(BigDecimal.valueOf(1000000)).intValue() % 1000000; if (zoneSpecified) { if (tz1 == null) { tz1 = tz2; } v.setTimezoneInMinutes((int) (tz1.getLengthInMicroseconds() / 60000000)); } v.typeLabel = BuiltInAtomicType.DATE_TIME; return v; }
/** * Return a new dateTime with the same normalized value, but in a different timezone. * * @param timezone the new timezone offset, in minutes * @return the date/time in the new timezone. This will be a new DateTimeValue unless no change * was required to the original value */ public CalendarValue adjustTimezone(int timezone) { if (!hasTimezone()) { CalendarValue in = (CalendarValue) copyAsSubType(typeLabel); in.setTimezoneInMinutes(timezone); return in; } int oldtz = getTimezoneInMinutes(); if (oldtz == timezone) { return this; } int tz = timezone - oldtz; int h = hour; int mi = minute; mi += tz; if (mi < 0 || mi > 59) { h += Math.floor(mi / 60.0); mi = (mi + 60 * 24) % 60; } if (h >= 0 && h < 24) { return new DateTimeValue( year, month, day, (byte) h, (byte) mi, second, microsecond, timezone); } // Following code is designed to handle the corner case of adjusting from -14:00 to +14:00 or // vice versa, which can cause a change of two days in the date DateTimeValue dt = this; while (h < 0) { h += 24; DateValue t = DateValue.yesterday(dt.getYear(), dt.getMonth(), dt.getDay()); dt = new DateTimeValue( t.getYear(), t.getMonth(), t.getDay(), (byte) h, (byte) mi, second, microsecond, timezone); } if (h > 23) { h -= 24; DateValue t = DateValue.tomorrow(year, month, day); return new DateTimeValue( t.getYear(), t.getMonth(), t.getDay(), (byte) h, (byte) mi, second, microsecond, timezone); } return dt; }
/** This method should fail because the value is no close to. */ @Test @NeedReload public void should_fail_because_value_is_not_close_to() { Table table = new Table(source, "test"); Changes changes = new Changes(table).setStartPointNow(); update("update test set var14 = 1 where var1 = 1"); changes.setEndPointNow(); try { assertThat(changes) .change() .column("var10") .valueAtEndPoint() .isCloseTo( DateTimeValue.of(DateValue.of(2014, 5, 24), TimeValue.of(9, 46, 31)), TimeValue.of(0, 0, 0, 1)); fail("An exception must be raised"); } catch (AssertionError e) { Assertions.assertThat(e.getMessage()) .isEqualTo( String.format( "[Value at end point of Column at index 9 (column name : VAR10) of Change at index 0 (with primary key : [1]) of Changes on TEST table of 'sa/jdbc:h2:mem:test' source] %n" + "Expecting:%n" + " <2014-05-24T09:46:30.000000000>%n" + "to be close to: %n" + " <2014-05-24T09:46:31.000000000> %n" + " with tolerance <00:00:00.000000001>")); } try { assertThat(table) .column("var10") .value() .isCloseTo( DateTimeValue.of(DateValue.of(2014, 5, 24), TimeValue.of(9, 46, 31)), TimeValue.of(0, 0, 0, 100)); fail("An exception must be raised"); } catch (AssertionError e) { Assertions.assertThat(e.getMessage()) .isEqualTo( String.format( "[Value at index 0 of Column at index 9 (column name : VAR10) of TEST table] %n" + "Expecting:%n" + " <2014-05-24T09:46:30.000000000>%n" + "to be close to: %n" + " <2014-05-24T09:46:31.000000000> %n" + " with tolerance <00:00:00.000000100>")); } }
/** * Add a duration to a dateTime * * @param duration the duration to be added (may be negative) * @return the new date * @throws net.sf.saxon.trans.XPathException if the duration is an xs:duration, as distinct from a * subclass thereof */ public CalendarValue add(DurationValue duration) throws XPathException { if (duration instanceof DayTimeDurationValue) { long microseconds = ((DayTimeDurationValue) duration).getLengthInMicroseconds(); BigDecimal seconds = BigDecimal.valueOf(microseconds) .divide(DecimalValue.BIG_DECIMAL_ONE_MILLION, 6, BigDecimal.ROUND_HALF_EVEN); BigDecimal julian = toJulianInstant(); julian = julian.add(seconds); DateTimeValue dt = fromJulianInstant(julian); dt.setTimezoneInMinutes(getTimezoneInMinutes()); return dt; } else if (duration instanceof YearMonthDurationValue) { int months = ((YearMonthDurationValue) duration).getLengthInMonths(); int m = (month - 1) + months; int y = year + m / 12; m = m % 12; if (m < 0) { m += 12; y -= 1; } m++; int d = day; while (!DateValue.isValidDate(y, m, d)) { d -= 1; } return new DateTimeValue( y, (byte) m, (byte) d, hour, minute, second, microsecond, getTimezoneInMinutes()); } else { XPathException err = new XPathException( "DateTime arithmetic is not supported on xs:duration, only on its subtypes"); err.setIsTypeError(true); throw err; } }
/** * Get the Julian instant: a decimal value whose integer part is the Julian day number multiplied * by the number of seconds per day, and whose fractional part is the fraction of the second. This * method operates on the local time, ignoring the timezone. The caller should call normalize() * before calling this method to get a normalized time. * * @return the Julian instant corresponding to this xs:dateTime value */ public BigDecimal toJulianInstant() { int julianDay = DateValue.getJulianDayNumber(year, month, day); long julianSecond = julianDay * (24L * 60L * 60L); julianSecond += (((hour * 60L + minute) * 60L) + second); BigDecimal j = BigDecimal.valueOf(julianSecond); if (microsecond == 0) { return j; } else { return j.add( BigDecimal.valueOf(microsecond) .divide(DecimalValue.BIG_DECIMAL_ONE_MILLION, 6, BigDecimal.ROUND_HALF_EVEN)); } }
static int hashCode( int year, byte month, byte day, byte hour, byte minute, byte second, int microsecond, int tzMinutes) { int tz = -tzMinutes; int h = hour; int mi = minute; mi += tz; if (mi < 0 || mi > 59) { h += Math.floor(mi / 60.0); mi = (mi + 60 * 24) % 60; } while (h < 0) { h += 24; DateValue t = DateValue.yesterday(year, month, day); year = t.getYear(); month = t.getMonth(); day = t.getDay(); } while (h > 23) { h -= 24; DateValue t = DateValue.tomorrow(year, month, day); year = t.getYear(); month = t.getMonth(); day = t.getDay(); } return (year << 4) ^ (month << 28) ^ (day << 23) ^ (h << 18) ^ (mi << 13) ^ second ^ microsecond; }
/** This method tests the {@code isCloseTo} assertion method. */ @Test @NeedReload public void test_is_close_to() { Table table = new Table(source, "test"); Changes changes = new Changes(table).setStartPointNow(); update("update test set var14 = 1 where var1 = 1"); changes.setEndPointNow(); ChangeColumnValueAssert changeColumnValueAssert = assertThat(changes).change().column("var10").valueAtEndPoint(); ChangeColumnValueAssert changeColumnValueAssert2 = changeColumnValueAssert.isCloseTo( DateTimeValue.of(DateValue.of(2014, 5, 24), TimeValue.of(9, 46, 30)), TimeValue.of(0, 0, 0)); Assertions.assertThat(changeColumnValueAssert).isSameAs(changeColumnValueAssert2); TableColumnValueAssert tableColumnValueAssert = assertThat(table).column("var10").value(); TableColumnValueAssert tableColumnValueAssert2 = tableColumnValueAssert.isCloseTo( DateTimeValue.of(DateValue.of(2014, 5, 24), TimeValue.of(9, 46, 30)), TimeValue.of(0, 0, 0)); Assertions.assertThat(tableColumnValueAssert).isSameAs(tableColumnValueAssert2); }
/** * Get the DateTimeValue corresponding to a given Julian instant * * @param instant the Julian instant: a decimal value whose integer part is the Julian day number * multiplied by the number of seconds per day, and whose fractional part is the fraction of * the second. * @return the xs:dateTime value corresponding to the Julian instant. This will always be in * timezone Z. */ public static DateTimeValue fromJulianInstant(BigDecimal instant) { BigInteger julianSecond = instant.toBigInteger(); BigDecimal microseconds = instant .subtract(new BigDecimal(julianSecond)) .multiply(DecimalValue.BIG_DECIMAL_ONE_MILLION); long js = julianSecond.longValue(); long jd = js / (24L * 60L * 60L); DateValue date = DateValue.dateFromJulianDayNumber((int) jd); js = js % (24L * 60L * 60L); byte hour = (byte) (js / (60L * 60L)); js = js % (60L * 60L); byte minute = (byte) (js / (60L)); js = js % (60L); return new DateTimeValue( date.getYear(), date.getMonth(), date.getDay(), hour, minute, (byte) js, microseconds.intValue(), 0); }
/** * Factory method: create a dateTime value from a supplied string, in ISO 8601 format * * @param s a string in the lexical space of xs:dateTime * @return either a DateTimeValue representing the xs:dateTime supplied, or a ValidationFailure if * the lexical value was invalid */ public static ConversionResult makeDateTimeValue(CharSequence s) { // input must have format [-]yyyy-mm-ddThh:mm:ss[.fff*][([+|-]hh:mm | Z)] DateTimeValue dt = new DateTimeValue(); StringTokenizer tok = new StringTokenizer(Whitespace.trimWhitespace(s).toString(), "-:.+TZ", true); if (!tok.hasMoreElements()) { return badDate("too short", s); } String part = (String) tok.nextElement(); int era = +1; if ("+".equals(part)) { return badDate("Date must not start with '+' sign", s); } else if ("-".equals(part)) { era = -1; if (!tok.hasMoreElements()) { return badDate("No year after '-'", s); } part = (String) tok.nextElement(); } int value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric year component", s); } dt.year = value * era; if (part.length() < 4) { return badDate("Year is less than four digits", s); } if (part.length() > 4 && part.charAt(0) == '0') { return badDate("When year exceeds 4 digits, leading zeroes are not allowed", s); } if (dt.year == 0) { return badDate("Year zero is not allowed", s); } if (era < 0) { dt.year++; // internal representation allows a year zero. } if (!tok.hasMoreElements()) { return badDate("Too short", s); } if (!"-".equals(tok.nextElement())) { return badDate("Wrong delimiter after year", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } part = (String) tok.nextElement(); if (part.length() != 2) { return badDate("Month must be two digits", s); } value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric month component", s); } dt.month = (byte) value; if (dt.month < 1 || dt.month > 12) { return badDate("Month is out of range", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } if (!"-".equals(tok.nextElement())) { return badDate("Wrong delimiter after month", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } part = (String) tok.nextElement(); if (part.length() != 2) { return badDate("Day must be two digits", s); } value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric day component", s); } dt.day = (byte) value; if (dt.day < 1 || dt.day > 31) { return badDate("Day is out of range", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } if (!"T".equals(tok.nextElement())) { return badDate("Wrong delimiter after day", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } part = (String) tok.nextElement(); if (part.length() != 2) { return badDate("Hour must be two digits", s); } value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric hour component", s); } dt.hour = (byte) value; if (dt.hour > 24) { return badDate("Hour is out of range", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } if (!":".equals(tok.nextElement())) { return badDate("Wrong delimiter after hour", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } part = (String) tok.nextElement(); if (part.length() != 2) { return badDate("Minute must be two digits", s); } value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric minute component", s); } dt.minute = (byte) value; if (dt.minute > 59) { return badDate("Minute is out of range", s); } if (dt.hour == 24 && dt.minute != 0) { return badDate("If hour is 24, minute must be 00", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } if (!":".equals(tok.nextElement())) { return badDate("Wrong delimiter after minute", s); } if (!tok.hasMoreElements()) { return badDate("Too short", s); } part = (String) tok.nextElement(); if (part.length() != 2) { return badDate("Second must be two digits", s); } value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric second component", s); } dt.second = (byte) value; if (dt.second > 59) { return badDate("Second is out of range", s); } if (dt.hour == 24 && dt.second != 0) { return badDate("If hour is 24, second must be 00", s); } int tz = 0; int state = 0; while (tok.hasMoreElements()) { if (state == 9) { return badDate("Characters after the end", s); } String delim = (String) tok.nextElement(); if (".".equals(delim)) { if (state != 0) { return badDate("Decimal separator occurs twice", s); } if (!tok.hasMoreElements()) { return badDate("Decimal point must be followed by digits", s); } part = (String) tok.nextElement(); value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric fractional seconds component", s); } double fractionalSeconds = Double.parseDouble('.' + part); dt.microsecond = (int) (Math.round(fractionalSeconds * 1000000)); if (dt.hour == 24 && dt.microsecond != 0) { return badDate("If hour is 24, fractional seconds must be 0", s); } state = 1; } else if ("Z".equals(delim)) { if (state > 1) { return badDate("Z cannot occur here", s); } tz = 0; state = 9; // we've finished dt.setTimezoneInMinutes(0); } else if ("+".equals(delim) || "-".equals(delim)) { if (state > 1) { return badDate(delim + " cannot occur here", s); } state = 2; if (!tok.hasMoreElements()) { return badDate("Missing timezone", s); } part = (String) tok.nextElement(); if (part.length() != 2) { return badDate("Timezone hour must be two digits", s); } value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric timezone hour component", s); } tz = value; if (tz > 14) { return badDate("Timezone is out of range (-14:00 to +14:00)", s); } tz *= 60; if ("-".equals(delim)) { tz = -tz; } } else if (":".equals(delim)) { if (state != 2) { return badDate("Misplaced ':'", s); } state = 9; part = (String) tok.nextElement(); value = DurationValue.simpleInteger(part); if (value < 0) { return badDate("Non-numeric timezone minute component", s); } int tzminute = value; if (part.length() != 2) { return badDate("Timezone minute must be two digits", s); } if (tzminute > 59) { return badDate("Timezone minute is out of range", s); } if (tz < 0) { tzminute = -tzminute; } if (Math.abs(tz) == 14 * 60 && tzminute != 0) { return badDate("Timezone is out of range (-14:00 to +14:00)", s); } tz += tzminute; dt.setTimezoneInMinutes(tz); } else { return badDate("Timezone format is incorrect", s); } } if (state == 2 || state == 3) { return badDate("Timezone incomplete", s); } boolean midnight = false; if (dt.hour == 24) { dt.hour = 0; midnight = true; } // Check that this is a valid calendar date if (!DateValue.isValidDate(dt.year, dt.month, dt.day)) { return badDate("Non-existent date", s); } // Adjust midnight to 00:00:00 on the next day if (midnight) { DateValue t = DateValue.tomorrow(dt.year, dt.month, dt.day); dt.year = t.getYear(); dt.month = t.getMonth(); dt.day = t.getDay(); } dt.typeLabel = BuiltInAtomicType.DATE_TIME; return dt; }
@Override public void visit(DateValue dv) { ValueExpression ve = new ValueSpecification(_dateConv, dv.getValue()); _exprStack.push(ve); }
/** * The method <code>normalizeTime</code> * * @param timeValue a <code>String</code> value * @return a <code>String</code> value */ public static String normalizeTime(String timeValue) throws XPathException { int hours = 0; int mins = 0; int secs = 0; int mSecs = 0; int tzHours = 0; int tzMins = 0; DecimalFormat df = new DecimalFormat("00"); DecimalFormat msf = new DecimalFormat("000"); Matcher m = timeNoTZ.matcher(timeValue); if (m.matches()) { hours = Integer.valueOf(m.group(1)).intValue(); mins = Integer.valueOf(m.group(2)).intValue(); secs = Integer.valueOf(m.group(3)).intValue(); if (mins >= 60 || mins < 0 || secs >= 60 || secs < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (hours == 24) { if (mins == 0) { hours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = df.format(hours) + ":" + df.format(mins) + ":" + df.format(secs); } m = dateTimeNoTZ.matcher(timeValue); if (m.matches()) { String date = m.group(1); DateValue dateValue = null; hours = Integer.valueOf(m.group(3)).intValue(); mins = Integer.valueOf(m.group(4)).intValue(); secs = Integer.valueOf(m.group(5)).intValue(); if (mins >= 60 || mins < 0 || secs >= 60 || secs < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (hours == 24) { if (mins == 0) { hours = 0; dateValue = (DateValue) new DateValue(date).plus(new DayTimeDurationValue("P1D")); } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = (dateValue == null ? date : dateValue.getStringValue()) + m.group(2) + df.format(hours) + ":" + df.format(mins) + ":" + df.format(secs); } m = dateTimeMsWTZ.matcher(timeValue); if (m.matches()) { hours = Integer.valueOf(m.group(2)).intValue(); mins = Integer.valueOf(m.group(3)).intValue(); secs = Integer.valueOf(m.group(4)).intValue(); mSecs = Integer.valueOf(m.group(6)).intValue(); tzHours = Integer.valueOf(m.group(8)).intValue(); tzMins = Integer.valueOf(m.group(9)).intValue(); if (mins >= 60 || mins < 0 || tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (hours == 24) { if (mins == 0) { hours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = m.group(1) + df.format(hours) + ":" + df.format(mins) + ":" + df.format(secs) + m.group(5) + msf.format(mSecs) + m.group(7) + df.format(tzHours) + ":" + df.format(tzMins); } m = timeMsWTZ.matcher(timeValue); if (m.matches()) { hours = Integer.valueOf(m.group(1)).intValue(); mins = Integer.valueOf(m.group(2)).intValue(); secs = Integer.valueOf(m.group(3)).intValue(); mSecs = Integer.valueOf(m.group(5)).intValue(); tzHours = Integer.valueOf(m.group(7)).intValue(); tzMins = Integer.valueOf(m.group(8)).intValue(); if (mins >= 60 || mins < 0 || tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (hours == 24) { if (mins == 0) { hours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } timeValue = df.format(hours) + ":" + df.format(mins) + ":" + df.format(secs) + m.group(4) + msf.format(mSecs) + m.group(6) + df.format(tzHours) + ":" + df.format(tzMins); } m = gYearWUTCTZ.matcher(timeValue); if (m.matches()) { tzHours = Integer.valueOf(m.group(3)).intValue(); tzMins = Integer.valueOf(m.group(4)).intValue(); if (tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (tzHours == 24) { if (tzMins == 0) { tzHours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = m.group(1) + m.group(2) + df.format(tzHours) + ":" + df.format(tzMins); } m = gDayWTZ.matcher(timeValue); if (m.matches()) { tzHours = Integer.valueOf(m.group(3)).intValue(); tzMins = Integer.valueOf(m.group(4)).intValue(); if (tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (tzHours == 24) { if (tzMins == 0) { tzHours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = m.group(1) + m.group(2) + df.format(tzHours) + ":" + df.format(tzMins); } m = gMonthWTZ.matcher(timeValue); if (m.matches()) { tzHours = Integer.valueOf(m.group(3)).intValue(); tzMins = Integer.valueOf(m.group(4)).intValue(); if (tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (tzHours == 24) { if (tzMins == 0) { tzHours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = m.group(1) + m.group(2) + df.format(tzHours) + ":" + df.format(tzMins); } m = gYearMonthWTZ.matcher(timeValue); if (m.matches()) { tzHours = Integer.valueOf(m.group(3)).intValue(); tzMins = Integer.valueOf(m.group(4)).intValue(); if (tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (tzHours == 24) { if (tzMins == 0) { tzHours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = m.group(1) + m.group(2) + df.format(tzHours) + ":" + df.format(tzMins); } m = gMonthDayWTZ.matcher(timeValue); if (m.matches()) { tzHours = Integer.valueOf(m.group(3)).intValue(); tzMins = Integer.valueOf(m.group(4)).intValue(); if (tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (tzHours == 24) { if (tzMins == 0) { tzHours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = m.group(1) + m.group(2) + df.format(tzHours) + ":" + df.format(tzMins); } m = dateWTZ.matcher(timeValue); if (m.matches()) { hours = Integer.valueOf(m.group(3)).intValue(); mins = Integer.valueOf(m.group(4)).intValue(); if (mins >= 60 || mins < 0 || tzMins >= 60 || tzMins < 0) { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'"); } if (hours == 24) { if (mins == 0) { hours = 0; } else { throw new XPathException( "err:FORG0001: illegal lexical form for date-time-like value '" + timeValue + "'. If hours is 24, minutes must be 00."); } } // fixme! timeValue = m.group(1) + m.group(2) + df.format(hours) + ":" + df.format(mins); } return timeValue; }
public void testMinus7() throws XPathException { DateValue d1 = new DateValue("2005-10-10"), d2 = new DateValue("2005-10-09"); DayTimeDurationValue r = (DayTimeDurationValue) d1.minus(d2); assertEquals((double) 1 * 24 * 60 * 60, r.getValue(), 0); assertEquals(1, r.getPart(DurationValue.DAY)); }