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