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 }