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 java.io.Serializable;
019 import java.util.Calendar;
020 import java.util.Date;
021 import java.util.Locale;
022
023 import org.joda.convert.FromString;
024 import org.joda.convert.ToString;
025 import org.joda.time.base.BasePartial;
026 import org.joda.time.chrono.ISOChronology;
027 import org.joda.time.field.AbstractPartialFieldProperty;
028 import org.joda.time.field.FieldUtils;
029 import org.joda.time.format.DateTimeFormat;
030 import org.joda.time.format.DateTimeFormatter;
031 import org.joda.time.format.ISODateTimeFormat;
032
033 /**
034 * YearMonth is an immutable partial supporting the year and monthOfYear fields.
035 * <p>
036 * NOTE: This class only supports the two fields listed above.
037 * It is impossible to query any other fields, such as dayOfWeek or centuryOfEra.
038 * <p>
039 * Calculations on YearMonth are performed using a {@link Chronology}.
040 * This chronology is set to be in the UTC time zone for all calculations.
041 * <p>
042 * One use case for this class is to store a credit card expiry date, as that only
043 * references the year and month.
044 * This class can be used as the gYearMonth type in XML Schema.
045 * <p>
046 * Each individual field can be queried in two ways:
047 * <ul>
048 * <li><code>getMonthOfYear()</code>
049 * <li><code>monthOfYear().get()</code>
050 * </ul>
051 * The second technique also provides access to other useful methods on the
052 * field:
053 * <ul>
054 * <li>numeric value - <code>monthOfYear().get()</code>
055 * <li>text value - <code>monthOfYear().getAsText()</code>
056 * <li>short text value - <code>monthOfYear().getAsShortText()</code>
057 * <li>maximum/minimum values - <code>monthOfYear().getMaximumValue()</code>
058 * <li>add/subtract - <code>monthOfYear().addToCopy()</code>
059 * <li>set - <code>monthOfYear().setCopy()</code>
060 * </ul>
061 * <p>
062 * YearMonth is thread-safe and immutable, provided that the Chronology is as well.
063 * All standard Chronology classes supplied are thread-safe and immutable.
064 *
065 * @author Stephen Colebourne
066 * @since 2.0
067 */
068 public final class YearMonth
069 extends BasePartial
070 implements ReadablePartial, Serializable {
071
072 /** Serialization version */
073 private static final long serialVersionUID = 797544782896179L;
074 /** The singleton set of field types */
075 private static final DateTimeFieldType[] FIELD_TYPES = new DateTimeFieldType[] {
076 DateTimeFieldType.year(),
077 DateTimeFieldType.monthOfYear(),
078 };
079
080 /** The index of the year field in the field array */
081 public static final int YEAR = 0;
082 /** The index of the monthOfYear field in the field array */
083 public static final int MONTH_OF_YEAR = 1;
084
085 //-----------------------------------------------------------------------
086 /**
087 * Obtains a {@code YearMonth} set to the current system millisecond time
088 * using <code>ISOChronology</code> in the default time zone.
089 * The resulting object does not use the zone.
090 *
091 * @return the current year-month, not null
092 * @since 2.0
093 */
094 public static YearMonth now() {
095 return new YearMonth();
096 }
097
098 /**
099 * Obtains a {@code YearMonth} set to the current system millisecond time
100 * using <code>ISOChronology</code> in the specified time zone.
101 * The resulting object does not use the zone.
102 *
103 * @param zone the time zone, not null
104 * @return the current year-month, not null
105 * @since 2.0
106 */
107 public static YearMonth now(DateTimeZone zone) {
108 if (zone == null) {
109 throw new NullPointerException("Zone must not be null");
110 }
111 return new YearMonth(zone);
112 }
113
114 /**
115 * Obtains a {@code YearMonth} set to the current system millisecond time
116 * using the specified chronology.
117 * The resulting object does not use the zone.
118 *
119 * @param chronology the chronology, not null
120 * @return the current year-month, not null
121 * @since 2.0
122 */
123 public static YearMonth now(Chronology chronology) {
124 if (chronology == null) {
125 throw new NullPointerException("Chronology must not be null");
126 }
127 return new YearMonth(chronology);
128 }
129
130 //-----------------------------------------------------------------------
131 /**
132 * Parses a {@code YearMonth} from the specified string.
133 * <p>
134 * This uses {@link ISODateTimeFormat#localDateParser()}.
135 *
136 * @param str the string to parse, not null
137 * @since 2.0
138 */
139 @FromString
140 public static YearMonth parse(String str) {
141 return parse(str, ISODateTimeFormat.localDateParser());
142 }
143
144 /**
145 * Parses a {@code YearMonth} from the specified string using a formatter.
146 *
147 * @param str the string to parse, not null
148 * @param formatter the formatter to use, not null
149 * @since 2.0
150 */
151 public static YearMonth parse(String str, DateTimeFormatter formatter) {
152 LocalDate date = formatter.parseLocalDate(str);
153 return new YearMonth(date.getYear(), date.getMonthOfYear());
154 }
155
156 //-----------------------------------------------------------------------
157 /**
158 * Constructs a YearMonth from a <code>java.util.Calendar</code>
159 * using exactly the same field values avoiding any time zone effects.
160 * <p>
161 * Each field is queried from the Calendar and assigned to the YearMonth.
162 * <p>
163 * This factory method ignores the type of the calendar and always
164 * creates a YearMonth with ISO chronology. It is expected that you
165 * will only pass in instances of <code>GregorianCalendar</code> however
166 * this is not validated.
167 *
168 * @param calendar the Calendar to extract fields from
169 * @return the created YearMonth, never null
170 * @throws IllegalArgumentException if the calendar is null
171 * @throws IllegalArgumentException if the year or month is invalid for the ISO chronology
172 */
173 public static YearMonth fromCalendarFields(Calendar calendar) {
174 if (calendar == null) {
175 throw new IllegalArgumentException("The calendar must not be null");
176 }
177 return new YearMonth(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
178 }
179
180 /**
181 * Constructs a YearMonth from a <code>java.util.Date</code>
182 * using exactly the same field values avoiding any time zone effects.
183 * <p>
184 * Each field is queried from the Date and assigned to the YearMonth.
185 * <p>
186 * This factory method always creates a YearMonth with ISO chronology.
187 *
188 * @param date the Date to extract fields from
189 * @return the created YearMonth, never null
190 * @throws IllegalArgumentException if the calendar is null
191 * @throws IllegalArgumentException if the year or month is invalid for the ISO chronology
192 */
193 @SuppressWarnings("deprecation")
194 public static YearMonth fromDateFields(Date date) {
195 if (date == null) {
196 throw new IllegalArgumentException("The date must not be null");
197 }
198 return new YearMonth(date.getYear() + 1900, date.getMonth() + 1);
199 }
200
201 //-----------------------------------------------------------------------
202 /**
203 * Constructs a YearMonth with the current year-month, using ISOChronology in
204 * the default zone to extract the fields.
205 * <p>
206 * The constructor uses the default time zone, resulting in the local time
207 * being initialised. Once the constructor is complete, all further calculations
208 * are performed without reference to a time-zone (by switching to UTC).
209 *
210 * @see #now()
211 */
212 public YearMonth() {
213 super();
214 }
215
216 /**
217 * Constructs a YearMonth with the current year-month, using ISOChronology in
218 * the specified zone to extract the fields.
219 * <p>
220 * The constructor uses the specified time zone to obtain the current year-month.
221 * Once the constructor is complete, all further calculations
222 * are performed without reference to a time-zone (by switching to UTC).
223 *
224 * @param zone the zone to use, null means default zone
225 * @see #now(DateTimeZone)
226 */
227 public YearMonth(DateTimeZone zone) {
228 super(ISOChronology.getInstance(zone));
229 }
230
231 /**
232 * Constructs a YearMonth with the current year-month, using the specified chronology
233 * and zone to extract the fields.
234 * <p>
235 * The constructor uses the time zone of the chronology specified.
236 * Once the constructor is complete, all further calculations are performed
237 * without reference to a time-zone (by switching to UTC).
238 *
239 * @param chronology the chronology, null means ISOChronology in the default zone
240 * @see #now(Chronology)
241 */
242 public YearMonth(Chronology chronology) {
243 super(chronology);
244 }
245
246 /**
247 * Constructs a YearMonth extracting the partial fields from the specified
248 * milliseconds using the ISOChronology in the default zone.
249 * <p>
250 * The constructor uses the default time zone, resulting in the local time
251 * being initialised. Once the constructor is complete, all further calculations
252 * are performed without reference to a time-zone (by switching to UTC).
253 *
254 * @param instant the milliseconds from 1970-01-01T00:00:00Z
255 */
256 public YearMonth(long instant) {
257 super(instant);
258 }
259
260 /**
261 * Constructs a YearMonth extracting the partial fields from the specified
262 * milliseconds using the chronology provided.
263 * <p>
264 * The constructor uses the time zone of the chronology specified.
265 * Once the constructor is complete, all further calculations are performed
266 * without reference to a time-zone (by switching to UTC).
267 *
268 * @param instant the milliseconds from 1970-01-01T00:00:00Z
269 * @param chronology the chronology, null means ISOChronology in the default zone
270 */
271 public YearMonth(long instant, Chronology chronology) {
272 super(instant, chronology);
273 }
274
275 /**
276 * Constructs a YearMonth from an Object that represents some form of time.
277 * <p>
278 * The recognised object types are defined in
279 * {@link org.joda.time.convert.ConverterManager ConverterManager} and
280 * include ReadableInstant, String, Calendar and Date.
281 * The String formats are described by {@link ISODateTimeFormat#localDateParser()}.
282 * <p>
283 * The chronology used will be derived from the object, defaulting to ISO.
284 *
285 * @param instant the date-time object, null means now
286 * @throws IllegalArgumentException if the instant is invalid
287 */
288 public YearMonth(Object instant) {
289 super(instant, null, ISODateTimeFormat.localDateParser());
290 }
291
292 /**
293 * Constructs a YearMonth from an Object that represents some form of time,
294 * using the specified chronology.
295 * <p>
296 * The recognised object types are defined in
297 * {@link org.joda.time.convert.ConverterManager ConverterManager} and
298 * include ReadableInstant, String, Calendar and Date.
299 * The String formats are described by {@link ISODateTimeFormat#localDateParser()}.
300 * <p>
301 * The constructor uses the time zone of the chronology specified.
302 * Once the constructor is complete, all further calculations are performed
303 * without reference to a time-zone (by switching to UTC).
304 * The specified chronology overrides that of the object.
305 *
306 * @param instant the date-time object, null means now
307 * @param chronology the chronology, null means ISO default
308 * @throws IllegalArgumentException if the instant is invalid
309 */
310 public YearMonth(Object instant, Chronology chronology) {
311 super(instant, DateTimeUtils.getChronology(chronology), ISODateTimeFormat.localDateParser());
312 }
313
314 /**
315 * Constructs a YearMonth with specified year and month
316 * using <code>ISOChronology</code>.
317 * <p>
318 * The constructor uses the no time zone initialising the fields as provided.
319 * Once the constructor is complete, all further calculations
320 * are performed without reference to a time-zone (by switching to UTC).
321 *
322 * @param year the year
323 * @param monthOfYear the month of the year
324 */
325 public YearMonth(int year, int monthOfYear) {
326 this(year, monthOfYear, null);
327 }
328
329 /**
330 * Constructs an instance set to the specified year and month
331 * using the specified chronology, whose zone is ignored.
332 * <p>
333 * If the chronology is null, <code>ISOChronology</code> is used.
334 * <p>
335 * The constructor uses the time zone of the chronology specified.
336 * Once the constructor is complete, all further calculations are performed
337 * without reference to a time-zone (by switching to UTC).
338 *
339 * @param year the year
340 * @param monthOfYear the month of the year
341 * @param chronology the chronology, null means ISOChronology in the default zone
342 */
343 public YearMonth(int year, int monthOfYear, Chronology chronology) {
344 super(new int[] {year, monthOfYear}, chronology);
345 }
346
347 /**
348 * Constructs a YearMonth with chronology from this instance and new values.
349 *
350 * @param partial the partial to base this new instance on
351 * @param values the new set of values
352 */
353 YearMonth(YearMonth partial, int[] values) {
354 super(partial, values);
355 }
356
357 /**
358 * Constructs a YearMonth with values from this instance and a new chronology.
359 *
360 * @param partial the partial to base this new instance on
361 * @param chrono the new chronology
362 */
363 YearMonth(YearMonth partial, Chronology chrono) {
364 super(partial, chrono);
365 }
366
367 /**
368 * Handle broken serialization from other tools.
369 * @return the resolved object, not null
370 */
371 private Object readResolve() {
372 if (DateTimeZone.UTC.equals(getChronology().getZone()) == false) {
373 return new YearMonth(this, getChronology().withUTC());
374 }
375 return this;
376 }
377
378 //-----------------------------------------------------------------------
379 /**
380 * Gets the number of fields in this partial, which is two.
381 * The supported fields are Year and MonthOfYear.
382 * Note that only these fields may be queried.
383 *
384 * @return the field count, two
385 */
386 public int size() {
387 return 2;
388 }
389
390 /**
391 * Gets the field for a specific index in the chronology specified.
392 * <p>
393 * This method must not use any instance variables.
394 *
395 * @param index the index to retrieve
396 * @param chrono the chronology to use
397 * @return the field, never null
398 */
399 protected DateTimeField getField(int index, Chronology chrono) {
400 switch (index) {
401 case YEAR:
402 return chrono.year();
403 case MONTH_OF_YEAR:
404 return chrono.monthOfYear();
405 default:
406 throw new IndexOutOfBoundsException("Invalid index: " + index);
407 }
408 }
409
410 /**
411 * Gets the field type at the specified index.
412 *
413 * @param index the index to retrieve
414 * @return the field at the specified index, never null
415 * @throws IndexOutOfBoundsException if the index is invalid
416 */
417 public DateTimeFieldType getFieldType(int index) {
418 return FIELD_TYPES[index];
419 }
420
421 /**
422 * Gets an array of the field type of each of the fields that this partial supports.
423 * <p>
424 * The fields are returned largest to smallest, Year, Month.
425 *
426 * @return the array of field types (cloned), largest to smallest, never null
427 */
428 public DateTimeFieldType[] getFieldTypes() {
429 return (DateTimeFieldType[]) FIELD_TYPES.clone();
430 }
431
432 //-----------------------------------------------------------------------
433 /**
434 * Returns a copy of this year-month with the specified chronology.
435 * This instance is immutable and unaffected by this method call.
436 * <p>
437 * This method retains the values of the fields, thus the result will
438 * typically refer to a different instant.
439 * <p>
440 * The time zone of the specified chronology is ignored, as YearMonth
441 * operates without a time zone.
442 *
443 * @param newChronology the new chronology, null means ISO
444 * @return a copy of this year-month with a different chronology, never null
445 * @throws IllegalArgumentException if the values are invalid for the new chronology
446 */
447 public YearMonth withChronologyRetainFields(Chronology newChronology) {
448 newChronology = DateTimeUtils.getChronology(newChronology);
449 newChronology = newChronology.withUTC();
450 if (newChronology == getChronology()) {
451 return this;
452 } else {
453 YearMonth newYearMonth = new YearMonth(this, newChronology);
454 newChronology.validate(newYearMonth, getValues());
455 return newYearMonth;
456 }
457 }
458
459 /**
460 * Returns a copy of this year-month with the specified field set to a new value.
461 * <p>
462 * For example, if the field type is <code>monthOfYear</code> then the month
463 * would be changed in the returned instance.
464 * <p>
465 * These three lines are equivalent:
466 * <pre>
467 * YearMonth updated = ym.withField(DateTimeFieldType.monthOfYear(), 6);
468 * YearMonth updated = ym.monthOfYear().setCopy(6);
469 * YearMonth updated = ym.property(DateTimeFieldType.monthOfYear()).setCopy(6);
470 * </pre>
471 *
472 * @param fieldType the field type to set, not null
473 * @param value the value to set
474 * @return a copy of this instance with the field set, never null
475 * @throws IllegalArgumentException if the value is null or invalid
476 */
477 public YearMonth withField(DateTimeFieldType fieldType, int value) {
478 int index = indexOfSupported(fieldType);
479 if (value == getValue(index)) {
480 return this;
481 }
482 int[] newValues = getValues();
483 newValues = getField(index).set(this, index, newValues, value);
484 return new YearMonth(this, newValues);
485 }
486
487 /**
488 * Returns a copy of this year-month with the value of the specified field increased.
489 * <p>
490 * If the addition is zero, then <code>this</code> is returned.
491 * <p>
492 * These three lines are equivalent:
493 * <pre>
494 * YearMonth added = ym.withFieldAdded(DurationFieldType.months(), 6);
495 * YearMonth added = ym.plusMonths(6);
496 * YearMonth added = ym.monthOfYear().addToCopy(6);
497 * </pre>
498 *
499 * @param fieldType the field type to add to, not null
500 * @param amount the amount to add
501 * @return a copy of this instance with the field updated, never null
502 * @throws IllegalArgumentException if the value is null or invalid
503 * @throws ArithmeticException if the new date-time exceeds the capacity
504 */
505 public YearMonth withFieldAdded(DurationFieldType fieldType, int amount) {
506 int index = indexOfSupported(fieldType);
507 if (amount == 0) {
508 return this;
509 }
510 int[] newValues = getValues();
511 newValues = getField(index).add(this, index, newValues, amount);
512 return new YearMonth(this, newValues);
513 }
514
515 /**
516 * Returns a copy of this year-month with the specified period added.
517 * <p>
518 * If the addition is zero, then <code>this</code> is returned.
519 * Fields in the period that aren't present in the partial are ignored.
520 * <p>
521 * This method is typically used to add multiple copies of complex
522 * period instances. Adding one field is best achieved using methods
523 * like {@link #withFieldAdded(DurationFieldType, int)}
524 * or {@link #plusYears(int)}.
525 *
526 * @param period the period to add to this one, null means zero
527 * @param scalar the amount of times to add, such as -1 to subtract once
528 * @return a copy of this instance with the period added, never null
529 * @throws ArithmeticException if the new date-time exceeds the capacity
530 */
531 public YearMonth withPeriodAdded(ReadablePeriod period, int scalar) {
532 if (period == null || scalar == 0) {
533 return this;
534 }
535 int[] newValues = getValues();
536 for (int i = 0; i < period.size(); i++) {
537 DurationFieldType fieldType = period.getFieldType(i);
538 int index = indexOf(fieldType);
539 if (index >= 0) {
540 newValues = getField(index).add(this, index, newValues,
541 FieldUtils.safeMultiply(period.getValue(i), scalar));
542 }
543 }
544 return new YearMonth(this, newValues);
545 }
546
547 //-----------------------------------------------------------------------
548 /**
549 * Returns a copy of this year-month with the specified period added.
550 * <p>
551 * If the amount is zero or null, then <code>this</code> is returned.
552 * <p>
553 * This method is typically used to add complex period instances.
554 * Adding one field is best achieved using methods
555 * like {@link #plusYears(int)}.
556 *
557 * @param period the duration to add to this one, null means zero
558 * @return a copy of this instance with the period added, never null
559 * @throws ArithmeticException if the new year-month exceeds the capacity
560 */
561 public YearMonth plus(ReadablePeriod period) {
562 return withPeriodAdded(period, 1);
563 }
564
565 //-----------------------------------------------------------------------
566 /**
567 * Returns a copy of this year-month plus the specified number of years.
568 * <p>
569 * This year-month instance is immutable and unaffected by this method call.
570 * <p>
571 * The following three lines are identical in effect:
572 * <pre>
573 * YearMonth added = ym.plusYears(6);
574 * YearMonth added = ym.plus(Period.years(6));
575 * YearMonth added = ym.withFieldAdded(DurationFieldType.years(), 6);
576 * </pre>
577 *
578 * @param years the amount of years to add, may be negative
579 * @return the new year-month plus the increased years, never null
580 */
581 public YearMonth plusYears(int years) {
582 return withFieldAdded(DurationFieldType.years(), years);
583 }
584
585 /**
586 * Returns a copy of this year-month plus the specified number of months.
587 * <p>
588 * This year-month instance is immutable and unaffected by this method call.
589 * <p>
590 * The following three lines are identical in effect:
591 * <pre>
592 * YearMonth added = ym.plusMonths(6);
593 * YearMonth added = ym.plus(Period.months(6));
594 * YearMonth added = ym.withFieldAdded(DurationFieldType.months(), 6);
595 * </pre>
596 *
597 * @param months the amount of months to add, may be negative
598 * @return the new year-month plus the increased months, never null
599 */
600 public YearMonth plusMonths(int months) {
601 return withFieldAdded(DurationFieldType.months(), months);
602 }
603
604 //-----------------------------------------------------------------------
605 /**
606 * Returns a copy of this year-month with the specified period taken away.
607 * <p>
608 * If the amount is zero or null, then <code>this</code> is returned.
609 * <p>
610 * This method is typically used to subtract complex period instances.
611 * Subtracting one field is best achieved using methods
612 * like {@link #minusYears(int)}.
613 *
614 * @param period the period to reduce this instant by
615 * @return a copy of this instance with the period taken away, never null
616 * @throws ArithmeticException if the new year-month exceeds the capacity
617 */
618 public YearMonth minus(ReadablePeriod period) {
619 return withPeriodAdded(period, -1);
620 }
621
622 //-----------------------------------------------------------------------
623 /**
624 * Returns a copy of this year-month minus the specified number of years.
625 * <p>
626 * This year-month instance is immutable and unaffected by this method call.
627 * <p>
628 * The following three lines are identical in effect:
629 * <pre>
630 * YearMonth subtracted = ym.minusYears(6);
631 * YearMonth subtracted = ym.minus(Period.years(6));
632 * YearMonth subtracted = ym.withFieldAdded(DurationFieldType.years(), -6);
633 * </pre>
634 *
635 * @param years the amount of years to subtract, may be negative
636 * @return the new year-month minus the increased years, never null
637 */
638 public YearMonth minusYears(int years) {
639 return withFieldAdded(DurationFieldType.years(), FieldUtils.safeNegate(years));
640 }
641
642 /**
643 * Returns a copy of this year-month minus the specified number of months.
644 * <p>
645 * This year-month instance is immutable and unaffected by this method call.
646 * <p>
647 * The following three lines are identical in effect:
648 * <pre>
649 * YearMonth subtracted = ym.minusMonths(6);
650 * YearMonth subtracted = ym.minus(Period.months(6));
651 * YearMonth subtracted = ym.withFieldAdded(DurationFieldType.months(), -6);
652 * </pre>
653 *
654 * @param months the amount of months to subtract, may be negative
655 * @return the new year-month minus the increased months, never null
656 */
657 public YearMonth minusMonths(int months) {
658 return withFieldAdded(DurationFieldType.months(), FieldUtils.safeNegate(months));
659 }
660
661 //-----------------------------------------------------------------------
662 /**
663 * Converts this object to a LocalDate with the same year-month and chronology.
664 *
665 * @param dayOfMonth the day of month to use, valid for chronology, such as 1-31 for ISO
666 * @return a LocalDate with the same year-month and chronology, never null
667 */
668 public LocalDate toLocalDate(int dayOfMonth) {
669 return new LocalDate(getYear(), getMonthOfYear(), dayOfMonth, getChronology());
670 }
671
672 //-----------------------------------------------------------------------
673 /**
674 * Converts this object to an Interval representing the whole month.
675 * <p>
676 * The interval will use the chronology of the year-month in the default zone.
677 * <p>
678 * This instance is immutable and unaffected by this method call.
679 *
680 * @return an interval over the month, never null
681 */
682 public Interval toInterval() {
683 return toInterval(null);
684 }
685
686 /**
687 * Converts this object to an Interval representing the whole month.
688 * <p>
689 * The interval will use the chronology of the year-month in the specified zone.
690 * <p>
691 * This instance is immutable and unaffected by this method call.
692 *
693 * @param zone the zone to get the Interval in, null means default
694 * @return an interval over the month, never null
695 */
696 public Interval toInterval(DateTimeZone zone) {
697 zone = DateTimeUtils.getZone(zone);
698 DateTime start = toLocalDate(1).toDateTimeAtStartOfDay(zone);
699 DateTime end = plusMonths(1).toLocalDate(1).toDateTimeAtStartOfDay(zone);
700 return new Interval(start, end);
701 }
702
703 //-----------------------------------------------------------------------
704 /**
705 * Get the year field value.
706 *
707 * @return the year
708 */
709 public int getYear() {
710 return getValue(YEAR);
711 }
712
713 /**
714 * Get the month of year field value.
715 *
716 * @return the month of year
717 */
718 public int getMonthOfYear() {
719 return getValue(MONTH_OF_YEAR);
720 }
721
722 //-----------------------------------------------------------------------
723 /**
724 * Returns a copy of this year-month with the year field updated.
725 * <p>
726 * YearMonth is immutable, so there are no set methods.
727 * Instead, this method returns a new instance with the value of
728 * year changed.
729 *
730 * @param year the year to set
731 * @return a copy of this object with the field set, never null
732 * @throws IllegalArgumentException if the value is invalid
733 */
734 public YearMonth withYear(int year) {
735 int[] newValues = getValues();
736 newValues = getChronology().year().set(this, YEAR, newValues, year);
737 return new YearMonth(this, newValues);
738 }
739
740 /**
741 * Returns a copy of this year-month with the month of year field updated.
742 * <p>
743 * YearMonth is immutable, so there are no set methods.
744 * Instead, this method returns a new instance with the value of
745 * month of year changed.
746 *
747 * @param monthOfYear the month of year to set
748 * @return a copy of this object with the field set, never null
749 * @throws IllegalArgumentException if the value is invalid
750 */
751 public YearMonth withMonthOfYear(int monthOfYear) {
752 int[] newValues = getValues();
753 newValues = getChronology().monthOfYear().set(this, MONTH_OF_YEAR, newValues, monthOfYear);
754 return new YearMonth(this, newValues);
755 }
756
757 //-----------------------------------------------------------------------
758 /**
759 * Gets the property object for the specified type, which contains
760 * many useful methods.
761 *
762 * @param type the field type to get the property for
763 * @return the property object
764 * @throws IllegalArgumentException if the field is null or unsupported
765 */
766 public Property property(DateTimeFieldType type) {
767 return new Property(this, indexOfSupported(type));
768 }
769
770 //-----------------------------------------------------------------------
771 /**
772 * Get the year field property which provides access to advanced functionality.
773 *
774 * @return the year property
775 */
776 public Property year() {
777 return new Property(this, YEAR);
778 }
779
780 /**
781 * Get the month of year field property which provides access to advanced functionality.
782 *
783 * @return the month of year property
784 */
785 public Property monthOfYear() {
786 return new Property(this, MONTH_OF_YEAR);
787 }
788
789 //-----------------------------------------------------------------------
790 /**
791 * Output the year-month in ISO8601 format (yyyy-MM).
792 *
793 * @return ISO8601 time formatted string.
794 */
795 @ToString
796 public String toString() {
797 return ISODateTimeFormat.yearMonth().print(this);
798 }
799
800 /**
801 * Output the year-month using the specified format pattern.
802 *
803 * @param pattern the pattern specification, null means use <code>toString</code>
804 * @see org.joda.time.format.DateTimeFormat
805 */
806 public String toString(String pattern) {
807 if (pattern == null) {
808 return toString();
809 }
810 return DateTimeFormat.forPattern(pattern).print(this);
811 }
812
813 /**
814 * Output the year-month using the specified format pattern.
815 *
816 * @param pattern the pattern specification, null means use <code>toString</code>
817 * @param locale Locale to use, null means default
818 * @see org.joda.time.format.DateTimeFormat
819 */
820 public String toString(String pattern, Locale locale) throws IllegalArgumentException {
821 if (pattern == null) {
822 return toString();
823 }
824 return DateTimeFormat.forPattern(pattern).withLocale(locale).print(this);
825 }
826
827 //-----------------------------------------------------------------------
828 /**
829 * The property class for <code>YearMonth</code>.
830 * <p>
831 * This class binds a <code>YearMonth</code> to a <code>DateTimeField</code>.
832 *
833 * @author Stephen Colebourne
834 * @since 2.0
835 */
836 public static class Property extends AbstractPartialFieldProperty implements Serializable {
837
838 /** Serialization version */
839 private static final long serialVersionUID = 5727734012190224363L;
840
841 /** The partial */
842 private final YearMonth iBase;
843 /** The field index */
844 private final int iFieldIndex;
845
846 /**
847 * Constructs a property.
848 *
849 * @param partial the partial instance
850 * @param fieldIndex the index in the partial
851 */
852 Property(YearMonth partial, int fieldIndex) {
853 super();
854 iBase = partial;
855 iFieldIndex = fieldIndex;
856 }
857
858 /**
859 * Gets the field that this property uses.
860 *
861 * @return the field
862 */
863 public DateTimeField getField() {
864 return iBase.getField(iFieldIndex);
865 }
866
867 /**
868 * Gets the partial that this property belongs to.
869 *
870 * @return the partial
871 */
872 protected ReadablePartial getReadablePartial() {
873 return iBase;
874 }
875
876 /**
877 * Gets the partial that this property belongs to.
878 *
879 * @return the partial
880 */
881 public YearMonth getYearMonth() {
882 return iBase;
883 }
884
885 /**
886 * Gets the value of this field.
887 *
888 * @return the field value
889 */
890 public int get() {
891 return iBase.getValue(iFieldIndex);
892 }
893
894 //-----------------------------------------------------------------------
895 /**
896 * Adds to the value of this field in a copy of this YearMonth.
897 * <p>
898 * The value will be added to this field. If the value is too large to be
899 * added solely to this field then it will affect larger fields.
900 * Smaller fields are unaffected.
901 * <p>
902 * If the result would be too large, beyond the maximum year, then an
903 * IllegalArgumentException is thrown.
904 * <p>
905 * The YearMonth attached to this property is unchanged by this call.
906 * Instead, a new instance is returned.
907 *
908 * @param valueToAdd the value to add to the field in the copy
909 * @return a copy of the YearMonth with the field value changed
910 * @throws IllegalArgumentException if the value isn't valid
911 */
912 public YearMonth addToCopy(int valueToAdd) {
913 int[] newValues = iBase.getValues();
914 newValues = getField().add(iBase, iFieldIndex, newValues, valueToAdd);
915 return new YearMonth(iBase, newValues);
916 }
917
918 /**
919 * Adds to the value of this field in a copy of this YearMonth wrapping
920 * within this field if the maximum value is reached.
921 * <p>
922 * The value will be added to this field. If the value is too large to be
923 * added solely to this field then it wraps within this field.
924 * Other fields are unaffected.
925 * <p>
926 * For example,
927 * <code>2004-12</code> addWrapField one month returns <code>2004-01</code>.
928 * <p>
929 * The YearMonth attached to this property is unchanged by this call.
930 * Instead, a new instance is returned.
931 *
932 * @param valueToAdd the value to add to the field in the copy
933 * @return a copy of the YearMonth with the field value changed
934 * @throws IllegalArgumentException if the value isn't valid
935 */
936 public YearMonth addWrapFieldToCopy(int valueToAdd) {
937 int[] newValues = iBase.getValues();
938 newValues = getField().addWrapField(iBase, iFieldIndex, newValues, valueToAdd);
939 return new YearMonth(iBase, newValues);
940 }
941
942 //-----------------------------------------------------------------------
943 /**
944 * Sets this field in a copy of the YearMonth.
945 * <p>
946 * The YearMonth attached to this property is unchanged by this call.
947 * Instead, a new instance is returned.
948 *
949 * @param value the value to set the field in the copy to
950 * @return a copy of the YearMonth with the field value changed
951 * @throws IllegalArgumentException if the value isn't valid
952 */
953 public YearMonth setCopy(int value) {
954 int[] newValues = iBase.getValues();
955 newValues = getField().set(iBase, iFieldIndex, newValues, value);
956 return new YearMonth(iBase, newValues);
957 }
958
959 /**
960 * Sets this field in a copy of the YearMonth to a parsed text value.
961 * <p>
962 * The YearMonth attached to this property is unchanged by this call.
963 * Instead, a new instance is returned.
964 *
965 * @param text the text value to set
966 * @param locale optional locale to use for selecting a text symbol
967 * @return a copy of the YearMonth with the field value changed
968 * @throws IllegalArgumentException if the text value isn't valid
969 */
970 public YearMonth setCopy(String text, Locale locale) {
971 int[] newValues = iBase.getValues();
972 newValues = getField().set(iBase, iFieldIndex, newValues, text, locale);
973 return new YearMonth(iBase, newValues);
974 }
975
976 /**
977 * Sets this field in a copy of the YearMonth to a parsed text value.
978 * <p>
979 * The YearMonth attached to this property is unchanged by this call.
980 * Instead, a new instance is returned.
981 *
982 * @param text the text value to set
983 * @return a copy of the YearMonth with the field value changed
984 * @throws IllegalArgumentException if the text value isn't valid
985 */
986 public YearMonth setCopy(String text) {
987 return setCopy(text, null);
988 }
989 }
990
991 }