001/*
002 *  Copyright 2001-2006 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.base;
017
018import java.util.Date;
019
020import org.joda.time.Chronology;
021import org.joda.time.DateTime;
022import org.joda.time.DateTimeField;
023import org.joda.time.DateTimeFieldType;
024import org.joda.time.DateTimeUtils;
025import org.joda.time.DateTimeZone;
026import org.joda.time.Instant;
027import org.joda.time.MutableDateTime;
028import org.joda.time.ReadableInstant;
029import org.joda.time.chrono.ISOChronology;
030import org.joda.time.field.FieldUtils;
031import org.joda.time.format.DateTimeFormatter;
032import org.joda.time.format.ISODateTimeFormat;
033
034/**
035 * AbstractInstant provides the common behaviour for instant classes.
036 * <p>
037 * This class has no concept of a chronology, all methods work on the
038 * millisecond instant.
039 * <p>
040 * This class should generally not be used directly by API users. The 
041 * {@link ReadableInstant} interface should be used when different 
042 * kinds of date/time objects are to be referenced.
043 * <p>
044 * Whenever you want to implement <code>ReadableInstant</code> you should
045 * extend this class.
046 * <p>
047 * AbstractInstant itself is thread-safe and immutable, but subclasses may be
048 * mutable and not thread-safe.
049 *
050 * @author Stephen Colebourne
051 * @author Brian S O'Neill
052 * @since 1.0
053 */
054public abstract class AbstractInstant implements ReadableInstant {
055
056    /**
057     * Constructor.
058     */
059    protected AbstractInstant() {
060        super();
061    }
062
063    //-----------------------------------------------------------------------
064    /**
065     * Gets the time zone of the instant from the chronology.
066     * 
067     * @return the DateTimeZone that the instant is using, never null
068     */
069    public DateTimeZone getZone() {
070        return getChronology().getZone();
071    }
072
073    /**
074     * Get the value of one of the fields of a datetime using the chronology of the instant.
075     * <p>
076     * This method uses the chronology of the instant to obtain the value.
077     * For example:
078     * <pre>
079     * DateTime dt = new DateTime();
080     * int year = dt.get(DateTimeFieldType.year());
081     * </pre>
082     *
083     * @param type  a field type, usually obtained from DateTimeFieldType, not null
084     * @return the value of that field
085     * @throws IllegalArgumentException if the field type is null
086     */
087    public int get(DateTimeFieldType type) {
088        if (type == null) {
089            throw new IllegalArgumentException("The DateTimeFieldType must not be null");
090        }
091        return type.getField(getChronology()).get(getMillis());
092    }
093
094    /**
095     * Checks if the field type specified is supported by this instant and chronology.
096     * This can be used to avoid exceptions in {@link #get(DateTimeFieldType)}.
097     *
098     * @param type  a field type, usually obtained from DateTimeFieldType
099     * @return true if the field type is supported
100     */
101    public boolean isSupported(DateTimeFieldType type) {
102        if (type == null) {
103            return false;
104        }
105        return type.getField(getChronology()).isSupported();
106    }
107
108    /**
109     * Get the value of one of the fields of a datetime.
110     * <p>
111     * This could be used to get a field using a different Chronology.
112     * For example:
113     * <pre>
114     * Instant dt = new Instant();
115     * int gjYear = dt.get(Chronology.getCoptic().year());
116     * </pre>
117     * 
118     * @param field  the DateTimeField to use, not null
119     * @return the value
120     * @throws IllegalArgumentException if the field is null
121     */
122    public int get(DateTimeField field) {
123        if (field == null) {
124            throw new IllegalArgumentException("The DateTimeField must not be null");
125        }
126        return field.get(getMillis());
127    }
128
129    //-----------------------------------------------------------------------
130    /**
131     * Get this object as an Instant.
132     * 
133     * @return an Instant using the same millis
134     */
135    public Instant toInstant() {
136        return new Instant(getMillis());
137    }
138
139    /**
140     * Get this object as a DateTime in the same zone.
141     *
142     * @return a DateTime using the same millis
143     */
144    public DateTime toDateTime() {
145        return new DateTime(getMillis(), getZone());
146    }
147
148    /**
149     * Get this object as a DateTime using ISOChronology in the same zone.
150     *
151     * @return a DateTime using the same millis with ISOChronology
152     */
153    public DateTime toDateTimeISO() {
154        return new DateTime(getMillis(), ISOChronology.getInstance(getZone()));
155    }
156
157    /**
158     * Get this object as a DateTime using the same chronology but a different zone.
159     * 
160     * @param zone time zone to apply, or default if null
161     * @return a DateTime using the same millis
162     */
163    public DateTime toDateTime(DateTimeZone zone) {
164        Chronology chrono = DateTimeUtils.getChronology(getChronology());
165        chrono = chrono.withZone(zone);
166        return new DateTime(getMillis(), chrono);
167    }
168
169    /**
170     * Get this object as a DateTime using the given chronology and its zone.
171     * 
172     * @param chronology chronology to apply, or ISOChronology if null
173     * @return a DateTime using the same millis
174     */
175    public DateTime toDateTime(Chronology chronology) {
176        return new DateTime(getMillis(), chronology);
177    }
178
179    // NOTE: Although the toMutableDateTime methods could check to see if this
180    // is already a MutableDateTime and return this casted, it makes it too
181    // easy to mistakenly modify ReadableDateTime input parameters. Always
182    // returning a copy prevents this.
183
184    /**
185     * Get this object as a MutableDateTime in the same zone.
186     *
187     * @return a MutableDateTime using the same millis
188     */
189    public MutableDateTime toMutableDateTime() {
190        return new MutableDateTime(getMillis(), getZone());
191    }
192
193    /**
194     * Get this object as a MutableDateTime using ISOChronology in the same zone.
195     *
196     * @return a MutableDateTime using the same millis with ISOChronology
197     */
198    public MutableDateTime toMutableDateTimeISO() {
199        return new MutableDateTime(getMillis(), ISOChronology.getInstance(getZone()));
200    }
201
202    /**
203     * Get this object as a MutableDateTime using the same chronology but a different zone.
204     * 
205     * @param zone time zone to apply, or default if null
206     * @return a MutableDateTime using the same millis
207     */
208    public MutableDateTime toMutableDateTime(DateTimeZone zone) {
209        Chronology chrono = DateTimeUtils.getChronology(getChronology());
210        chrono = chrono.withZone(zone);
211        return new MutableDateTime(getMillis(), chrono);
212    }
213
214    /**
215     * Get this object as a MutableDateTime using the given chronology and its zone.
216     * 
217     * @param chronology chronology to apply, or ISOChronology if null
218     * @return a MutableDateTime using the same millis
219     */
220    public MutableDateTime toMutableDateTime(Chronology chronology) {
221        return new MutableDateTime(getMillis(), chronology);
222    }
223
224    //-----------------------------------------------------------------------
225    /**
226     * Get the date time as a <code>java.util.Date</code>.
227     * <p>
228     * The <code>Date</code> object created has exactly the same millisecond
229     * instant as this object.
230     *
231     * @return a Date initialised with this datetime
232     */
233    public Date toDate() {
234        return new Date(getMillis());
235    }
236
237    //-----------------------------------------------------------------------
238    /**
239     * Compares this object with the specified object for equality based
240     * on the millisecond instant, chronology and time zone.
241     * <p>
242     * Two objects which represent the same instant in time, but are in
243     * different time zones (based on time zone id), will be considered to
244     * be different. Only two objects with the same {@link DateTimeZone},
245     * {@link Chronology} and instant are equal.
246     * <p>
247     * See {@link #isEqual(ReadableInstant)} for an equals method that
248     * ignores the Chronology and time zone.
249     * <p>
250     * All ReadableInstant instances are accepted.
251     *
252     * @param readableInstant  a readable instant to check against
253     * @return true if millisecond and chronology are equal, false if
254     *  not or the instant is null or of an incorrect type
255     */
256    public boolean equals(Object readableInstant) {
257        // must be to fulfil ReadableInstant contract
258        if (this == readableInstant) {
259            return true;
260        }
261        if (readableInstant instanceof ReadableInstant == false) {
262            return false;
263        }
264        ReadableInstant otherInstant = (ReadableInstant) readableInstant;
265        return
266            getMillis() == otherInstant.getMillis() &&
267            FieldUtils.equals(getChronology(), otherInstant.getChronology());
268    }
269
270    /**
271     * Gets a hash code for the instant as defined in <code>ReadableInstant</code>.
272     *
273     * @return a suitable hash code
274     */
275    public int hashCode() {
276        // must be to fulfil ReadableInstant contract
277        return
278            ((int) (getMillis() ^ (getMillis() >>> 32))) +
279            (getChronology().hashCode());
280    }
281
282    /**
283     * Compares this object with the specified object for ascending
284     * millisecond instant order. This ordering is inconsistent with
285     * equals, as it ignores the Chronology.
286     * <p>
287     * All ReadableInstant instances are accepted.
288     *
289     * @param instant  a readable instant to check against
290     * @return negative value if this is less, 0 if equal, or positive value if greater
291     * @throws NullPointerException if the object is null
292     * @throws ClassCastException if the object type is not supported
293     */
294    public int compareTo(Object instant) {
295        if (this == instant) {
296            return 0;
297        }
298        
299        ReadableInstant otherInstant = (ReadableInstant) instant;
300        
301        long otherMillis = otherInstant.getMillis();
302        long thisMillis = getMillis();
303        
304        // cannot do (thisMillis - otherMillis) as can overflow
305        if (thisMillis == otherMillis) {
306            return 0;
307        }
308        if (thisMillis < otherMillis) {
309            return -1;
310        } else {
311            return 1;
312        }
313    }
314
315    //-----------------------------------------------------------------------
316    /**
317     * Is this instant after the millisecond instant passed in
318     * comparing solely by millisecond.
319     *
320     * @param instant  a millisecond instant to check against
321     * @return true if this instant is after the instant passed in
322     */
323    public boolean isAfter(long instant) {
324        return (getMillis() > instant);
325    }
326
327    /**
328     * Is this instant after the current instant
329     * comparing solely by millisecond.
330     * 
331     * @return true if this instant is after the current instant
332     */
333    public boolean isAfterNow() {
334        return isAfter(DateTimeUtils.currentTimeMillis());
335    }
336
337    /**
338     * Is this instant after the instant passed in
339     * comparing solely by millisecond.
340     *
341     * @param instant  an instant to check against, null means now
342     * @return true if the instant is after the instant passed in
343     */
344    public boolean isAfter(ReadableInstant instant) {
345        long instantMillis = DateTimeUtils.getInstantMillis(instant);
346        return isAfter(instantMillis);
347    }
348
349    //-----------------------------------------------------------------------
350    /**
351     * Is this instant before the millisecond instant passed in
352     * comparing solely by millisecond.
353     *
354     * @param instant  a millisecond instant to check against
355     * @return true if this instant is before the instant passed in
356     */
357    public boolean isBefore(long instant) {
358        return (getMillis() < instant);
359    }
360
361    /**
362     * Is this instant before the current instant
363     * comparing solely by millisecond.
364     * 
365     * @return true if this instant is before the current instant
366     */
367    public boolean isBeforeNow() {
368        return isBefore(DateTimeUtils.currentTimeMillis());
369    }
370
371    /**
372     * Is this instant before the instant passed in
373     * comparing solely by millisecond.
374     *
375     * @param instant  an instant to check against, null means now
376     * @return true if the instant is before the instant passed in
377     */
378    public boolean isBefore(ReadableInstant instant) {
379        long instantMillis = DateTimeUtils.getInstantMillis(instant);
380        return isBefore(instantMillis);
381    }
382
383    //-----------------------------------------------------------------------
384    /**
385     * Is this instant equal to the millisecond instant passed in
386     * comparing solely by millisecond.
387     *
388     * @param instant  a millisecond instant to check against
389     * @return true if this instant is before the instant passed in
390     */
391    public boolean isEqual(long instant) {
392        return (getMillis() == instant);
393    }
394
395    /**
396     * Is this instant equal to the current instant
397     * comparing solely by millisecond.
398     * 
399     * @return true if this instant is before the current instant
400     */
401    public boolean isEqualNow() {
402        return isEqual(DateTimeUtils.currentTimeMillis());
403    }
404
405    /**
406     * Is this instant equal to the instant passed in
407     * comparing solely by millisecond.
408     *
409     * @param instant  an instant to check against, null means now
410     * @return true if the instant is equal to the instant passed in
411     */
412    public boolean isEqual(ReadableInstant instant) {
413        long instantMillis = DateTimeUtils.getInstantMillis(instant);
414        return isEqual(instantMillis);
415    }
416
417    //-----------------------------------------------------------------------
418    /**
419     * Output the date time in ISO8601 format (yyyy-MM-ddTHH:mm:ss.SSSZZ).
420     * 
421     * @return ISO8601 time formatted string.
422     */
423    public String toString() {
424        return ISODateTimeFormat.dateTime().print(this);
425    }
426
427    //-----------------------------------------------------------------------
428    /**
429     * Uses the specified formatter to convert this partial to a String.
430     *
431     * @param formatter  the formatter to use, null means use <code>toString()</code>.
432     * @return the formatted string
433     * @since 1.1
434     */
435    public String toString(DateTimeFormatter formatter) {
436        if (formatter == null) {
437            return toString();
438        }
439        return formatter.print(this);
440    }
441
442}