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