1 | /* |
2 | * Copyright 2001-2006 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.time.base.BaseSingleFieldPeriod; |
19 | import org.joda.time.field.FieldUtils; |
20 | import org.joda.time.format.ISOPeriodFormat; |
21 | import org.joda.time.format.PeriodFormatter; |
22 | |
23 | /** |
24 | * An immutable time period representing a number of months. |
25 | * <p> |
26 | * <code>Months</code> is an immutable period that can only store months. |
27 | * It does not store years, days or hours for example. As such it is a |
28 | * type-safe way of representing a number of months in an application. |
29 | * <p> |
30 | * The number of months is set in the constructor, and may be queried using |
31 | * <code>getMonths()</code>. Basic mathematical operations are provided - |
32 | * <code>plus()</code>, <code>minus()</code>, <code>multipliedBy()</code> and |
33 | * <code>dividedBy()</code>. |
34 | * <p> |
35 | * <code>Months</code> is thread-safe and immutable. |
36 | * |
37 | * @author Stephen Colebourne |
38 | * @since 1.4 |
39 | */ |
40 | public final class Months extends BaseSingleFieldPeriod { |
41 | |
42 | /** Constant representing zero months. */ |
43 | public static final Months ZERO = new Months(0); |
44 | /** Constant representing one day. */ |
45 | public static final Months ONE = new Months(1); |
46 | /** Constant representing two months. */ |
47 | public static final Months TWO = new Months(2); |
48 | /** Constant representing three months. */ |
49 | public static final Months THREE = new Months(3); |
50 | /** Constant representing four months. */ |
51 | public static final Months FOUR = new Months(4); |
52 | /** Constant representing five months. */ |
53 | public static final Months FIVE = new Months(5); |
54 | /** Constant representing six months. */ |
55 | public static final Months SIX = new Months(6); |
56 | /** Constant representing seven months. */ |
57 | public static final Months SEVEN = new Months(7); |
58 | /** Constant representing eight months. */ |
59 | public static final Months EIGHT = new Months(8); |
60 | /** Constant representing nine months. */ |
61 | public static final Months NINE = new Months(9); |
62 | /** Constant representing ten months. */ |
63 | public static final Months TEN = new Months(10); |
64 | /** Constant representing eleven months. */ |
65 | public static final Months ELEVEN = new Months(11); |
66 | /** Constant representing twelve months. */ |
67 | public static final Months TWELVE = new Months(12); |
68 | /** Constant representing the maximum number of months that can be stored in this object. */ |
69 | public static final Months MAX_VALUE = new Months(Integer.MAX_VALUE); |
70 | /** Constant representing the minimum number of months that can be stored in this object. */ |
71 | public static final Months MIN_VALUE = new Months(Integer.MIN_VALUE); |
72 | |
73 | /** The paser to use for this class. */ |
74 | private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.months()); |
75 | /** Serialization version. */ |
76 | private static final long serialVersionUID = 87525275727380867L; |
77 | |
78 | //----------------------------------------------------------------------- |
79 | /** |
80 | * Obtains an instance of <code>Months</code> that may be cached. |
81 | * <code>Months</code> is immutable, so instances can be cached and shared. |
82 | * This factory method provides access to shared instances. |
83 | * |
84 | * @param months the number of months to obtain an instance for |
85 | * @return the instance of Months |
86 | */ |
87 | public static Months months(int months) { |
88 | switch (months) { |
89 | case 0: |
90 | return ZERO; |
91 | case 1: |
92 | return ONE; |
93 | case 2: |
94 | return TWO; |
95 | case 3: |
96 | return THREE; |
97 | case 4: |
98 | return FOUR; |
99 | 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 | } |