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 months. 027 * <p> 028 * <code>Months</code> is an immutable period that can only store months. 029 * It does not store years, days or hours for example. As such it is a 030 * type-safe way of representing a number of months in an application. 031 * <p> 032 * The number of months is set in the constructor, and may be queried using 033 * <code>getMonths()</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>Months</code> is thread-safe and immutable. 038 * 039 * @author Stephen Colebourne 040 * @since 1.4 041 */ 042 public final class Months extends BaseSingleFieldPeriod { 043 044 /** Constant representing zero months. */ 045 public static final Months ZERO = new Months(0); 046 /** Constant representing one month. */ 047 public static final Months ONE = new Months(1); 048 /** Constant representing two months. */ 049 public static final Months TWO = new Months(2); 050 /** Constant representing three months. */ 051 public static final Months THREE = new Months(3); 052 /** Constant representing four months. */ 053 public static final Months FOUR = new Months(4); 054 /** Constant representing five months. */ 055 public static final Months FIVE = new Months(5); 056 /** Constant representing six months. */ 057 public static final Months SIX = new Months(6); 058 /** Constant representing seven months. */ 059 public static final Months SEVEN = new Months(7); 060 /** Constant representing eight months. */ 061 public static final Months EIGHT = new Months(8); 062 /** Constant representing nine months. */ 063 public static final Months NINE = new Months(9); 064 /** Constant representing ten months. */ 065 public static final Months TEN = new Months(10); 066 /** Constant representing eleven months. */ 067 public static final Months ELEVEN = new Months(11); 068 /** Constant representing twelve months. */ 069 public static final Months TWELVE = new Months(12); 070 /** Constant representing the maximum number of months that can be stored in this object. */ 071 public static final Months MAX_VALUE = new Months(Integer.MAX_VALUE); 072 /** Constant representing the minimum number of months that can be stored in this object. */ 073 public static final Months MIN_VALUE = new Months(Integer.MIN_VALUE); 074 075 /** The parser to use for this class. */ 076 private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.months()); 077 /** Serialization version. */ 078 private static final long serialVersionUID = 87525275727380867L; 079 080 //----------------------------------------------------------------------- 081 /** 082 * Obtains an instance of <code>Months</code> that may be cached. 083 * <code>Months</code> is immutable, so instances can be cached and shared. 084 * This factory method provides access to shared instances. 085 * 086 * @param months the number of months to obtain an instance for 087 * @return the instance of Months 088 */ 089 public static Months months(int months) { 090 switch (months) { 091 case 0: 092 return ZERO; 093 case 1: 094 return ONE; 095 case 2: 096 return TWO; 097 case 3: 098 return THREE; 099 case 4: 100 return FOUR; 101 case 5: 102 return FIVE; 103 case 6: 104 return SIX; 105 case 7: 106 return SEVEN; 107 case 8: 108 return EIGHT; 109 case 9: 110 return NINE; 111 case 10: 112 return TEN; 113 case 11: 114 return ELEVEN; 115 case 12: 116 return TWELVE; 117 case Integer.MAX_VALUE: 118 return MAX_VALUE; 119 case Integer.MIN_VALUE: 120 return MIN_VALUE; 121 default: 122 return new Months(months); 123 } 124 } 125 126 //----------------------------------------------------------------------- 127 /** 128 * Creates a <code>Months</code> representing the number of whole months 129 * between the two specified datetimes. This method corectly handles 130 * any daylight savings time changes that may occur during the interval. 131 * 132 * @param start the start instant, must not be null 133 * @param end the end instant, must not be null 134 * @return the period in months 135 * @throws IllegalArgumentException if the instants are null or invalid 136 */ 137 public static Months monthsBetween(ReadableInstant start, ReadableInstant end) { 138 int amount = BaseSingleFieldPeriod.between(start, end, DurationFieldType.months()); 139 return Months.months(amount); 140 } 141 142 /** 143 * Creates a <code>Months</code> representing the number of whole months 144 * between the two specified partial datetimes. 145 * <p> 146 * The two partials must contain the same fields, for example you can specify 147 * two <code>LocalDate</code> objects. 148 * 149 * @param start the start partial date, must not be null 150 * @param end the end partial date, must not be null 151 * @return the period in months 152 * @throws IllegalArgumentException if the partials are null or invalid 153 */ 154 public static Months monthsBetween(ReadablePartial start, ReadablePartial end) { 155 if (start instanceof LocalDate && end instanceof LocalDate) { 156 Chronology chrono = DateTimeUtils.getChronology(start.getChronology()); 157 int months = chrono.months().getDifference( 158 ((LocalDate) end).getLocalMillis(), ((LocalDate) start).getLocalMillis()); 159 return Months.months(months); 160 } 161 int amount = BaseSingleFieldPeriod.between(start, end, ZERO); 162 return Months.months(amount); 163 } 164 165 /** 166 * Creates a <code>Months</code> representing the number of whole months 167 * in the specified interval. This method corectly handles any daylight 168 * savings time changes that may occur during the interval. 169 * 170 * @param interval the interval to extract months from, null returns zero 171 * @return the period in months 172 * @throws IllegalArgumentException if the partials are null or invalid 173 */ 174 public static Months monthsIn(ReadableInterval interval) { 175 if (interval == null) { 176 return Months.ZERO; 177 } 178 int amount = BaseSingleFieldPeriod.between(interval.getStart(), interval.getEnd(), DurationFieldType.months()); 179 return Months.months(amount); 180 } 181 182 /** 183 * Creates a new <code>Months</code> by parsing a string in the ISO8601 format 'PnM'. 184 * <p> 185 * The parse will accept the full ISO syntax of PnYnMnWnDTnHnMnS however only the 186 * months component may be non-zero. If any other component is non-zero, an exception 187 * will be thrown. 188 * 189 * @param periodStr the period string, null returns zero 190 * @return the period in months 191 * @throws IllegalArgumentException if the string format is invalid 192 */ 193 @FromString 194 public static Months parseMonths(String periodStr) { 195 if (periodStr == null) { 196 return Months.ZERO; 197 } 198 Period p = PARSER.parsePeriod(periodStr); 199 return Months.months(p.getMonths()); 200 } 201 202 //----------------------------------------------------------------------- 203 /** 204 * Creates a new instance representing a number of months. 205 * You should consider using the factory method {@link #months(int)} 206 * instead of the constructor. 207 * 208 * @param months the number of months to represent 209 */ 210 private Months(int months) { 211 super(months); 212 } 213 214 /** 215 * Resolves singletons. 216 * 217 * @return the singleton instance 218 */ 219 private Object readResolve() { 220 return Months.months(getValue()); 221 } 222 223 //----------------------------------------------------------------------- 224 /** 225 * Gets the duration field type, which is <code>months</code>. 226 * 227 * @return the period type 228 */ 229 public DurationFieldType getFieldType() { 230 return DurationFieldType.months(); 231 } 232 233 /** 234 * Gets the period type, which is <code>months</code>. 235 * 236 * @return the period type 237 */ 238 public PeriodType getPeriodType() { 239 return PeriodType.months(); 240 } 241 242 //----------------------------------------------------------------------- 243 /** 244 * Gets the number of months that this period represents. 245 * 246 * @return the number of months in the period 247 */ 248 public int getMonths() { 249 return getValue(); 250 } 251 252 //----------------------------------------------------------------------- 253 /** 254 * Returns a new instance with the specified number of months added. 255 * <p> 256 * This instance is immutable and unaffected by this method call. 257 * 258 * @param months the amount of months to add, may be negative 259 * @return the new period plus the specified number of months 260 * @throws ArithmeticException if the result overflows an int 261 */ 262 public Months plus(int months) { 263 if (months == 0) { 264 return this; 265 } 266 return Months.months(FieldUtils.safeAdd(getValue(), months)); 267 } 268 269 /** 270 * Returns a new instance with the specified number of months added. 271 * <p> 272 * This instance is immutable and unaffected by this method call. 273 * 274 * @param months the amount of months to add, may be negative, null means zero 275 * @return the new period plus the specified number of months 276 * @throws ArithmeticException if the result overflows an int 277 */ 278 public Months plus(Months months) { 279 if (months == null) { 280 return this; 281 } 282 return plus(months.getValue()); 283 } 284 285 //----------------------------------------------------------------------- 286 /** 287 * Returns a new instance with the specified number of months taken away. 288 * <p> 289 * This instance is immutable and unaffected by this method call. 290 * 291 * @param months the amount of months to take away, may be negative 292 * @return the new period minus the specified number of months 293 * @throws ArithmeticException if the result overflows an int 294 */ 295 public Months minus(int months) { 296 return plus(FieldUtils.safeNegate(months)); 297 } 298 299 /** 300 * Returns a new instance with the specified number of months taken away. 301 * <p> 302 * This instance is immutable and unaffected by this method call. 303 * 304 * @param months the amount of months to take away, may be negative, null means zero 305 * @return the new period minus the specified number of months 306 * @throws ArithmeticException if the result overflows an int 307 */ 308 public Months minus(Months months) { 309 if (months == null) { 310 return this; 311 } 312 return minus(months.getValue()); 313 } 314 315 //----------------------------------------------------------------------- 316 /** 317 * Returns a new instance with the months multiplied by the specified scalar. 318 * <p> 319 * This instance is immutable and unaffected by this method call. 320 * 321 * @param scalar the amount to multiply by, may be negative 322 * @return the new period multiplied by the specified scalar 323 * @throws ArithmeticException if the result overflows an int 324 */ 325 public Months multipliedBy(int scalar) { 326 return Months.months(FieldUtils.safeMultiply(getValue(), scalar)); 327 } 328 329 /** 330 * Returns a new instance with the months divided by the specified divisor. 331 * The calculation uses integer division, thus 3 divided by 2 is 1. 332 * <p> 333 * This instance is immutable and unaffected by this method call. 334 * 335 * @param divisor the amount to divide by, may be negative 336 * @return the new period divided by the specified divisor 337 * @throws ArithmeticException if the divisor is zero 338 */ 339 public Months dividedBy(int divisor) { 340 if (divisor == 1) { 341 return this; 342 } 343 return Months.months(getValue() / divisor); 344 } 345 346 //----------------------------------------------------------------------- 347 /** 348 * Returns a new instance with the months value negated. 349 * 350 * @return the new period with a negated value 351 * @throws ArithmeticException if the result overflows an int 352 */ 353 public Months negated() { 354 return Months.months(FieldUtils.safeNegate(getValue())); 355 } 356 357 //----------------------------------------------------------------------- 358 /** 359 * Is this months instance greater than the specified number of months. 360 * 361 * @param other the other period, null means zero 362 * @return true if this months instance is greater than the specified one 363 */ 364 public boolean isGreaterThan(Months other) { 365 if (other == null) { 366 return getValue() > 0; 367 } 368 return getValue() > other.getValue(); 369 } 370 371 /** 372 * Is this months instance less than the specified number of months. 373 * 374 * @param other the other period, null means zero 375 * @return true if this months instance is less than the specified one 376 */ 377 public boolean isLessThan(Months other) { 378 if (other == null) { 379 return getValue() < 0; 380 } 381 return getValue() < other.getValue(); 382 } 383 384 //----------------------------------------------------------------------- 385 /** 386 * Gets this instance as a String in the ISO8601 duration format. 387 * <p> 388 * For example, "P4M" represents 4 months. 389 * 390 * @return the value as an ISO8601 string 391 */ 392 @ToString 393 public String toString() { 394 return "P" + String.valueOf(getValue()) + "M"; 395 } 396 397 }