1 /*
2 * Copyright 2001-2013 Stephen Colebourne
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.joda.time;
17
18 import org.joda.convert.FromString;
19 import org.joda.convert.ToString;
20 import org.joda.time.base.BaseSingleFieldPeriod;
21 import org.joda.time.field.FieldUtils;
22 import org.joda.time.format.ISOPeriodFormat;
23 import org.joda.time.format.PeriodFormatter;
24
25 /**
26 * An immutable time period representing a number of years.
27 * <p>
28 * <code>Years</code> is an immutable period that can only store years.
29 * It does not store months, days or hours for example. As such it is a
30 * type-safe way of representing a number of years in an application.
31 * <p>
32 * The number of years is set in the constructor, and may be queried using
33 * <code>getYears()</code>. Basic mathematical operations are provided -
34 * <code>plus()</code>, <code>minus()</code>, <code>multipliedBy()</code> and
35 * <code>dividedBy()</code>.
36 * <p>
37 * <code>Years</code> is thread-safe and immutable.
38 *
39 * @author Stephen Colebourne
40 * @since 1.4
41 */
42 public final class Years extends BaseSingleFieldPeriod {
43
44 /** Constant representing zero years. */
45 public static final Years ZERO = new Years(0);
46 /** Constant representing one year. */
47 public static final Years ONE = new Years(1);
48 /** Constant representing two years. */
49 public static final Years TWO = new Years(2);
50 /** Constant representing three years. */
51 public static final Years THREE = new Years(3);
52 /** Constant representing the maximum number of years that can be stored in this object. */
53 public static final Years MAX_VALUE = new Years(Integer.MAX_VALUE);
54 /** Constant representing the minimum number of years that can be stored in this object. */
55 public static final Years MIN_VALUE = new Years(Integer.MIN_VALUE);
56
57 /** The paser to use for this class. */
58 private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.years());
59 /** Serialization version. */
60 private static final long serialVersionUID = 87525275727380868L;
61
62 //-----------------------------------------------------------------------
63 /**
64 * Obtains an instance of <code>Years</code> that may be cached.
65 * <code>Years</code> is immutable, so instances can be cached and shared.
66 * This factory method provides access to shared instances.
67 *
68 * @param years the number of years to obtain an instance for
69 * @return the instance of Years
70 */
71 public static Years years(int years) {
72 switch (years) {
73 case 0:
74 return ZERO;
75 case 1:
76 return ONE;
77 case 2:
78 return TWO;
79 case 3:
80 return THREE;
81 case Integer.MAX_VALUE:
82 return MAX_VALUE;
83 case Integer.MIN_VALUE:
84 return MIN_VALUE;
85 default:
86 return new Years(years);
87 }
88 }
89
90 //-----------------------------------------------------------------------
91 /**
92 * Creates a <code>Years</code> representing the number of whole years
93 * between the two specified datetimes. This method corectly handles
94 * any daylight savings time changes that may occur during the interval.
95 *
96 * @param start the start instant, must not be null
97 * @param end the end instant, must not be null
98 * @return the period in years
99 * @throws IllegalArgumentException if the instants are null or invalid
100 */
101 public static Years yearsBetween(ReadableInstant start, ReadableInstant end) {
102 int amount = BaseSingleFieldPeriod.between(start, end, DurationFieldType.years());
103 return Years.years(amount);
104 }
105
106 /**
107 * Creates a <code>Years</code> representing the number of whole years
108 * between the two specified partial datetimes.
109 * <p>
110 * The two partials must contain the same fields, for example you can specify
111 * two <code>LocalDate</code> objects.
112 *
113 * @param start the start partial date, must not be null
114 * @param end the end partial date, must not be null
115 * @return the period in years
116 * @throws IllegalArgumentException if the partials are null or invalid
117 */
118 public static Years yearsBetween(ReadablePartial start, ReadablePartial end) {
119 if (start instanceof LocalDate && end instanceof LocalDate) {
120 Chronology chrono = DateTimeUtils.getChronology(start.getChronology());
121 int years = chrono.years().getDifference(
122 ((LocalDate) end).getLocalMillis(), ((LocalDate) start).getLocalMillis());
123 return Years.years(years);
124 }
125 int amount = BaseSingleFieldPeriod.between(start, end, ZERO);
126 return Years.years(amount);
127 }
128
129 /**
130 * Creates a <code>Years</code> representing the number of whole years
131 * in the specified interval. This method corectly handles any daylight
132 * savings time changes that may occur during the interval.
133 *
134 * @param interval the interval to extract years from, null returns zero
135 * @return the period in years
136 * @throws IllegalArgumentException if the partials are null or invalid
137 */
138 public static Years yearsIn(ReadableInterval interval) {
139 if (interval == null) {
140 return Years.ZERO;
141 }
142 int amount = BaseSingleFieldPeriod.between(interval.getStart(), interval.getEnd(), DurationFieldType.years());
143 return Years.years(amount);
144 }
145
146 /**
147 * Creates a new <code>Years</code> by parsing a string in the ISO8601 format 'PnY'.
148 * <p>
149 * The parse will accept the full ISO syntax of PnYnMnWnDTnHnMnS however only the
150 * years component may be non-zero. If any other component is non-zero, an exception
151 * will be thrown.
152 *
153 * @param periodStr the period string, null returns zero
154 * @return the period in years
155 * @throws IllegalArgumentException if the string format is invalid
156 */
157 @FromString
158 public static Years parseYears(String periodStr) {
159 if (periodStr == null) {
160 return Years.ZERO;
161 }
162 Period p = PARSER.parsePeriod(periodStr);
163 return Years.years(p.getYears());
164 }
165
166 //-----------------------------------------------------------------------
167 /**
168 * Creates a new instance representing a number of years.
169 * You should consider using the factory method {@link #years(int)}
170 * instead of the constructor.
171 *
172 * @param years the number of years to represent
173 */
174 private Years(int years) {
175 super(years);
176 }
177
178 /**
179 * Resolves singletons.
180 *
181 * @return the singleton instance
182 */
183 private Object readResolve() {
184 return Years.years(getValue());
185 }
186
187 //-----------------------------------------------------------------------
188 /**
189 * Gets the duration field type, which is <code>years</code>.
190 *
191 * @return the period type
192 */
193 public DurationFieldType getFieldType() {
194 return DurationFieldType.years();
195 }
196
197 /**
198 * Gets the period type, which is <code>years</code>.
199 *
200 * @return the period type
201 */
202 public PeriodType getPeriodType() {
203 return PeriodType.years();
204 }
205
206 //-----------------------------------------------------------------------
207 /**
208 * Gets the number of years that this period represents.
209 *
210 * @return the number of years in the period
211 */
212 public int getYears() {
213 return getValue();
214 }
215
216 //-----------------------------------------------------------------------
217 /**
218 * Returns a new instance with the specified number of years added.
219 * <p>
220 * This instance is immutable and unaffected by this method call.
221 *
222 * @param years the amount of years to add, may be negative
223 * @return the new period plus the specified number of years
224 * @throws ArithmeticException if the result overflows an int
225 */
226 public Years plus(int years) {
227 if (years == 0) {
228 return this;
229 }
230 return Years.years(FieldUtils.safeAdd(getValue(), years));
231 }
232
233 /**
234 * Returns a new instance with the specified number of years added.
235 * <p>
236 * This instance is immutable and unaffected by this method call.
237 *
238 * @param years the amount of years to add, may be negative, null means zero
239 * @return the new period plus the specified number of years
240 * @throws ArithmeticException if the result overflows an int
241 */
242 public Years plus(Years years) {
243 if (years == null) {
244 return this;
245 }
246 return plus(years.getValue());
247 }
248
249 //-----------------------------------------------------------------------
250 /**
251 * Returns a new instance with the specified number of years taken away.
252 * <p>
253 * This instance is immutable and unaffected by this method call.
254 *
255 * @param years the amount of years to take away, may be negative
256 * @return the new period minus the specified number of years
257 * @throws ArithmeticException if the result overflows an int
258 */
259 public Years minus(int years) {
260 return plus(FieldUtils.safeNegate(years));
261 }
262
263 /**
264 * Returns a new instance with the specified number of years taken away.
265 * <p>
266 * This instance is immutable and unaffected by this method call.
267 *
268 * @param years the amount of years to take away, may be negative, null means zero
269 * @return the new period minus the specified number of years
270 * @throws ArithmeticException if the result overflows an int
271 */
272 public Years minus(Years years) {
273 if (years == null) {
274 return this;
275 }
276 return minus(years.getValue());
277 }
278
279 //-----------------------------------------------------------------------
280 /**
281 * Returns a new instance with the years multiplied by the specified scalar.
282 * <p>
283 * This instance is immutable and unaffected by this method call.
284 *
285 * @param scalar the amount to multiply by, may be negative
286 * @return the new period multiplied by the specified scalar
287 * @throws ArithmeticException if the result overflows an int
288 */
289 public Years multipliedBy(int scalar) {
290 return Years.years(FieldUtils.safeMultiply(getValue(), scalar));
291 }
292
293 /**
294 * Returns a new instance with the years divided by the specified divisor.
295 * The calculation uses integer division, thus 3 divided by 2 is 1.
296 * <p>
297 * This instance is immutable and unaffected by this method call.
298 *
299 * @param divisor the amount to divide by, may be negative
300 * @return the new period divided by the specified divisor
301 * @throws ArithmeticException if the divisor is zero
302 */
303 public Years dividedBy(int divisor) {
304 if (divisor == 1) {
305 return this;
306 }
307 return Years.years(getValue() / divisor);
308 }
309
310 //-----------------------------------------------------------------------
311 /**
312 * Returns a new instance with the years value negated.
313 *
314 * @return the new period with a negated value
315 * @throws ArithmeticException if the result overflows an int
316 */
317 public Years negated() {
318 return Years.years(FieldUtils.safeNegate(getValue()));
319 }
320
321 //-----------------------------------------------------------------------
322 /**
323 * Is this years instance greater than the specified number of years.
324 *
325 * @param other the other period, null means zero
326 * @return true if this years instance is greater than the specified one
327 */
328 public boolean isGreaterThan(Years other) {
329 if (other == null) {
330 return getValue() > 0;
331 }
332 return getValue() > other.getValue();
333 }
334
335 /**
336 * Is this years instance less than the specified number of years.
337 *
338 * @param other the other period, null means zero
339 * @return true if this years instance is less than the specified one
340 */
341 public boolean isLessThan(Years other) {
342 if (other == null) {
343 return getValue() < 0;
344 }
345 return getValue() < other.getValue();
346 }
347
348 //-----------------------------------------------------------------------
349 /**
350 * Gets this instance as a String in the ISO8601 duration format.
351 * <p>
352 * For example, "P4Y" represents 4 years.
353 *
354 * @return the value as an ISO8601 string
355 */
356 @ToString
357 public String toString() {
358 return "P" + String.valueOf(getValue()) + "Y";
359 }
360
361 }