View Javadoc

1   /*
2    *  Copyright 2001-2010 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.joda.time;
17  
18  import org.joda.convert.FromString;
19  import org.joda.convert.ToString;
20  import org.joda.time.base.BaseSingleFieldPeriod;
21  import org.joda.time.field.FieldUtils;
22  import org.joda.time.format.ISOPeriodFormat;
23  import org.joda.time.format.PeriodFormatter;
24  
25  /**
26   * An immutable time period representing a number of months.
27   * <p>
28   * <code>Months</code> is an immutable period that can only store months.
29   * It does not store years, days or hours for example. As such it is a
30   * type-safe way of representing a number of months in an application.
31   * <p>
32   * The number of months is set in the constructor, and may be queried using
33   * <code>getMonths()</code>. Basic mathematical operations are provided -
34   * <code>plus()</code>, <code>minus()</code>, <code>multipliedBy()</code> and
35   * <code>dividedBy()</code>.
36   * <p>
37   * <code>Months</code> is thread-safe and immutable.
38   *
39   * @author Stephen Colebourne
40   * @since 1.4
41   */
42  public final class Months extends BaseSingleFieldPeriod {
43  
44      /** Constant representing zero months. */
45      public static final Months ZERO = new Months(0);
46      /** Constant representing one month. */
47      public static final Months ONE = new Months(1);
48      /** Constant representing two months. */
49      public static final Months TWO = new Months(2);
50      /** Constant representing three months. */
51      public static final Months THREE = new Months(3);
52      /** Constant representing four months. */
53      public static final Months FOUR = new Months(4);
54      /** Constant representing five months. */
55      public static final Months FIVE = new Months(5);
56      /** Constant representing six months. */
57      public static final Months SIX = new Months(6);
58      /** Constant representing seven months. */
59      public static final Months SEVEN = new Months(7);
60      /** Constant representing eight months. */
61      public static final Months EIGHT = new Months(8);
62      /** Constant representing nine months. */
63      public static final Months NINE = new Months(9);
64      /** Constant representing ten months. */
65      public static final Months TEN = new Months(10);
66      /** Constant representing eleven months. */
67      public static final Months ELEVEN = new Months(11);
68      /** Constant representing twelve months. */
69      public static final Months TWELVE = new Months(12);
70      /** Constant representing the maximum number of months that can be stored in this object. */
71      public static final Months MAX_VALUE = new Months(Integer.MAX_VALUE);
72      /** Constant representing the minimum number of months that can be stored in this object. */
73      public static final Months MIN_VALUE = new Months(Integer.MIN_VALUE);
74  
75      /** The parser to use for this class. */
76      private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.months());
77      /** Serialization version. */
78      private static final long serialVersionUID = 87525275727380867L;
79  
80      //-----------------------------------------------------------------------
81      /**
82       * Obtains an instance of <code>Months</code> that may be cached.
83       * <code>Months</code> is immutable, so instances can be cached and shared.
84       * This factory method provides access to shared instances.
85       *
86       * @param months  the number of months to obtain an instance for
87       * @return the instance of Months
88       */
89      public static Months months(int months) {
90          switch (months) {
91              case 0:
92                  return ZERO;
93              case 1:
94                  return ONE;
95              case 2:
96                  return TWO;
97              case 3:
98                  return THREE;
99              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 }