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 }