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