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