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