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 */ 016 package org.joda.time; 017 018 import org.joda.time.base.BaseSingleFieldPeriod; 019 import org.joda.time.field.FieldUtils; 020 import org.joda.time.format.ISOPeriodFormat; 021 import org.joda.time.format.PeriodFormatter; 022 023 /** 024 * An immutable time period representing a number of days. 025 * <p> 026 * <code>Days</code> is an immutable period that can only store days. 027 * It does not store years, months or hours for example. As such it is a 028 * type-safe way of representing a number of days in an application. 029 * <p> 030 * The number of days is set in the constructor, and may be queried using 031 * <code>getDays()</code>. Basic mathematical operations are provided - 032 * <code>plus()</code>, <code>minus()</code>, <code>multipliedBy()</code> and 033 * <code>dividedBy()</code>. 034 * <p> 035 * <code>Days</code> is thread-safe and immutable. 036 * 037 * @author Stephen Colebourne 038 * @since 1.4 039 */ 040 public final class Days extends BaseSingleFieldPeriod { 041 042 /** Constant representing zero days. */ 043 public static final Days ZERO = new Days(0); 044 /** Constant representing one day. */ 045 public static final Days ONE = new Days(1); 046 /** Constant representing two days. */ 047 public static final Days TWO = new Days(2); 048 /** Constant representing three days. */ 049 public static final Days THREE = new Days(3); 050 /** Constant representing four days. */ 051 public static final Days FOUR = new Days(4); 052 /** Constant representing five days. */ 053 public static final Days FIVE = new Days(5); 054 /** Constant representing six days. */ 055 public static final Days SIX = new Days(6); 056 /** Constant representing seven days. */ 057 public static final Days SEVEN = new Days(7); 058 /** Constant representing the maximum number of days that can be stored in this object. */ 059 public static final Days MAX_VALUE = new Days(Integer.MAX_VALUE); 060 /** Constant representing the minimum number of days that can be stored in this object. */ 061 public static final Days MIN_VALUE = new Days(Integer.MIN_VALUE); 062 063 /** The paser to use for this class. */ 064 private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.days()); 065 /** Serialization version. */ 066 private static final long serialVersionUID = 87525275727380865L; 067 068 //----------------------------------------------------------------------- 069 /** 070 * Obtains an instance of <code>Days</code> that may be cached. 071 * <code>Days</code> is immutable, so instances can be cached and shared. 072 * This factory method provides access to shared instances. 073 * 074 * @param days the number of days to obtain an instance for 075 * @return the instance of Days 076 */ 077 public static Days days(int days) { 078 switch (days) { 079 case 0: 080 return ZERO; 081 case 1: 082 return ONE; 083 case 2: 084 return TWO; 085 case 3: 086 return THREE; 087 case 4: 088 return FOUR; 089 case 5: 090 return FIVE; 091 case 6: 092 return SIX; 093 case 7: 094 return SEVEN; 095 case Integer.MAX_VALUE: 096 return MAX_VALUE; 097 case Integer.MIN_VALUE: 098 return MIN_VALUE; 099 default: 100 return new Days(days); 101 } 102 } 103 104 //----------------------------------------------------------------------- 105 /** 106 * Creates a <code>Days</code> representing the number of whole days 107 * between the two specified datetimes. This method corectly handles 108 * any daylight savings time changes that may occur during the interval. 109 * 110 * @param start the start instant, must not be null 111 * @param end the end instant, must not be null 112 * @return the period in days 113 * @throws IllegalArgumentException if the instants are null or invalid 114 */ 115 public static Days daysBetween(ReadableInstant start, ReadableInstant end) { 116 int amount = BaseSingleFieldPeriod.between(start, end, DurationFieldType.days()); 117 return Days.days(amount); 118 } 119 120 /** 121 * Creates a <code>Days</code> representing the number of whole days 122 * between the two specified partial datetimes. 123 * <p> 124 * The two partials must contain the same fields, for example you can specify 125 * two <code>LocalDate</code> objects. 126 * 127 * @param start the start partial date, must not be null 128 * @param end the end partial date, must not be null 129 * @return the period in days 130 * @throws IllegalArgumentException if the partials are null or invalid 131 */ 132 public static Days daysBetween(ReadablePartial start, ReadablePartial end) { 133 if (start instanceof LocalDate && end instanceof LocalDate) { 134 Chronology chrono = DateTimeUtils.getChronology(start.getChronology()); 135 int days = chrono.days().getDifference( 136 ((LocalDate) end).getLocalMillis(), ((LocalDate) start).getLocalMillis()); 137 return Days.days(days); 138 } 139 int amount = BaseSingleFieldPeriod.between(start, end, ZERO); 140 return Days.days(amount); 141 } 142 143 /** 144 * Creates a <code>Days</code> representing the number of whole days 145 * in the specified interval. This method corectly handles any daylight 146 * savings time changes that may occur during the interval. 147 * 148 * @param interval the interval to extract days from, null returns zero 149 * @return the period in days 150 * @throws IllegalArgumentException if the partials are null or invalid 151 */ 152 public static Days daysIn(ReadableInterval interval) { 153 if (interval == null) { 154 return Days.ZERO; 155 } 156 int amount = BaseSingleFieldPeriod.between(interval.getStart(), interval.getEnd(), DurationFieldType.days()); 157 return Days.days(amount); 158 } 159 160 /** 161 * Creates a new <code>Days</code> representing the number of complete 162 * standard length days in the specified period. 163 * <p> 164 * This factory method converts all fields from the period to hours using standardised 165 * durations for each field. Only those fields which have a precise duration in 166 * the ISO UTC chronology can be converted. 167 * <ul> 168 * <li>One week consists of 7 days. 169 * <li>One day consists of 24 hours. 170 * <li>One hour consists of 60 minutes. 171 * <li>One minute consists of 60 seconds. 172 * <li>One second consists of 1000 milliseconds. 173 * </ul> 174 * Months and Years are imprecise and periods containing these values cannot be converted. 175 * 176 * @param period the period to get the number of hours from, null returns zero 177 * @return the period in days 178 * @throws IllegalArgumentException if the period contains imprecise duration values 179 */ 180 public static Days standardDaysIn(ReadablePeriod period) { 181 int amount = BaseSingleFieldPeriod.standardPeriodIn(period, DateTimeConstants.MILLIS_PER_DAY); 182 return Days.days(amount); 183 } 184 185 /** 186 * Creates a new <code>Days</code> by parsing a string in the ISO8601 format 'PnD'. 187 * <p> 188 * The parse will accept the full ISO syntax of PnYnMnWnDTnHnMnS however only the 189 * days component may be non-zero. If any other component is non-zero, an exception 190 * will be thrown. 191 * 192 * @param periodStr the period string, null returns zero 193 * @return the period in days 194 * @throws IllegalArgumentException if the string format is invalid 195 */ 196 public static Days parseDays(String periodStr) { 197 if (periodStr == null) { 198 return Days.ZERO; 199 } 200 Period p = PARSER.parsePeriod(periodStr); 201 return Days.days(p.getDays()); 202 } 203 204 //----------------------------------------------------------------------- 205 /** 206 * Creates a new instance representing a number of days. 207 * You should consider using the factory method {@link #days(int)} 208 * instead of the constructor. 209 * 210 * @param days the number of days to represent 211 */ 212 private Days(int days) { 213 super(days); 214 } 215 216 /** 217 * Resolves singletons. 218 * 219 * @return the singleton instance 220 */ 221 private Object readResolve() { 222 return Days.days(getValue()); 223 } 224 225 //----------------------------------------------------------------------- 226 /** 227 * Gets the duration field type, which is <code>days</code>. 228 * 229 * @return the period type 230 */ 231 public DurationFieldType getFieldType() { 232 return DurationFieldType.days(); 233 } 234 235 /** 236 * Gets the period type, which is <code>days</code>. 237 * 238 * @return the period type 239 */ 240 public PeriodType getPeriodType() { 241 return PeriodType.days(); 242 } 243 244 //----------------------------------------------------------------------- 245 /** 246 * Converts this period in days to a period in weeks assuming a 247 * 7 day week. 248 * <p> 249 * This method allows you to convert between different types of period. 250 * However to achieve this it makes the assumption that all weeks are 251 * 7 days long. 252 * This may not be true for some unusual chronologies. However, it is included 253 * as it is a useful operation for many applications and business rules. 254 * 255 * @return a period representing the number of weeks for this number of days 256 */ 257 public Weeks toStandardWeeks() { 258 return Weeks.weeks(getValue() / DateTimeConstants.DAYS_PER_WEEK); 259 } 260 261 /** 262 * Converts this period in days to a period in hours assuming a 263 * 24 hour day. 264 * <p> 265 * This method allows you to convert between different types of period. 266 * However to achieve this it makes the assumption that all days are 24 hours long. 267 * This is not true when daylight savings is considered and may also not 268 * be true for some unusual chronologies. However, it is included 269 * as it is a useful operation for many applications and business rules. 270 * 271 * @return a period representing the number of hours for this number of days 272 * @throws ArithmeticException if the number of hours is too large to be represented 273 */ 274 public Hours toStandardHours() { 275 return Hours.hours(FieldUtils.safeMultiply(getValue(), DateTimeConstants.HOURS_PER_DAY)); 276 } 277 278 /** 279 * Converts this period in days to a period in minutes assuming a 280 * 24 hour day and 60 minute hour. 281 * <p> 282 * This method allows you to convert between different types of period. 283 * However to achieve this it makes the assumption that all days are 24 hours 284 * long and all hours are 60 minutes long. 285 * This is not true when daylight savings is considered and may also not 286 * be true for some unusual chronologies. However, it is included 287 * as it is a useful operation for many applications and business rules. 288 * 289 * @return a period representing the number of minutes for this number of days 290 * @throws ArithmeticException if the number of minutes is too large to be represented 291 */ 292 public Minutes toStandardMinutes() { 293 return Minutes.minutes(FieldUtils.safeMultiply(getValue(), DateTimeConstants.MINUTES_PER_DAY)); 294 } 295 296 /** 297 * Converts this period in days to a period in seconds assuming a 298 * 24 hour day, 60 minute hour and 60 second minute. 299 * <p> 300 * This method allows you to convert between different types of period. 301 * However to achieve this it makes the assumption that all days are 24 hours 302 * long, all hours are 60 minutes long and all minutes are 60 seconds long. 303 * This is not true when daylight savings is considered and may also not 304 * be true for some unusual chronologies. However, it is included 305 * as it is a useful operation for many applications and business rules. 306 * 307 * @return a period representing the number of seconds for this number of days 308 * @throws ArithmeticException if the number of seconds is too large to be represented 309 */ 310 public Seconds toStandardSeconds() { 311 return Seconds.seconds(FieldUtils.safeMultiply(getValue(), DateTimeConstants.SECONDS_PER_DAY)); 312 } 313 314 //----------------------------------------------------------------------- 315 /** 316 * Converts this period in days to a duration in milliseconds assuming a 317 * 24 hour day, 60 minute hour and 60 second minute. 318 * <p> 319 * This method allows you to convert from a period to a duration. 320 * However to achieve this it makes the assumption that all days are 24 hours 321 * long, all hours are 60 minutes and all minutes are 60 seconds. 322 * This is not true when daylight savings time is considered, and may also 323 * not be true for some unusual chronologies. However, it is included as it 324 * is a useful operation for many applications and business rules. 325 * 326 * @return a duration equivalent to this number of days 327 */ 328 public Duration toStandardDuration() { 329 long days = getValue(); // assign to a long 330 return new Duration(days * DateTimeConstants.MILLIS_PER_DAY); 331 } 332 333 //----------------------------------------------------------------------- 334 /** 335 * Gets the number of days that this period represents. 336 * 337 * @return the number of days in the period 338 */ 339 public int getDays() { 340 return getValue(); 341 } 342 343 //----------------------------------------------------------------------- 344 /** 345 * Returns a new instance with the specified number of days added. 346 * <p> 347 * This instance is immutable and unaffected by this method call. 348 * 349 * @param days the amount of days to add, may be negative 350 * @return the new period plus the specified number of days 351 * @throws ArithmeticException if the result overflows an int 352 */ 353 public Days plus(int days) { 354 if (days == 0) { 355 return this; 356 } 357 return Days.days(FieldUtils.safeAdd(getValue(), days)); 358 } 359 360 /** 361 * Returns a new instance with the specified number of days added. 362 * <p> 363 * This instance is immutable and unaffected by this method call. 364 * 365 * @param days the amount of days to add, may be negative, null means zero 366 * @return the new period plus the specified number of days 367 * @throws ArithmeticException if the result overflows an int 368 */ 369 public Days plus(Days days) { 370 if (days == null) { 371 return this; 372 } 373 return plus(days.getValue()); 374 } 375 376 //----------------------------------------------------------------------- 377 /** 378 * Returns a new instance with the specified number of days taken away. 379 * <p> 380 * This instance is immutable and unaffected by this method call. 381 * 382 * @param days the amount of days to take away, may be negative 383 * @return the new period minus the specified number of days 384 * @throws ArithmeticException if the result overflows an int 385 */ 386 public Days minus(int days) { 387 return plus(FieldUtils.safeNegate(days)); 388 } 389 390 /** 391 * Returns a new instance with the specified number of days taken away. 392 * <p> 393 * This instance is immutable and unaffected by this method call. 394 * 395 * @param days the amount of days to take away, may be negative, null means zero 396 * @return the new period minus the specified number of days 397 * @throws ArithmeticException if the result overflows an int 398 */ 399 public Days minus(Days days) { 400 if (days == null) { 401 return this; 402 } 403 return minus(days.getValue()); 404 } 405 406 //----------------------------------------------------------------------- 407 /** 408 * Returns a new instance with the days multiplied by the specified scalar. 409 * <p> 410 * This instance is immutable and unaffected by this method call. 411 * 412 * @param scalar the amount to multiply by, may be negative 413 * @return the new period multiplied by the specified scalar 414 * @throws ArithmeticException if the result overflows an int 415 */ 416 public Days multipliedBy(int scalar) { 417 return Days.days(FieldUtils.safeMultiply(getValue(), scalar)); 418 } 419 420 /** 421 * Returns a new instance with the days divided by the specified divisor. 422 * The calculation uses integer division, thus 3 divided by 2 is 1. 423 * <p> 424 * This instance is immutable and unaffected by this method call. 425 * 426 * @param divisor the amount to divide by, may be negative 427 * @return the new period divided by the specified divisor 428 * @throws ArithmeticException if the divisor is zero 429 */ 430 public Days dividedBy(int divisor) { 431 if (divisor == 1) { 432 return this; 433 } 434 return Days.days(getValue() / divisor); 435 } 436 437 //----------------------------------------------------------------------- 438 /** 439 * Returns a new instance with the days value negated. 440 * 441 * @return the new period with a negated value 442 * @throws ArithmeticException if the result overflows an int 443 */ 444 public Days negated() { 445 return Days.days(FieldUtils.safeNegate(getValue())); 446 } 447 448 //----------------------------------------------------------------------- 449 /** 450 * Is this days instance greater than the specified number of days. 451 * 452 * @param other the other period, null means zero 453 * @return true if this days instance is greater than the specified one 454 */ 455 public boolean isGreaterThan(Days other) { 456 if (other == null) { 457 return getValue() > 0; 458 } 459 return getValue() > other.getValue(); 460 } 461 462 /** 463 * Is this days instance less than the specified number of days. 464 * 465 * @param other the other period, null means zero 466 * @return true if this days instance is less than the specified one 467 */ 468 public boolean isLessThan(Days other) { 469 if (other == null) { 470 return getValue() < 0; 471 } 472 return getValue() < other.getValue(); 473 } 474 475 //----------------------------------------------------------------------- 476 /** 477 * Gets this instance as a String in the ISO8601 duration format. 478 * <p> 479 * For example, "P4D" represents 4 days. 480 * 481 * @return the value as an ISO8601 string 482 */ 483 public String toString() { 484 return "P" + String.valueOf(getValue()) + "D"; 485 } 486 487 }