/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
HermiT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
package org.semanticweb.HermiT.datatypes.datetime;
import java.util.Collection;
public class DateTimeInterval {
protected final IntervalType m_intervalType;
protected final long m_lowerBound;
protected final BoundType m_lowerBoundType;
protected final long m_upperBound;
protected final BoundType m_upperBoundType;
public DateTimeInterval(IntervalType intervalType,long lowerBound,BoundType lowerBoundType,long upperBound,BoundType upperBoundType) {
assert !isIntervalEmpty(intervalType,lowerBound,lowerBoundType,upperBound,upperBoundType);
m_intervalType=intervalType;
m_lowerBound=lowerBound;
m_lowerBoundType=lowerBoundType;
m_upperBound=upperBound;
m_upperBoundType=upperBoundType;
}
/**
* Computes the intersection of this interval with the supplied one. If the two intervals
* do not intersect, the result is null.
*/
public DateTimeInterval intersectWith(DateTimeInterval that) {
if (m_intervalType!=that.m_intervalType)
return null;
long newLowerBound;
BoundType newLowerBoundType;
if (m_lowerBound<that.m_lowerBound) {
newLowerBound=that.m_lowerBound;
newLowerBoundType=that.m_lowerBoundType;
}
else if (m_lowerBound>that.m_lowerBound) {
newLowerBound=m_lowerBound;
newLowerBoundType=m_lowerBoundType;
}
else {
newLowerBound=m_lowerBound;
newLowerBoundType=BoundType.getMoreRestrictive(m_lowerBoundType,that.m_lowerBoundType);
}
long newUpperBound;
BoundType newUpperBoundType;
if (m_upperBound<that.m_upperBound) {
newUpperBound=m_upperBound;
newUpperBoundType=m_upperBoundType;
}
else if (m_upperBound>that.m_upperBound) {
newUpperBound=that.m_upperBound;
newUpperBoundType=that.m_upperBoundType;
}
else {
newUpperBound=m_upperBound;
newUpperBoundType=BoundType.getMoreRestrictive(m_upperBoundType,that.m_upperBoundType);
}
if (isIntervalEmpty(m_intervalType,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType))
return null;
// The following lines ensure that we don't create a new interval object unless there is need to.
if (isEqual(m_intervalType,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType))
return this;
else if (that.isEqual(m_intervalType,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType))
return that;
else
return new DateTimeInterval(m_intervalType,newLowerBound,newLowerBoundType,newUpperBound,newUpperBoundType);
}
protected boolean isEqual(IntervalType intervalType,long lowerBound,BoundType lowerBoundType,long upperBound,BoundType upperBoundType) {
return m_intervalType==intervalType && m_lowerBound==lowerBound && m_lowerBoundType==lowerBoundType && m_upperBound==upperBound && m_upperBoundType==upperBoundType;
}
/**
* Subtracts from the given argument the number of distinct objects that are contained in this interval.
* If the interval contains more objects than argument, the result is zero.
*/
public int subtractSizeFrom(int argument) {
if (argument<=0)
return 0;
else if (m_lowerBound<m_upperBound) {
// If the bounds are unequal, the interval contains an infinite number of objects.
// This is because seconds are decimal numbers in principle.
return 0;
}
else {
// Since the interval is not empty, both bounds must be inclusive.
assert m_lowerBoundType==BoundType.INCLUSIVE;
assert m_upperBoundType==BoundType.INCLUSIVE;
if (m_intervalType==IntervalType.WITHOUT_TIMEZONE) {
// There are at most two values without a time zone that gets mapped to a particular point on the time line.
int numberOfValues=1;
if (DateTime.isLastDayInstant(m_lowerBound))
numberOfValues++;
return Math.max(0,argument-numberOfValues);
}
else {
// 840+840+1 different timezoned values that get mapped to a particular point on the time line.
// This is because the time zone offset has the form hh:mm where 0<=hh<14 and 0<=mm<60 or 14:00,
// which gives us 840 values; the other 840 is for the negative time zones, and -1 is so that we don't count 00:00 twice.
int numberOfValues=840+840+1;
// More values can be added for last day instants.
if (DateTime.secondsAreZero(m_lowerBound)) {
if (m_lowerBound>=0) {
int minutesInDay=DateTime.getMinutesInDay(m_lowerBound);
assert minutesInDay<1440;
if (0<=minutesInDay && minutesInDay<=840)
numberOfValues++;
if (1440-840<=minutesInDay)
numberOfValues++;
}
else {
int minutesInDay=DateTime.getMinutesInDay(m_lowerBound);
assert -1440<minutesInDay;
if (-840<=minutesInDay && minutesInDay<=0)
numberOfValues++;
if (minutesInDay<=-1440+840)
numberOfValues++;
}
}
return Math.max(0,argument-numberOfValues);
}
}
}
public boolean containsDateTime(DateTime dateTime) {
if (dateTime.hasTimeZoneOffset()) {
if (m_intervalType==IntervalType.WITHOUT_TIMEZONE)
return false;
}
else {
if (m_intervalType==IntervalType.WITH_TIMEZONE)
return false;
}
long timeOnTimeline=dateTime.getTimeOnTimeline();
if (m_lowerBound>timeOnTimeline || (m_lowerBound==timeOnTimeline && m_lowerBoundType==BoundType.EXCLUSIVE))
return false;
if (m_upperBound<timeOnTimeline || (m_upperBound==timeOnTimeline && m_upperBoundType==BoundType.EXCLUSIVE))
return false;
return true;
}
public void enumerateDateTimes(Collection<Object> dateTimes) {
if (m_lowerBound==m_upperBound) {
// Since the interval is not empty, both bounds must be inclusive.
assert m_lowerBoundType==BoundType.INCLUSIVE;
assert m_upperBoundType==BoundType.INCLUSIVE;
if (m_intervalType==IntervalType.WITHOUT_TIMEZONE) {
// There are at most two values without a time zone that gets mapped to a particular point on the time line.
dateTimes.add(new DateTime(m_lowerBound,false,DateTime.NO_TIMEZONE));
if (DateTime.isLastDayInstant(m_lowerBound))
dateTimes.add(new DateTime(m_lowerBound,true,DateTime.NO_TIMEZONE));
}
else {
// 840+840-1 different timezoned values that get mapped to a particular point on the time line.
// This is because the time zone offset has the form hh:mm where 0<=hh<14 and 0<=mm<60 or 14:00,
// which gives us 840 values; the other 840 is for the negative time zones, and -1 is so that we don't count 00:00 twice.
for (int timeZoneOffset=-840;timeZoneOffset<=840;timeZoneOffset++)
dateTimes.add(new DateTime(m_lowerBound,false,timeZoneOffset));
if (DateTime.secondsAreZero(m_lowerBound)) {
if (m_lowerBound>=0) {
int minutesInDay=DateTime.getMinutesInDay(m_lowerBound);
assert minutesInDay<1440;
if (0<=minutesInDay && minutesInDay<=840)
dateTimes.add(new DateTime(m_lowerBound,true,-minutesInDay));
if (1440-840<=minutesInDay)
dateTimes.add(new DateTime(m_lowerBound,true,1440-minutesInDay));
}
else {
int minutesInDay=DateTime.getMinutesInDay(m_lowerBound);
assert -1440<minutesInDay;
if (-840<=minutesInDay && minutesInDay<=0)
dateTimes.add(new DateTime(m_lowerBound,true,-1440-minutesInDay));
if (minutesInDay<=-1440+840)
dateTimes.add(new DateTime(m_lowerBound,true,-minutesInDay));
}
}
}
}
else
throw new IllegalStateException("The data range is infinite.");
}
protected static boolean isIntervalEmpty(IntervalType intervalType,long lowerBound,BoundType lowerBoundType,long upperBound,BoundType upperBoundType) {
return lowerBound>upperBound ||(lowerBound==upperBound && (lowerBoundType==BoundType.EXCLUSIVE || upperBoundType==BoundType.EXCLUSIVE));
}
public String toString() {
StringBuffer buffer=new StringBuffer();
buffer.append(m_intervalType.toString());
if (m_lowerBoundType==BoundType.INCLUSIVE)
buffer.append('[');
else
buffer.append('<');
buffer.append(m_lowerBound);
buffer.append(" .. ");
buffer.append(m_upperBound);
if (m_upperBoundType==BoundType.INCLUSIVE)
buffer.append(']');
else
buffer.append('>');
return buffer.toString();
}
}