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.ArrayList;
020    import java.util.Calendar;
021    import java.util.Date;
022    import java.util.List;
023    import java.util.Locale;
024    
025    import org.joda.convert.FromString;
026    import org.joda.convert.ToString;
027    import org.joda.time.base.BasePartial;
028    import org.joda.time.chrono.ISOChronology;
029    import org.joda.time.field.AbstractPartialFieldProperty;
030    import org.joda.time.field.FieldUtils;
031    import org.joda.time.format.DateTimeFormat;
032    import org.joda.time.format.DateTimeFormatter;
033    import org.joda.time.format.DateTimeFormatterBuilder;
034    import org.joda.time.format.ISODateTimeFormat;
035    
036    /**
037     * MonthDay is an immutable partial supporting the monthOfYear and dayOfMonth fields.
038     * <p>
039     * NOTE: This class only supports the two fields listed above.
040     * It is impossible to query any other fields, such as dayOfWeek or centuryOfEra.
041     * <p>
042     * Calculations on MonthDay are performed using a {@link Chronology}.
043     * This chronology is set to be in the UTC time zone for all calculations.
044     * <p>
045     * One use case for this class is to store a birthday without the year (to avoid
046     * storing the age of the person).
047     * This class can be used as the gMonthDay type in XML Schema.
048     * <p>
049     * Each individual field can be queried in two ways:
050     * <ul>
051     * <li><code>getMonthOfYear()</code>
052     * <li><code>monthOfYear().get()</code>
053     * </ul>
054     * The second technique also provides access to other useful methods on the
055     * field:
056     * <ul>
057     * <li>numeric value - <code>monthOfYear().get()</code>
058     * <li>text value - <code>monthOfYear().getAsText()</code>
059     * <li>short text value - <code>monthOfYear().getAsShortText()</code>
060     * <li>maximum/minimum values - <code>monthOfYear().getMaximumValue()</code>
061     * <li>add/subtract - <code>monthOfYear().addToCopy()</code>
062     * <li>set - <code>monthOfYear().setCopy()</code>
063     * </ul>
064     * <p>
065     * MonthDay is thread-safe and immutable, provided that the Chronology is as well.
066     * All standard Chronology classes supplied are thread-safe and immutable.
067     *
068     * @author Chris Pheby
069     * @since 2.0
070     */
071    public final class MonthDay
072            extends BasePartial
073            implements ReadablePartial, Serializable {
074    
075        /** Serialization version */
076        private static final long serialVersionUID = 2954560699050434609L;
077    
078        /** The singleton set of field types */
079        private static final DateTimeFieldType[] FIELD_TYPES = new DateTimeFieldType[] {
080            DateTimeFieldType.monthOfYear(),
081            DateTimeFieldType.dayOfMonth(), };
082    
083        /** The singleton set of field types */
084        private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
085            .appendOptional(ISODateTimeFormat.localDateParser().getParser())
086            .appendOptional(DateTimeFormat.forPattern("--MM-dd").getParser()).toFormatter();
087    
088        /** The index of the monthOfYear field in the field array */
089        public static final int MONTH_OF_YEAR = 0;
090        /** The index of the day field in the field array */
091        public static final int DAY_OF_MONTH = 1;
092    
093        //-----------------------------------------------------------------------
094        /**
095         * Obtains a {@code MonthDay} set to the current system millisecond time
096         * using <code>ISOChronology</code> in the default time zone.
097         * The resulting object does not use the zone.
098         * 
099         * @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    }