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     */
016    package org.joda.time.base;
017    
018    import java.util.Date;
019    
020    import org.joda.time.Chronology;
021    import org.joda.time.DateTime;
022    import org.joda.time.DateTimeField;
023    import org.joda.time.DateTimeFieldType;
024    import org.joda.time.DateTimeUtils;
025    import org.joda.time.DateTimeZone;
026    import org.joda.time.Instant;
027    import org.joda.time.MutableDateTime;
028    import org.joda.time.ReadableInstant;
029    import org.joda.time.chrono.ISOChronology;
030    import org.joda.time.field.FieldUtils;
031    import org.joda.time.format.DateTimeFormatter;
032    import 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     */
054    public 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    }