1 /* 2 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * * Neither the name of JSR-310 nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.threeten.bp; 33 34 import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH; 35 import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR; 36 37 import java.io.DataInput; 38 import java.io.DataOutput; 39 import java.io.IOException; 40 import java.io.InvalidObjectException; 41 import java.io.ObjectStreamException; 42 import java.io.Serializable; 43 44 import org.threeten.bp.chrono.Chronology; 45 import org.threeten.bp.chrono.IsoChronology; 46 import org.threeten.bp.format.DateTimeFormatter; 47 import org.threeten.bp.format.DateTimeFormatterBuilder; 48 import org.threeten.bp.format.DateTimeParseException; 49 import org.threeten.bp.jdk8.DefaultInterfaceTemporalAccessor; 50 import org.threeten.bp.jdk8.Jdk8Methods; 51 import org.threeten.bp.temporal.ChronoField; 52 import org.threeten.bp.temporal.Temporal; 53 import org.threeten.bp.temporal.TemporalAccessor; 54 import org.threeten.bp.temporal.TemporalAdjuster; 55 import org.threeten.bp.temporal.TemporalField; 56 import org.threeten.bp.temporal.TemporalQueries; 57 import org.threeten.bp.temporal.TemporalQuery; 58 import org.threeten.bp.temporal.UnsupportedTemporalTypeException; 59 import org.threeten.bp.temporal.ValueRange; 60 61 /** 62 * A month-day in the ISO-8601 calendar system, such as {@code --12-03}. 63 * <p> 64 * {@code MonthDay} is an immutable date-time object that represents the combination 65 * of a month and day. Any field that can be derived from a month and day, such as 66 * quarter-of-year, can be obtained. 67 * <p> 68 * This class does not store or represent a year, time or time-zone. 69 * For example, the value "December 3rd" can be stored in a {@code MonthDay}. 70 * <p> 71 * Since a {@code MonthDay} does not possess a year, the leap day of 72 * February 29th is considered valid. 73 * <p> 74 * This class implements {@link TemporalAccessor} rather than {@link Temporal}. 75 * This is because it is not possible to define whether February 29th is valid or not 76 * without external information, preventing the implementation of plus/minus. 77 * Related to this, {@code MonthDay} only provides access to query and set the fields 78 * {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH}. 79 * <p> 80 * The ISO-8601 calendar system is the modern civil calendar system used today 81 * in most of the world. It is equivalent to the proleptic Gregorian calendar 82 * system, in which today's rules for leap years are applied for all time. 83 * For most applications written today, the ISO-8601 rules are entirely suitable. 84 * However, any application that makes use of historical dates, and requires them 85 * to be accurate will find the ISO-8601 approach unsuitable. 86 * 87 * <h3>Specification for implementors</h3> 88 * This class is immutable and thread-safe. 89 */ 90 public final class MonthDay 91 extends DefaultInterfaceTemporalAccessor 92 implements TemporalAccessor, TemporalAdjuster, Comparable<MonthDay>, Serializable { 93 94 /** 95 * Simulate JDK 8 method reference MonthDay::from. 96 */ 97 public static final TemporalQuery<MonthDay> FROM = new TemporalQuery<MonthDay>() { 98 @Override 99 public MonthDay queryFrom(TemporalAccessor temporal) { 100 return MonthDay.from(temporal); 101 } 102 }; 103 104 /** 105 * Serialization version. 106 */ 107 private static final long serialVersionUID = -939150713474957432L; 108 /** 109 * Parser. 110 */ 111 private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() 112 .appendLiteral("--") 113 .appendValue(MONTH_OF_YEAR, 2) 114 .appendLiteral('-') 115 .appendValue(DAY_OF_MONTH, 2) 116 .toFormatter(); 117 118 /** 119 * The month-of-year, not null. 120 */ 121 private final int month; 122 /** 123 * The day-of-month. 124 */ 125 private final int day; 126 127 //----------------------------------------------------------------------- 128 /** 129 * Obtains the current month-day from the system clock in the default time-zone. 130 * <p> 131 * This will query the {@link Clock#systemDefaultZone() system clock} in the default 132 * time-zone to obtain the current month-day. 133 * <p> 134 * Using this method will prevent the ability to use an alternate clock for testing 135 * because the clock is hard-coded. 136 * 137 * @return the current month-day using the system clock and default time-zone, not null 138 */ now()139 public static MonthDay now() { 140 return now(Clock.systemDefaultZone()); 141 } 142 143 /** 144 * Obtains the current month-day from the system clock in the specified time-zone. 145 * <p> 146 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current month-day. 147 * Specifying the time-zone avoids dependence on the default time-zone. 148 * <p> 149 * Using this method will prevent the ability to use an alternate clock for testing 150 * because the clock is hard-coded. 151 * 152 * @param zone the zone ID to use, not null 153 * @return the current month-day using the system clock, not null 154 */ now(ZoneId zone)155 public static MonthDay now(ZoneId zone) { 156 return now(Clock.system(zone)); 157 } 158 159 /** 160 * Obtains the current month-day from the specified clock. 161 * <p> 162 * This will query the specified clock to obtain the current month-day. 163 * Using this method allows the use of an alternate clock for testing. 164 * The alternate clock may be introduced using {@link Clock dependency injection}. 165 * 166 * @param clock the clock to use, not null 167 * @return the current month-day, not null 168 */ now(Clock clock)169 public static MonthDay now(Clock clock) { 170 final LocalDate now = LocalDate.now(clock); // called once 171 return MonthDay.of(now.getMonth(), now.getDayOfMonth()); 172 } 173 174 //----------------------------------------------------------------------- 175 /** 176 * Obtains an instance of {@code MonthDay}. 177 * <p> 178 * The day-of-month must be valid for the month within a leap year. 179 * Hence, for February, day 29 is valid. 180 * <p> 181 * For example, passing in April and day 31 will throw an exception, as 182 * there can never be April 31st in any year. By contrast, passing in 183 * February 29th is permitted, as that month-day can sometimes be valid. 184 * 185 * @param month the month-of-year to represent, not null 186 * @param dayOfMonth the day-of-month to represent, from 1 to 31 187 * @return the month-day, not null 188 * @throws DateTimeException if the value of any field is out of range 189 * @throws DateTimeException if the day-of-month is invalid for the month 190 */ of(Month month, int dayOfMonth)191 public static MonthDay of(Month month, int dayOfMonth) { 192 Jdk8Methods.requireNonNull(month, "month"); 193 DAY_OF_MONTH.checkValidValue(dayOfMonth); 194 if (dayOfMonth > month.maxLength()) { 195 throw new DateTimeException("Illegal value for DayOfMonth field, value " + dayOfMonth + 196 " is not valid for month " + month.name()); 197 } 198 return new MonthDay(month.getValue(), dayOfMonth); 199 } 200 201 /** 202 * Obtains an instance of {@code MonthDay}. 203 * <p> 204 * The day-of-month must be valid for the month within a leap year. 205 * Hence, for month 2 (February), day 29 is valid. 206 * <p> 207 * For example, passing in month 4 (April) and day 31 will throw an exception, as 208 * there can never be April 31st in any year. By contrast, passing in 209 * February 29th is permitted, as that month-day can sometimes be valid. 210 * 211 * @param month the month-of-year to represent, from 1 (January) to 12 (December) 212 * @param dayOfMonth the day-of-month to represent, from 1 to 31 213 * @return the month-day, not null 214 * @throws DateTimeException if the value of any field is out of range 215 * @throws DateTimeException if the day-of-month is invalid for the month 216 */ of(int month, int dayOfMonth)217 public static MonthDay of(int month, int dayOfMonth) { 218 return of(Month.of(month), dayOfMonth); 219 } 220 221 //----------------------------------------------------------------------- 222 /** 223 * Obtains an instance of {@code MonthDay} from a temporal object. 224 * <p> 225 * A {@code TemporalAccessor} represents some form of date and time information. 226 * This factory converts the arbitrary temporal object to an instance of {@code MonthDay}. 227 * <p> 228 * The conversion extracts the {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and 229 * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} fields. 230 * The extraction is only permitted if the date-time has an ISO chronology. 231 * <p> 232 * This method matches the signature of the functional interface {@link TemporalQuery} 233 * allowing it to be used in queries via method reference, {@code MonthDay::from}. 234 * 235 * @param temporal the temporal object to convert, not null 236 * @return the month-day, not null 237 * @throws DateTimeException if unable to convert to a {@code MonthDay} 238 */ from(TemporalAccessor temporal)239 public static MonthDay from(TemporalAccessor temporal) { 240 if (temporal instanceof MonthDay) { 241 return (MonthDay) temporal; 242 } 243 try { 244 if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { 245 temporal = LocalDate.from(temporal); 246 } 247 return of(temporal.get(MONTH_OF_YEAR), temporal.get(DAY_OF_MONTH)); 248 } catch (DateTimeException ex) { 249 throw new DateTimeException("Unable to obtain MonthDay from TemporalAccessor: " + 250 temporal + ", type " + temporal.getClass().getName()); 251 } 252 } 253 254 //----------------------------------------------------------------------- 255 /** 256 * Obtains an instance of {@code MonthDay} from a text string such as {@code --12-03}. 257 * <p> 258 * The string must represent a valid month-day. 259 * The format is {@code --MM-dd}. 260 * 261 * @param text the text to parse such as "--12-03", not null 262 * @return the parsed month-day, not null 263 * @throws DateTimeParseException if the text cannot be parsed 264 */ parse(CharSequence text)265 public static MonthDay parse(CharSequence text) { 266 return parse(text, PARSER); 267 } 268 269 /** 270 * Obtains an instance of {@code MonthDay} from a text string using a specific formatter. 271 * <p> 272 * The text is parsed using the formatter, returning a month-day. 273 * 274 * @param text the text to parse, not null 275 * @param formatter the formatter to use, not null 276 * @return the parsed month-day, not null 277 * @throws DateTimeParseException if the text cannot be parsed 278 */ parse(CharSequence text, DateTimeFormatter formatter)279 public static MonthDay parse(CharSequence text, DateTimeFormatter formatter) { 280 Jdk8Methods.requireNonNull(formatter, "formatter"); 281 return formatter.parse(text, MonthDay.FROM); 282 } 283 284 //----------------------------------------------------------------------- 285 /** 286 * Constructor, previously validated. 287 * 288 * @param month the month-of-year to represent, validated from 1 to 12 289 * @param dayOfMonth the day-of-month to represent, validated from 1 to 29-31 290 */ MonthDay(int month, int dayOfMonth)291 private MonthDay(int month, int dayOfMonth) { 292 this.month = month; 293 this.day = dayOfMonth; 294 } 295 296 //----------------------------------------------------------------------- 297 /** 298 * Checks if the specified field is supported. 299 * <p> 300 * This checks if this month-day can be queried for the specified field. 301 * If false, then calling the {@link #range(TemporalField) range} and 302 * {@link #get(TemporalField) get} methods will throw an exception. 303 * <p> 304 * If the field is a {@link ChronoField} then the query is implemented here. 305 * The {@link #isSupported(TemporalField) supported fields} will return valid 306 * values based on this date-time. 307 * The supported fields are: 308 * <ul> 309 * <li>{@code MONTH_OF_YEAR} 310 * <li>{@code YEAR} 311 * </ul> 312 * All other {@code ChronoField} instances will return false. 313 * <p> 314 * If the field is not a {@code ChronoField}, then the result of this method 315 * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} 316 * passing {@code this} as the argument. 317 * Whether the field is supported is determined by the field. 318 * 319 * @param field the field to check, null returns false 320 * @return true if the field is supported on this month-day, false if not 321 */ 322 @Override isSupported(TemporalField field)323 public boolean isSupported(TemporalField field) { 324 if (field instanceof ChronoField) { 325 return field == MONTH_OF_YEAR || field == DAY_OF_MONTH; 326 } 327 return field != null && field.isSupportedBy(this); 328 } 329 330 /** 331 * Gets the range of valid values for the specified field. 332 * <p> 333 * The range object expresses the minimum and maximum valid values for a field. 334 * This month-day is used to enhance the accuracy of the returned range. 335 * If it is not possible to return the range, because the field is not supported 336 * or for some other reason, an exception is thrown. 337 * <p> 338 * If the field is a {@link ChronoField} then the query is implemented here. 339 * The {@link #isSupported(TemporalField) supported fields} will return 340 * appropriate range instances. 341 * All other {@code ChronoField} instances will throw a {@code DateTimeException}. 342 * <p> 343 * If the field is not a {@code ChronoField}, then the result of this method 344 * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)} 345 * passing {@code this} as the argument. 346 * Whether the range can be obtained is determined by the field. 347 * 348 * @param field the field to query the range for, not null 349 * @return the range of valid values for the field, not null 350 * @throws DateTimeException if the range for the field cannot be obtained 351 */ 352 @Override range(TemporalField field)353 public ValueRange range(TemporalField field) { 354 if (field == MONTH_OF_YEAR) { 355 return field.range(); 356 } else if (field == DAY_OF_MONTH) { 357 return ValueRange.of(1, getMonth().minLength(), getMonth().maxLength()); 358 } 359 return super.range(field); 360 } 361 362 /** 363 * Gets the value of the specified field from this month-day as an {@code int}. 364 * <p> 365 * This queries this month-day for the value for the specified field. 366 * The returned value will always be within the valid range of values for the field. 367 * If it is not possible to return the value, because the field is not supported 368 * or for some other reason, an exception is thrown. 369 * <p> 370 * If the field is a {@link ChronoField} then the query is implemented here. 371 * The {@link #isSupported(TemporalField) supported fields} will return valid 372 * values based on this month-day. 373 * All other {@code ChronoField} instances will throw a {@code DateTimeException}. 374 * <p> 375 * If the field is not a {@code ChronoField}, then the result of this method 376 * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} 377 * passing {@code this} as the argument. Whether the value can be obtained, 378 * and what the value represents, is determined by the field. 379 * 380 * @param field the field to get, not null 381 * @return the value for the field 382 * @throws DateTimeException if a value for the field cannot be obtained 383 * @throws ArithmeticException if numeric overflow occurs 384 */ 385 @Override // override for Javadoc get(TemporalField field)386 public int get(TemporalField field) { 387 return range(field).checkValidIntValue(getLong(field), field); 388 } 389 390 /** 391 * Gets the value of the specified field from this month-day as a {@code long}. 392 * <p> 393 * This queries this month-day for the value for the specified field. 394 * If it is not possible to return the value, because the field is not supported 395 * or for some other reason, an exception is thrown. 396 * <p> 397 * If the field is a {@link ChronoField} then the query is implemented here. 398 * The {@link #isSupported(TemporalField) supported fields} will return valid 399 * values based on this month-day. 400 * All other {@code ChronoField} instances will throw a {@code DateTimeException}. 401 * <p> 402 * If the field is not a {@code ChronoField}, then the result of this method 403 * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} 404 * passing {@code this} as the argument. Whether the value can be obtained, 405 * and what the value represents, is determined by the field. 406 * 407 * @param field the field to get, not null 408 * @return the value for the field 409 * @throws DateTimeException if a value for the field cannot be obtained 410 * @throws ArithmeticException if numeric overflow occurs 411 */ 412 @Override getLong(TemporalField field)413 public long getLong(TemporalField field) { 414 if (field instanceof ChronoField) { 415 switch ((ChronoField) field) { 416 // alignedDOW and alignedWOM not supported because they cannot be set in with() 417 case DAY_OF_MONTH: return day; 418 case MONTH_OF_YEAR: return month; 419 } 420 throw new UnsupportedTemporalTypeException("Unsupported field: " + field); 421 } 422 return field.getFrom(this); 423 } 424 425 //----------------------------------------------------------------------- 426 /** 427 * Gets the month-of-year field from 1 to 12. 428 * <p> 429 * This method returns the month as an {@code int} from 1 to 12. 430 * Application code is frequently clearer if the enum {@link Month} 431 * is used by calling {@link #getMonth()}. 432 * 433 * @return the month-of-year, from 1 to 12 434 * @see #getMonth() 435 */ getMonthValue()436 public int getMonthValue() { 437 return month; 438 } 439 440 /** 441 * Gets the month-of-year field using the {@code Month} enum. 442 * <p> 443 * This method returns the enum {@link Month} for the month. 444 * This avoids confusion as to what {@code int} values mean. 445 * If you need access to the primitive {@code int} value then the enum 446 * provides the {@link Month#getValue() int value}. 447 * 448 * @return the month-of-year, not null 449 * @see #getMonthValue() 450 */ getMonth()451 public Month getMonth() { 452 return Month.of(month); 453 } 454 455 /** 456 * Gets the day-of-month field. 457 * <p> 458 * This method returns the primitive {@code int} value for the day-of-month. 459 * 460 * @return the day-of-month, from 1 to 31 461 */ getDayOfMonth()462 public int getDayOfMonth() { 463 return day; 464 } 465 466 //----------------------------------------------------------------------- 467 /** 468 * Checks if the year is valid for this month-day. 469 * <p> 470 * This method checks whether this month and day and the input year form 471 * a valid date. This can only return false for February 29th. 472 * 473 * @param year the year to validate, an out of range value returns false 474 * @return true if the year is valid for this month-day 475 * @see Year#isValidMonthDay(MonthDay) 476 */ isValidYear(int year)477 public boolean isValidYear(int year) { 478 return (day == 29 && month == 2 && Year.isLeap(year) == false) == false; 479 } 480 481 //----------------------------------------------------------------------- 482 /** 483 * Returns a copy of this {@code MonthDay} with the month-of-year altered. 484 * <p> 485 * This returns a month-day with the specified month. 486 * If the day-of-month is invalid for the specified month, the day will 487 * be adjusted to the last valid day-of-month. 488 * <p> 489 * This instance is immutable and unaffected by this method call. 490 * 491 * @param month the month-of-year to set in the returned month-day, from 1 (January) to 12 (December) 492 * @return a {@code MonthDay} based on this month-day with the requested month, not null 493 * @throws DateTimeException if the month-of-year value is invalid 494 */ withMonth(int month)495 public MonthDay withMonth(int month) { 496 return with(Month.of(month)); 497 } 498 499 /** 500 * Returns a copy of this {@code MonthDay} with the month-of-year altered. 501 * <p> 502 * This returns a month-day with the specified month. 503 * If the day-of-month is invalid for the specified month, the day will 504 * be adjusted to the last valid day-of-month. 505 * <p> 506 * This instance is immutable and unaffected by this method call. 507 * 508 * @param month the month-of-year to set in the returned month-day, not null 509 * @return a {@code MonthDay} based on this month-day with the requested month, not null 510 */ with(Month month)511 public MonthDay with(Month month) { 512 Jdk8Methods.requireNonNull(month, "month"); 513 if (month.getValue() == this.month) { 514 return this; 515 } 516 int day = Math.min(this.day, month.maxLength()); 517 return new MonthDay(month.getValue(), day); 518 } 519 520 /** 521 * Returns a copy of this {@code MonthDay} with the day-of-month altered. 522 * <p> 523 * This returns a month-day with the specified day-of-month. 524 * If the day-of-month is invalid for the month, an exception is thrown. 525 * <p> 526 * This instance is immutable and unaffected by this method call. 527 * 528 * @param dayOfMonth the day-of-month to set in the return month-day, from 1 to 31 529 * @return a {@code MonthDay} based on this month-day with the requested day, not null 530 * @throws DateTimeException if the day-of-month value is invalid 531 * @throws DateTimeException if the day-of-month is invalid for the month 532 */ withDayOfMonth(int dayOfMonth)533 public MonthDay withDayOfMonth(int dayOfMonth) { 534 if (dayOfMonth == this.day) { 535 return this; 536 } 537 return of(month, dayOfMonth); 538 } 539 540 //----------------------------------------------------------------------- 541 /** 542 * Queries this month-day using the specified query. 543 * <p> 544 * This queries this month-day using the specified query strategy object. 545 * The {@code TemporalQuery} object defines the logic to be used to 546 * obtain the result. Read the documentation of the query to understand 547 * what the result of this method will be. 548 * <p> 549 * The result of this method is obtained by invoking the 550 * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the 551 * specified query passing {@code this} as the argument. 552 * 553 * @param <R> the type of the result 554 * @param query the query to invoke, not null 555 * @return the query result, null may be returned (defined by the query) 556 * @throws DateTimeException if unable to query (defined by the query) 557 * @throws ArithmeticException if numeric overflow occurs (defined by the query) 558 */ 559 @SuppressWarnings("unchecked") 560 @Override query(TemporalQuery<R> query)561 public <R> R query(TemporalQuery<R> query) { 562 if (query == TemporalQueries.chronology()) { 563 return (R) IsoChronology.INSTANCE; 564 } 565 return super.query(query); 566 } 567 568 /** 569 * Adjusts the specified temporal object to have this month-day. 570 * <p> 571 * This returns a temporal object of the same observable type as the input 572 * with the month and day-of-month changed to be the same as this. 573 * <p> 574 * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} 575 * twice, passing {@link ChronoField#MONTH_OF_YEAR} and 576 * {@link ChronoField#DAY_OF_MONTH} as the fields. 577 * If the specified temporal object does not use the ISO calendar system then 578 * a {@code DateTimeException} is thrown. 579 * <p> 580 * In most cases, it is clearer to reverse the calling pattern by using 581 * {@link Temporal#with(TemporalAdjuster)}: 582 * <pre> 583 * // these two lines are equivalent, but the second approach is recommended 584 * temporal = thisMonthDay.adjustInto(temporal); 585 * temporal = temporal.with(thisMonthDay); 586 * </pre> 587 * <p> 588 * This instance is immutable and unaffected by this method call. 589 * 590 * @param temporal the target object to be adjusted, not null 591 * @return the adjusted object, not null 592 * @throws DateTimeException if unable to make the adjustment 593 * @throws ArithmeticException if numeric overflow occurs 594 */ 595 @Override adjustInto(Temporal temporal)596 public Temporal adjustInto(Temporal temporal) { 597 if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) { 598 throw new DateTimeException("Adjustment only supported on ISO date-time"); 599 } 600 temporal = temporal.with(MONTH_OF_YEAR, month); 601 return temporal.with(DAY_OF_MONTH, Math.min(temporal.range(DAY_OF_MONTH).getMaximum(), day)); 602 } 603 604 //----------------------------------------------------------------------- 605 /** 606 * Combines this month-day with a year to create a {@code LocalDate}. 607 * <p> 608 * This returns a {@code LocalDate} formed from this month-day and the specified year. 609 * <p> 610 * A month-day of February 29th will be adjusted to February 28th in the resulting 611 * date if the year is not a leap year. 612 * <p> 613 * This instance is immutable and unaffected by this method call. 614 * 615 * @param year the year to use, from MIN_YEAR to MAX_YEAR 616 * @return the local date formed from this month-day and the specified year, not null 617 * @throws DateTimeException if the year is outside the valid range of years 618 */ atYear(int year)619 public LocalDate atYear(int year) { 620 return LocalDate.of(year, month, isValidYear(year) ? day : 28); 621 } 622 623 //----------------------------------------------------------------------- 624 /** 625 * Compares this month-day to another month-day. 626 * <p> 627 * The comparison is based first on value of the month, then on the value of the day. 628 * It is "consistent with equals", as defined by {@link Comparable}. 629 * 630 * @param other the other month-day to compare to, not null 631 * @return the comparator value, negative if less, positive if greater 632 */ compareTo(MonthDay other)633 public int compareTo(MonthDay other) { 634 int cmp = (month - other.month); 635 if (cmp == 0) { 636 cmp = (day - other.day); 637 } 638 return cmp; 639 } 640 641 /** 642 * Is this month-day after the specified month-day. 643 * 644 * @param other the other month-day to compare to, not null 645 * @return true if this is after the specified month-day 646 */ isAfter(MonthDay other)647 public boolean isAfter(MonthDay other) { 648 return compareTo(other) > 0; 649 } 650 651 /** 652 * Is this month-day before the specified month-day. 653 * 654 * @param other the other month-day to compare to, not null 655 * @return true if this point is before the specified month-day 656 */ isBefore(MonthDay other)657 public boolean isBefore(MonthDay other) { 658 return compareTo(other) < 0; 659 } 660 661 //----------------------------------------------------------------------- 662 /** 663 * Checks if this month-day is equal to another month-day. 664 * <p> 665 * The comparison is based on the time-line position of the month-day within a year. 666 * 667 * @param obj the object to check, null returns false 668 * @return true if this is equal to the other month-day 669 */ 670 @Override equals(Object obj)671 public boolean equals(Object obj) { 672 if (this == obj) { 673 return true; 674 } 675 if (obj instanceof MonthDay) { 676 MonthDay other = (MonthDay) obj; 677 return month == other.month && day == other.day; 678 } 679 return false; 680 } 681 682 /** 683 * A hash code for this month-day. 684 * 685 * @return a suitable hash code 686 */ 687 @Override hashCode()688 public int hashCode() { 689 return (month << 6) + day; 690 } 691 692 //----------------------------------------------------------------------- 693 /** 694 * Outputs this month-day as a {@code String}, such as {@code --12-03}. 695 * <p> 696 * The output will be in the format {@code --MM-dd}: 697 * 698 * @return a string representation of this month-day, not null 699 */ 700 @Override toString()701 public String toString() { 702 return new StringBuilder(10).append("--") 703 .append(month < 10 ? "0" : "").append(month) 704 .append(day < 10 ? "-0" : "-").append(day) 705 .toString(); 706 } 707 708 /** 709 * Outputs this month-day as a {@code String} using the formatter. 710 * <p> 711 * This month-day will be passed to the formatter 712 * {@link DateTimeFormatter#format(TemporalAccessor) print method}. 713 * 714 * @param formatter the formatter to use, not null 715 * @return the formatted month-day string, not null 716 * @throws DateTimeException if an error occurs during printing 717 */ format(DateTimeFormatter formatter)718 public String format(DateTimeFormatter formatter) { 719 Jdk8Methods.requireNonNull(formatter, "formatter"); 720 return formatter.format(this); 721 } 722 723 //----------------------------------------------------------------------- writeReplace()724 private Object writeReplace() { 725 return new Ser(Ser.MONTH_DAY_TYPE, this); 726 } 727 728 /** 729 * Defend against malicious streams. 730 * @return never 731 * @throws InvalidObjectException always 732 */ readResolve()733 private Object readResolve() throws ObjectStreamException { 734 throw new InvalidObjectException("Deserialization via serialization delegate"); 735 } 736 writeExternal(DataOutput out)737 void writeExternal(DataOutput out) throws IOException { 738 out.writeByte(month); 739 out.writeByte(day); 740 } 741 readExternal(DataInput in)742 static MonthDay readExternal(DataInput in) throws IOException { 743 byte month = in.readByte(); 744 byte day = in.readByte(); 745 return MonthDay.of(month, day); 746 } 747 748 } 749