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