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