/*
* Copyright 2001-2009 Stephen Colebourne
*
* Licensed 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.joda.time;
import java.io.Serializable;
import java.util.Comparator;
import org.joda.time.convert.ConverterManager;
import org.joda.time.convert.InstantConverter;
/**
* DateTimeComparator provides comparators to compare one date with another.
*
* Dates may be specified using any object recognised by the
* {@link org.joda.time.convert.ConverterManager ConverterManager} class.
*
* The default objects recognised by the comparator are:
*
* ReadableInstant
* String
* Calendar
* Date
* Long (milliseconds)
* null (now)
*
*
*
* DateTimeComparator is thread-safe and immutable.
*
* @author Guy Allard
* @author Stephen Colebourne
* @author Brian S O'Neill
* @since 1.0
*/
public class DateTimeComparator implements Comparator, Serializable {
/** Serialization lock */
private static final long serialVersionUID = -6097339773320178364L;
/** Singleton instance */
private static final DateTimeComparator ALL_INSTANCE = new DateTimeComparator(null, null);
/** Singleton instance */
private static final DateTimeComparator DATE_INSTANCE = new DateTimeComparator(DateTimeFieldType.dayOfYear(), null);
/** Singleton instance */
private static final DateTimeComparator TIME_INSTANCE = new DateTimeComparator(null, DateTimeFieldType.dayOfYear());
/** The lower limit of fields to compare, null if no limit */
private final DateTimeFieldType iLowerLimit;
/** The upper limit of fields to compare, null if no limit */
private final DateTimeFieldType iUpperLimit;
//-----------------------------------------------------------------------
/**
* Returns a DateTimeComparator the compares the entire date time value.
*
* @return a comparator over all fields
*/
public static DateTimeComparator getInstance() {
return ALL_INSTANCE;
}
/**
* Returns a DateTimeComparator with a lower limit only. Fields of a
* magnitude less than the lower limit are excluded from comparisons.
*
* The time-zone is considered when using this comparator.
* The input millis are truncated using the time-zone of that input value.
* Thus, two inputs with different time-zones will typically not be equal
*
* @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
* @return a comparator over all fields above the lower limit
*/
public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit) {
return getInstance(lowerLimit, null);
}
/**
* Returns a DateTimeComparator with a lower and upper limit. Fields of a
* magnitude less than the lower limit are excluded from comparisons.
* Fields of a magnitude greater than or equal to the upper limit are also
* excluded from comparisons. Either limit may be specified as null, which
* indicates an unbounded limit.
*
* The time-zone is considered when using this comparator unless both limits are null.
* The input millis are rounded/truncated using the time-zone of that input value.
* Thus, two inputs with different time-zones will typically not be equal
*
* @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
* @param upperLimit exclusive upper limit for fields to be compared, null means no limit
* @return a comparator over all fields between the limits
*/
public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
if (lowerLimit == null && upperLimit == null) {
return ALL_INSTANCE;
}
if (lowerLimit == DateTimeFieldType.dayOfYear() && upperLimit == null) {
return DATE_INSTANCE;
}
if (lowerLimit == null && upperLimit == DateTimeFieldType.dayOfYear()) {
return TIME_INSTANCE;
}
return new DateTimeComparator(lowerLimit, upperLimit);
}
/**
* Returns a comparator that only considers date fields.
* Time of day is ignored.
*
* The time-zone is considered when using this comparator.
* The input millis are rounded down to the start of the day
* in the time-zone of that input value. Thus, two inputs with
* different time-zones will typically not be equal
*
* @return a comparator over all date fields
*/
public static DateTimeComparator getDateOnlyInstance() {
return DATE_INSTANCE;
}
/**
* Returns a comparator that only considers time fields.
* Date is ignored.
*
* The time-zone is considered when using this comparator.
* The input millis are truncated to be within the day
* in the time-zone of that input value. Thus, two inputs with
* different time-zones will typically not be equal
*
* @return a comparator over all time fields
*/
public static DateTimeComparator getTimeOnlyInstance() {
return TIME_INSTANCE;
}
/**
* Restricted constructor.
*
* @param lowerLimit the lower field limit, null means no limit
* @param upperLimit the upper field limit, null means no limit
*/
protected DateTimeComparator(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
super();
iLowerLimit = lowerLimit;
iUpperLimit = upperLimit;
}
//-----------------------------------------------------------------------
/**
* Gets the field type that represents the lower limit of comparison.
*
* @return the field type, null if no upper limit
*/
public DateTimeFieldType getLowerLimit() {
return iLowerLimit;
}
/**
* Gets the field type that represents the upper limit of comparison.
*
* @return the field type, null if no upper limit
*/
public DateTimeFieldType getUpperLimit() {
return iUpperLimit;
}
/**
* Compare two objects against only the range of date time fields as
* specified in the constructor.
*
* @param lhsObj the first object,
* logically on the left of a < comparison, null means now
* @param rhsObj the second object,
* logically on the right of a < comparison, null means now
* @return zero if order does not matter,
* negative value if lhsObj < rhsObj, positive value otherwise.
* @throws IllegalArgumentException if either argument is not supported
*/
public int compare(Object lhsObj, Object rhsObj) {
InstantConverter conv = ConverterManager.getInstance().getInstantConverter(lhsObj);
Chronology lhsChrono = conv.getChronology(lhsObj, (Chronology) null);
long lhsMillis = conv.getInstantMillis(lhsObj, lhsChrono);
// handle null==null and other cases where objects are the same
// but only do this after checking the input is valid
if (lhsObj == rhsObj) {
return 0;
}
conv = ConverterManager.getInstance().getInstantConverter(rhsObj);
Chronology rhsChrono = conv.getChronology(rhsObj, (Chronology) null);
long rhsMillis = conv.getInstantMillis(rhsObj, rhsChrono);
if (iLowerLimit != null) {
lhsMillis = iLowerLimit.getField(lhsChrono).roundFloor(lhsMillis);
rhsMillis = iLowerLimit.getField(rhsChrono).roundFloor(rhsMillis);
}
if (iUpperLimit != null) {
lhsMillis = iUpperLimit.getField(lhsChrono).remainder(lhsMillis);
rhsMillis = iUpperLimit.getField(rhsChrono).remainder(rhsMillis);
}
if (lhsMillis < rhsMillis) {
return -1;
} else if (lhsMillis > rhsMillis) {
return 1;
} else {
return 0;
}
}
//-----------------------------------------------------------------------
/**
* Support serialization singletons.
*
* @return the resolved singleton instance
*/
private Object readResolve() {
return getInstance(iLowerLimit, iUpperLimit);
}
/**
* Compares this comparator to another.
*
* @param object the object to compare to
* @return true if equal
*/
public boolean equals(Object object) {
if (object instanceof DateTimeComparator) {
DateTimeComparator other = (DateTimeComparator) object;
return (iLowerLimit == other.getLowerLimit() ||
(iLowerLimit != null && iLowerLimit.equals(other.getLowerLimit()))) &&
(iUpperLimit == other.getUpperLimit() ||
(iUpperLimit != null && iUpperLimit.equals(other.getUpperLimit())));
}
return false;
}
/**
* Gets a suitable hashcode.
*
* @return the hashcode
*/
public int hashCode() {
return (iLowerLimit == null ? 0 : iLowerLimit.hashCode()) +
(123 * (iUpperLimit == null ? 0 : iUpperLimit.hashCode()));
}
/**
* Gets a debugging string.
*
* @return a debugging string
*/
public String toString() {
if (iLowerLimit == iUpperLimit) {
return "DateTimeComparator["
+ (iLowerLimit == null ? "" : iLowerLimit.getName())
+ "]";
} else {
return "DateTimeComparator["
+ (iLowerLimit == null ? "" : iLowerLimit.getName())
+ "-"
+ (iUpperLimit == null ? "" : iUpperLimit.getName())
+ "]";
}
}
}