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