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