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 years.
027     * <p>
028     * <code>Years</code> is an immutable period that can only store years.
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 years in an application.
031     * <p>
032     * The number of years is set in the constructor, and may be queried using
033     * <code>getYears()</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>Years</code> is thread-safe and immutable.
038     *
039     * @author Stephen Colebourne
040     * @since 1.4
041     */
042    public final class Years extends BaseSingleFieldPeriod {
043    
044        /** Constant representing zero years. */
045        public static final Years ZERO = new Years(0);
046        /** Constant representing one year. */
047        public static final Years ONE = new Years(1);
048        /** Constant representing two years. */
049        public static final Years TWO = new Years(2);
050        /** Constant representing three years. */
051        public static final Years THREE = new Years(3);
052        /** Constant representing the maximum number of years that can be stored in this object. */
053        public static final Years MAX_VALUE = new Years(Integer.MAX_VALUE);
054        /** Constant representing the minimum number of years that can be stored in this object. */
055        public static final Years MIN_VALUE = new Years(Integer.MIN_VALUE);
056    
057        /** The paser to use for this class. */
058        private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.years());
059        /** Serialization version. */
060        private static final long serialVersionUID = 87525275727380868L;
061    
062        //-----------------------------------------------------------------------
063        /**
064         * Obtains an instance of <code>Years</code> that may be cached.
065         * <code>Years</code> is immutable, so instances can be cached and shared.
066         * This factory method provides access to shared instances.
067         *
068         * @param years  the number of years to obtain an instance for
069         * @return the instance of Years
070         */
071        public static Years years(int years) {
072            switch (years) {
073                case 0:
074                    return ZERO;
075                case 1:
076                    return ONE;
077                case 2:
078                    return TWO;
079                case 3:
080                    return THREE;
081                case Integer.MAX_VALUE:
082                    return MAX_VALUE;
083                case Integer.MIN_VALUE:
084                    return MIN_VALUE;
085                default:
086                    return new Years(years);
087            }
088        }
089    
090        //-----------------------------------------------------------------------
091        /**
092         * Creates a <code>Years</code> representing the number of whole years
093         * between the two specified datetimes. This method corectly handles
094         * any daylight savings time changes that may occur during the interval.
095         *
096         * @param start  the start instant, must not be null
097         * @param end  the end instant, must not be null
098         * @return the period in years
099         * @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    }