001 /*
002 * Copyright 2001-2009 Stephen Colebourne
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.joda.time;
017
018 import java.io.Serializable;
019 import java.util.Comparator;
020
021 import org.joda.time.convert.ConverterManager;
022 import org.joda.time.convert.InstantConverter;
023
024 /**
025 * DateTimeComparator provides comparators to compare one date with another.
026 * <p>
027 * Dates may be specified using any object recognised by the
028 * {@link org.joda.time.convert.ConverterManager ConverterManager} class.
029 * <p>
030 * The default objects recognised by the comparator are:
031 * <ul>
032 * <li>ReadableInstant
033 * <li>String
034 * <li>Calendar
035 * <li>Date
036 * <li>Long (milliseconds)
037 * <li>null (now)
038 * </ul>
039 *
040 * <p>
041 * DateTimeComparator is thread-safe and immutable.
042 *
043 * @author Guy Allard
044 * @author Stephen Colebourne
045 * @author Brian S O'Neill
046 * @since 1.0
047 */
048 public class DateTimeComparator implements Comparator<Object>, Serializable {
049
050 /** Serialization lock */
051 private static final long serialVersionUID = -6097339773320178364L;
052
053 /** Singleton instance */
054 private static final DateTimeComparator ALL_INSTANCE = new DateTimeComparator(null, null);
055 /** Singleton instance */
056 private static final DateTimeComparator DATE_INSTANCE = new DateTimeComparator(DateTimeFieldType.dayOfYear(), null);
057 /** Singleton instance */
058 private static final DateTimeComparator TIME_INSTANCE = new DateTimeComparator(null, DateTimeFieldType.dayOfYear());
059
060 /** The lower limit of fields to compare, null if no limit */
061 private final DateTimeFieldType iLowerLimit;
062 /** The upper limit of fields to compare, null if no limit */
063 private final DateTimeFieldType iUpperLimit;
064
065 //-----------------------------------------------------------------------
066 /**
067 * Returns a DateTimeComparator the compares the entire date time value.
068 *
069 * @return a comparator over all fields
070 */
071 public static DateTimeComparator getInstance() {
072 return ALL_INSTANCE;
073 }
074
075 /**
076 * Returns a DateTimeComparator with a lower limit only. Fields of a
077 * magnitude less than the lower limit are excluded from comparisons.
078 *
079 * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
080 * @return a comparator over all fields above the lower limit
081 */
082 public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit) {
083 return getInstance(lowerLimit, null);
084 }
085
086 /**
087 * Returns a DateTimeComparator with a lower and upper limit. Fields of a
088 * magnitude less than the lower limit are excluded from comparisons.
089 * Fields of a magnitude greater than or equal to the upper limit are also
090 * excluded from comparisons. Either limit may be specified as null, which
091 * indicates an unbounded limit.
092 *
093 * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
094 * @param upperLimit exclusive upper limit for fields to be compared, null means no limit
095 * @return a comparator over all fields between the limits
096 */
097 public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
098 if (lowerLimit == null && upperLimit == null) {
099 return ALL_INSTANCE;
100 }
101 if (lowerLimit == DateTimeFieldType.dayOfYear() && upperLimit == null) {
102 return DATE_INSTANCE;
103 }
104 if (lowerLimit == null && upperLimit == DateTimeFieldType.dayOfYear()) {
105 return TIME_INSTANCE;
106 }
107 return new DateTimeComparator(lowerLimit, upperLimit);
108 }
109
110 /**
111 * Returns a comparator that only considers date fields.
112 * Time of day is ignored.
113 *
114 * @return a comparator over all date fields
115 */
116 public static DateTimeComparator getDateOnlyInstance() {
117 return DATE_INSTANCE;
118 }
119
120 /**
121 * Returns a comparator that only considers time fields.
122 * Date is ignored.
123 *
124 * @return a comparator over all time fields
125 */
126 public static DateTimeComparator getTimeOnlyInstance() {
127 return TIME_INSTANCE;
128 }
129
130 /**
131 * Restricted constructor.
132 *
133 * @param lowerLimit the lower field limit, null means no limit
134 * @param upperLimit the upper field limit, null means no limit
135 */
136 protected DateTimeComparator(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
137 super();
138 iLowerLimit = lowerLimit;
139 iUpperLimit = upperLimit;
140 }
141
142 //-----------------------------------------------------------------------
143 /**
144 * Gets the field type that represents the lower limit of comparison.
145 *
146 * @return the field type, null if no upper limit
147 */
148 public DateTimeFieldType getLowerLimit() {
149 return iLowerLimit;
150 }
151
152 /**
153 * Gets the field type that represents the upper limit of comparison.
154 *
155 * @return the field type, null if no upper limit
156 */
157 public DateTimeFieldType getUpperLimit() {
158 return iUpperLimit;
159 }
160
161 /**
162 * Compare two objects against only the range of date time fields as
163 * specified in the constructor.
164 *
165 * @param lhsObj the first object,
166 * logically on the left of a < comparison, null means now
167 * @param rhsObj the second object,
168 * logically on the right of a < comparison, null means now
169 * @return zero if order does not matter,
170 * negative value if lhsObj < rhsObj, positive value otherwise.
171 * @throws IllegalArgumentException if either argument is not supported
172 */
173 public int compare(Object lhsObj, Object rhsObj) {
174 InstantConverter conv = ConverterManager.getInstance().getInstantConverter(lhsObj);
175 Chronology lhsChrono = conv.getChronology(lhsObj, (Chronology) null);
176 long lhsMillis = conv.getInstantMillis(lhsObj, lhsChrono);
177
178 conv = ConverterManager.getInstance().getInstantConverter(rhsObj);
179 Chronology rhsChrono = conv.getChronology(rhsObj, (Chronology) null);
180 long rhsMillis = conv.getInstantMillis(rhsObj, rhsChrono);
181
182 if (iLowerLimit != null) {
183 lhsMillis = iLowerLimit.getField(lhsChrono).roundFloor(lhsMillis);
184 rhsMillis = iLowerLimit.getField(rhsChrono).roundFloor(rhsMillis);
185 }
186
187 if (iUpperLimit != null) {
188 lhsMillis = iUpperLimit.getField(lhsChrono).remainder(lhsMillis);
189 rhsMillis = iUpperLimit.getField(rhsChrono).remainder(rhsMillis);
190 }
191
192 if (lhsMillis < rhsMillis) {
193 return -1;
194 } else if (lhsMillis > rhsMillis) {
195 return 1;
196 } else {
197 return 0;
198 }
199 }
200
201 //-----------------------------------------------------------------------
202 /**
203 * Support serialization singletons.
204 *
205 * @return the resolved singleton instance
206 */
207 private Object readResolve() {
208 return getInstance(iLowerLimit, iUpperLimit);
209 }
210
211 /**
212 * Compares this comparator to another.
213 *
214 * @param object the object to compare to
215 * @return true if equal
216 */
217 public boolean equals(Object object) {
218 if (object instanceof DateTimeComparator) {
219 DateTimeComparator other = (DateTimeComparator) object;
220 return (iLowerLimit == other.getLowerLimit() ||
221 (iLowerLimit != null && iLowerLimit.equals(other.getLowerLimit()))) &&
222 (iUpperLimit == other.getUpperLimit() ||
223 (iUpperLimit != null && iUpperLimit.equals(other.getUpperLimit())));
224 }
225 return false;
226 }
227
228 /**
229 * Gets a suitable hashcode.
230 *
231 * @return the hashcode
232 */
233 public int hashCode() {
234 return (iLowerLimit == null ? 0 : iLowerLimit.hashCode()) +
235 (123 * (iUpperLimit == null ? 0 : iUpperLimit.hashCode()));
236 }
237
238 /**
239 * Gets a debugging string.
240 *
241 * @return a debugging string
242 */
243 public String toString() {
244 if (iLowerLimit == iUpperLimit) {
245 return "DateTimeComparator["
246 + (iLowerLimit == null ? "" : iLowerLimit.getName())
247 + "]";
248 } else {
249 return "DateTimeComparator["
250 + (iLowerLimit == null ? "" : iLowerLimit.getName())
251 + "-"
252 + (iUpperLimit == null ? "" : iUpperLimit.getName())
253 + "]";
254 }
255 }
256
257 }