/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jena.datatypes.xsd; import java.util.Arrays ; /** * Base class for representation of XSD duration, time, date/time * and related datatype instances. We are using the Xerces internal * packages for the all heavy lifting which represent date/times * using an int array. These wrapper classes just provide more * convenient access to the date values. * <p> * This class includes code derived from Xerces 2.6.0 * Copyright (c) 1999-2002 The Apache Software Foundation. * All rights reserved. * </p> */ public class AbstractDateTime implements Comparable<AbstractDateTime> { /** The array of year/month etc values as ints */ protected int[] data; /** The fractional seconds */ protected double fractionalSeconds; //define constants protected final static int CY = 0, M = 1, D = 2, h = 3, m = 4, s = 5, ms = 6, utc=7, msscale=8 ; // Timezone constants protected final static int hh=0, mm=1; //size for all objects must have the same fields: //CCYY, MM, DD, h, m, s, ms + timeZone protected final static int TOTAL_SIZE = 9; // The number of comparable values protected final static int COMPARABLE_SUBSET = 6; /** constant to indicate a less than relationship from compare() */ public static final short LESS_THAN = -1; /** constant to indicate an equals relationship from compare() */ public static final short EQUAL = 0; /** constant to indicate a greater than relationship from compare() */ public static final short GREATER_THAN = 1; /** constant to indicate an indeterminate relationship from compare() */ public static final short INDETERMINATE = 2; /** * Constructor * @param value the date/time value returned by the parsing */ public AbstractDateTime(Object value) { data = (int[]) value; //if (data[utc] == 0) data[utc]='Z'; extractFractionalSeconds(); } /** * Comparison function. Not quite the same as normal java compare * because XSD date/times are not always comparable. * * @param other the time/date to compare to * @return an order flag - one of LESS_THAN, EQUAL, GREATER_THEN, INDETERMINATE */ public int compare(AbstractDateTime other) { return compareValues(data, other.data, true); } /** * Normal java comparison function. Treats INDETERMINATE as the same * as equals. This is not strictly correct but seems like an appropriate * way to handle partially ordered objects. */ @Override public int compareTo(AbstractDateTime o) { switch (compare(o)) { case EQUAL: case INDETERMINATE: return 0; case LESS_THAN: return -1; case GREATER_THAN: return 1; } return 0; } /** * Convert fractional second representation to a simple double.. */ protected void extractFractionalSeconds() { fractionalSeconds = 0.0 ; if (data[ms] != 0) { int fs = data[ms]; fractionalSeconds = (fs) / Math.pow(10.0, data[msscale]); } } /** * Equality function */ @Override public boolean equals(Object obj) { if (obj instanceof AbstractDateTime) { AbstractDateTime adt = (AbstractDateTime) obj; for (int i = 0; i < data.length; i++) { if (data[i] != adt.data[i]) return false; } // fractionalSeconds is generated from data[ms] return true; } return false; } @Override public int hashCode() { // See JENA-1140. // Eclipse generated - without fractionalSeconds which is generated from data[ms] final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(data); return result; } // -------------------------------------------------------------------- // This code is adapated from Xerces 2.6.0 AbstractDateTimeDV. // Copyright (c) 1999-2003 The Apache Software Foundation. All rights // reserved. // -------------------------------------------------------------------- /** * Compare algorithm described in dateDime (3.2.7). * * @param date1 normalized date representation of the first value * @param date2 normalized date representation of the second value * @param strict * @return less, greater, less_equal, greater_equal, equal */ protected short compareValues(int[] date1, int[] date2, boolean strict) { if ( date1[utc]==date2[utc] ) { return compareOrder(date1, date2); } short c1, c2; int[] tempDate = new int[TOTAL_SIZE]; int[] timeZone = new int[2]; if ( date1[utc]=='Z' ) { //compare date1<=date1<=(date2 with time zone -14) // cloneDate(date2, tempDate); //clones date1 value to global temporary storage: fTempDate timeZone[hh]=14; timeZone[mm]=0; tempDate[utc]='+'; normalize(tempDate, timeZone); c1 = compareOrder(date1, tempDate); if (c1 == LESS_THAN) return c1; //compare date1>=(date2 with time zone +14) // cloneDate(date2, tempDate); //clones date1 value to global temporary storage: tempDate timeZone[hh]=14; timeZone[mm]=0; tempDate[utc]='-'; normalize(tempDate, timeZone); c2 = compareOrder(date1, tempDate); if (c2 == GREATER_THAN) return c2; return INDETERMINATE; } else if ( date2[utc]=='Z' ) { //compare (date1 with time zone -14)<=date2 // cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate timeZone[hh]=14; timeZone[mm]=0; tempDate[utc]='-'; normalize(tempDate, timeZone); c1 = compareOrder(tempDate, date2); if (c1 == LESS_THAN) return c1; //compare (date1 with time zone +14)<=date2 // cloneDate(date1, tempDate); //clones date1 value to global temporary storage: tempDate timeZone[hh]=14; timeZone[mm]=0; tempDate[utc]='+'; normalize(tempDate, timeZone); c2 = compareOrder(tempDate, date2); if (c2 == GREATER_THAN) return c2; return INDETERMINATE; } return INDETERMINATE; } /** * Given normalized values, determines order-relation * between give date/time objects. * * @param date1 date/time object * @param date2 date/time object * @return 0 if date1 and date2 are equal, a value less than 0 if date1 is less than date2, a value greater than 0 if date1 is greater than date2 */ protected short compareOrder (int[] date1, int[] date2) { for ( int i=0; i < COMPARABLE_SUBSET;i++ ) { if (date1[i] < date2[i]) { return -1; } else if (date1[i] > date2[i]) { return 1; } } // Compare subsecond components int maxScale = Math.max(date1[msscale], date2[msscale]); int ss1 = scale(date1[ms], date1[msscale], maxScale); int ss2 = scale(date2[ms], date2[msscale], maxScale); if (ss1 < ss2) { return -1; } else if (ss1 > ss2) { return 1; } return 0; } /** * Scale up a subsecond part to make comparable to another of the given scale */ private int scale(int val, int scale, int targetScale) { for (int i = scale; i < targetScale; i++) val *= 10; return val; } /** * If timezone present - normalize dateTime [E Adding durations to dateTimes] * Public to allow reuse with type objects. * * @param date CCYY-MM-DDThh:mm:ss+03 */ public static void normalize (int[] date, int[] timeZone) { // REVISIT: we have common code in addDuration() for durations // should consider reorganizing it. // //add minutes (from time zone) int negate = 1; if (date[utc]=='+') { negate = -1; } int temp = date[m] + negate*timeZone[mm]; int carry = fQuotient (temp, 60); date[m]= mod(temp, 60, carry); //add hours temp = date[h] + negate*timeZone[hh] + carry; carry = fQuotient(temp, 24); date[h]=mod(temp, 24, carry); date[D]=date[D]+carry; while ( true ) { temp=maxDayInMonthFor(date[CY], date[M]); if (date[D]<1) { date[D] = date[D] + maxDayInMonthFor(date[CY], date[M]-1); carry=-1; } else if ( date[D]>temp ) { date[D]=date[D]-temp; carry=1; } else { break; } temp=date[M]+carry; date[M]=modulo(temp, 1, 13); date[CY]=date[CY]+fQuotient(temp, 1, 13); } date[utc]='Z'; } /** * Resets object representation of date/time * * @param data date/time object */ protected void resetDateObj (int[] data) { for ( int i=0;i<TOTAL_SIZE;i++ ) { data[i]=0; } } /** * Given {year,month} computes maximum * number of days for given month * * @param year * @param month * @return integer containg the number of days in a given month */ protected static int maxDayInMonthFor(int year, int month) { //validate days if ( month==4 || month==6 || month==9 || month==11 ) { return 30; } else if ( month==2 ) { if ( isLeapYear(year) ) { return 29; } else { return 28; } } else { return 31; } } private static boolean isLeapYear(int year) { //REVISIT: should we take care about Julian calendar? return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0))); } // // help function described in W3C PR Schema [E Adding durations to dateTimes] // protected static int mod (int a, int b, int quotient) { //modulo(a, b) = a - fQuotient(a,b)*b return (a - quotient*b) ; } // // help function described in W3C PR Schema [E Adding durations to dateTimes] // protected static int fQuotient (int a, int b) { //fQuotient(a, b) = the greatest integer less than or equal to a/b return (int)Math.floor((float)a/b); } // // help function described in W3C PR Schema [E Adding durations to dateTimes] // protected static int modulo (int temp, int low, int high) { //modulo(a - low, high - low) + low int a = temp - low; int b = high - low; return (mod (a, b, fQuotient(a, b)) + low) ; } // // help function described in W3C PR Schema [E Adding durations to dateTimes] // protected static int fQuotient (int temp, int low, int high) { //fQuotient(a - low, high - low) return fQuotient(temp - low, high - low); } // //Private help functions // private void cloneDate (int[] finalValue, int[] tempDate) { System.arraycopy(finalValue, 0, tempDate, 0, TOTAL_SIZE); } // -------------------------------------------------------------------- // End of code is adapated from Xerces 2.6.0 AbstractDateTimeDV. // -------------------------------------------------------------------- }