1 /*
2 * Copyright 2001-2009 Stephen Colebourne
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.joda.time;
17
18 import java.io.Serializable;
19 import java.util.Comparator;
20
21 import org.joda.time.convert.ConverterManager;
22 import org.joda.time.convert.InstantConverter;
23
24 /**
25 * DateTimeComparator provides comparators to compare one date with another.
26 * <p>
27 * Dates may be specified using any object recognised by the
28 * {@link org.joda.time.convert.ConverterManager ConverterManager} class.
29 * <p>
30 * The default objects recognised by the comparator are:
31 * <ul>
32 * <li>ReadableInstant
33 * <li>String
34 * <li>Calendar
35 * <li>Date
36 * <li>Long (milliseconds)
37 * <li>null (now)
38 * </ul>
39 *
40 * <p>
41 * DateTimeComparator is thread-safe and immutable.
42 *
43 * @author Guy Allard
44 * @author Stephen Colebourne
45 * @author Brian S O'Neill
46 * @since 1.0
47 */
48 public class DateTimeComparator implements Comparator<Object>, Serializable {
49
50 /** Serialization lock */
51 private static final long serialVersionUID = -6097339773320178364L;
52
53 /** Singleton instance */
54 private static final DateTimeComparator ALL_INSTANCE = new DateTimeComparator(null, null);
55 /** Singleton instance */
56 private static final DateTimeComparator DATE_INSTANCE = new DateTimeComparator(DateTimeFieldType.dayOfYear(), null);
57 /** Singleton instance */
58 private static final DateTimeComparator TIME_INSTANCE = new DateTimeComparator(null, DateTimeFieldType.dayOfYear());
59
60 /** The lower limit of fields to compare, null if no limit */
61 private final DateTimeFieldType iLowerLimit;
62 /** The upper limit of fields to compare, null if no limit */
63 private final DateTimeFieldType iUpperLimit;
64
65 //-----------------------------------------------------------------------
66 /**
67 * Returns a DateTimeComparator the compares the entire date time value.
68 *
69 * @return a comparator over all fields
70 */
71 public static DateTimeComparator getInstance() {
72 return ALL_INSTANCE;
73 }
74
75 /**
76 * Returns a DateTimeComparator with a lower limit only. Fields of a
77 * magnitude less than the lower limit are excluded from comparisons.
78 *
79 * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
80 * @return a comparator over all fields above the lower limit
81 */
82 public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit) {
83 return getInstance(lowerLimit, null);
84 }
85
86 /**
87 * Returns a DateTimeComparator with a lower and upper limit. Fields of a
88 * magnitude less than the lower limit are excluded from comparisons.
89 * Fields of a magnitude greater than or equal to the upper limit are also
90 * excluded from comparisons. Either limit may be specified as null, which
91 * indicates an unbounded limit.
92 *
93 * @param lowerLimit inclusive lower limit for fields to be compared, null means no limit
94 * @param upperLimit exclusive upper limit for fields to be compared, null means no limit
95 * @return a comparator over all fields between the limits
96 */
97 public static DateTimeComparator getInstance(DateTimeFieldType lowerLimit, DateTimeFieldType upperLimit) {
98 if (lowerLimit == null && upperLimit == null) {
99 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 }