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 */
016package org.joda.time;
017
018import org.joda.time.base.BaseSingleFieldPeriod;
019import org.joda.time.field.FieldUtils;
020import org.joda.time.format.ISOPeriodFormat;
021import 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 */
040public 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}