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 }