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 */
016package org.joda.time;
017
018import java.io.Serializable;
019import java.util.Comparator;
020
021import org.joda.time.convert.ConverterManager;
022import 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 */
048public 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 &lt; comparison, null means now
167     * @param rhsObj  the second object,
168     *      logically on the right of a &lt; comparison, null means now
169     * @return zero if order does not matter,
170     *      negative value if lhsObj &lt; 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}