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 }