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