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