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 }