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 }