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