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