1 | /* |
2 | * Copyright 2001-2006 Stephen Colebourne |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.joda.time.base; |
17 | |
18 | import java.util.Date; |
19 | |
20 | import org.joda.time.Chronology; |
21 | import org.joda.time.DateTime; |
22 | import org.joda.time.DateTimeField; |
23 | import org.joda.time.DateTimeFieldType; |
24 | import org.joda.time.DateTimeUtils; |
25 | import org.joda.time.DateTimeZone; |
26 | import org.joda.time.Instant; |
27 | import org.joda.time.MutableDateTime; |
28 | import org.joda.time.ReadableInstant; |
29 | import org.joda.time.chrono.ISOChronology; |
30 | import org.joda.time.field.FieldUtils; |
31 | import org.joda.time.format.DateTimeFormatter; |
32 | import org.joda.time.format.ISODateTimeFormat; |
33 | |
34 | /** |
35 | * AbstractInstant provides the common behaviour for instant classes. |
36 | * <p> |
37 | * This class has no concept of a chronology, all methods work on the |
38 | * millisecond instant. |
39 | * <p> |
40 | * This class should generally not be used directly by API users. The |
41 | * {@link ReadableInstant} interface should be used when different |
42 | * kinds of date/time objects are to be referenced. |
43 | * <p> |
44 | * Whenever you want to implement <code>ReadableInstant</code> you should |
45 | * extend this class. |
46 | * <p> |
47 | * AbstractInstant itself is thread-safe and immutable, but subclasses may be |
48 | * mutable and not thread-safe. |
49 | * |
50 | * @author Stephen Colebourne |
51 | * @author Brian S O'Neill |
52 | * @since 1.0 |
53 | */ |
54 | public abstract class AbstractInstant implements ReadableInstant { |
55 | |
56 | /** |
57 | * Constructor. |
58 | */ |
59 | protected AbstractInstant() { |
60 | super(); |
61 | } |
62 | |
63 | //----------------------------------------------------------------------- |
64 | /** |
65 | * Gets the time zone of the instant from the chronology. |
66 | * |
67 | * @return the DateTimeZone that the instant is using, never null |
68 | */ |
69 | public DateTimeZone getZone() { |
70 | return getChronology().getZone(); |
71 | } |
72 | |
73 | /** |
74 | * Get the value of one of the fields of a datetime using the chronology of the instant. |
75 | * <p> |
76 | * This method uses the chronology of the instant to obtain the value. |
77 | * For example: |
78 | * <pre> |
79 | * DateTime dt = new DateTime(); |
80 | * int year = dt.get(DateTimeFieldType.year()); |
81 | * </pre> |
82 | * |
83 | * @param type a field type, usually obtained from DateTimeFieldType, not null |
84 | * @return the value of that field |
85 | * @throws IllegalArgumentException if the field type is null |
86 | */ |
87 | public int get(DateTimeFieldType type) { |
88 | if (type == null) { |
89 | throw new IllegalArgumentException("The DateTimeFieldType must not be null"); |
90 | } |
91 | return type.getField(getChronology()).get(getMillis()); |
92 | } |
93 | |
94 | /** |
95 | * Checks if the field type specified is supported by this instant and chronology. |
96 | * This can be used to avoid exceptions in {@link #get(DateTimeFieldType)}. |
97 | * |
98 | * @param type a field type, usually obtained from DateTimeFieldType |
99 | * @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 | } |