1 /* 2 * Copyright (c) 2012, 2015, 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 private static final long serialVersionUID = -939150713474957432L; 128 /** 129 * Parser. 130 */ 131 private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() 132 .appendLiteral("--") 133 .appendValue(MONTH_OF_YEAR, 2) 134 .appendLiteral('-') 135 .appendValue(DAY_OF_MONTH, 2) 136 .toFormatter(); 137 138 /** 139 * The month-of-year, not null. 140 */ 141 private final int month; 142 /** 143 * The day-of-month. 144 */ 145 private final int day; 146 147 //----------------------------------------------------------------------- 148 /** 149 * Obtains the current month-day from the system clock in the default time-zone. 150 * <p> 151 * This will query the {@link Clock#systemDefaultZone() system clock} in the default 152 * time-zone to obtain the current month-day. 153 * <p> 154 * Using this method will prevent the ability to use an alternate clock for testing 155 * because the clock is hard-coded. 156 * 157 * @return the current month-day using the system clock and default time-zone, not null 158 */ now()159 public static MonthDay now() { 160 return now(Clock.systemDefaultZone()); 161 } 162 163 /** 164 * Obtains the current month-day from the system clock in the specified time-zone. 165 * <p> 166 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current month-day. 167 * Specifying the time-zone avoids dependence on the default time-zone. 168 * <p> 169 * Using this method will prevent the ability to use an alternate clock for testing 170 * because the clock is hard-coded. 171 * 172 * @param zone the zone ID to use, not null 173 * @return the current month-day using the system clock, not null 174 */ now(ZoneId zone)175 public static MonthDay now(ZoneId zone) { 176 return now(Clock.system(zone)); 177 } 178 179 /** 180 * Obtains the current month-day from the specified clock. 181 * <p> 182 * This will query the specified clock to obtain the current month-day. 183 * Using this method allows the use of an alternate clock for testing. 184 * The alternate clock may be introduced using {@link Clock dependency injection}. 185 * 186 * @param clock the clock to use, not null 187 * @return the current month-day, not null 188 */ now(Clock clock)189 public static MonthDay now(Clock clock) { 190 final LocalDate now = LocalDate.now(clock); // called once 191 return MonthDay.of(now.getMonth(), now.getDayOfMonth()); 192 } 193 194 //----------------------------------------------------------------------- 195 /** 196 * Obtains an instance of {@code MonthDay}. 197 * <p> 198 * The day-of-month must be valid for the month within a leap year. 199 * Hence, for February, day 29 is valid. 200 * <p> 201 * For example, passing in April and day 31 will throw an exception, as 202 * there can never be April 31st in any year. By contrast, passing in 203 * February 29th is permitted, as that month-day can sometimes be valid. 204 * 205 * @param month the month-of-year to represent, not null 206 * @param dayOfMonth the day-of-month to represent, from 1 to 31 207 * @return the month-day, not null 208 * @throws DateTimeException if the value of any field is out of range, 209 * or if the day-of-month is invalid for the month 210 */ of(Month month, int dayOfMonth)211 public static MonthDay of(Month month, int dayOfMonth) { 212 Objects.requireNonNull(month, "month"); 213 DAY_OF_MONTH.checkValidValue(dayOfMonth); 214 if (dayOfMonth > month.maxLength()) { 215 throw new DateTimeException("Illegal value for DayOfMonth field, value " + dayOfMonth + 216 " is not valid for month " + month.name()); 217 } 218 return new MonthDay(month.getValue(), dayOfMonth); 219 } 220 221 /** 222 * Obtains an instance of {@code MonthDay}. 223 * <p> 224 * The day-of-month must be valid for the month within a leap year. 225 * Hence, for month 2 (February), day 29 is valid. 226 * <p> 227 * For example, passing in month 4 (April) and day 31 will throw an exception, as 228 * there can never be April 31st in any year. By contrast, passing in 229 * February 29th is permitted, as that month-day can sometimes be valid. 230 * 231 * @param month the month-of-year to represent, from 1 (January) to 12 (December) 232 * @param dayOfMonth the day-of-month to represent, from 1 to 31 233 * @return the month-day, not null 234 * @throws DateTimeException if the value of any field is out of range, 235 * or if the day-of-month is invalid for the month 236 */ of(int month, int dayOfMonth)237 public static MonthDay of(int month, int dayOfMonth) { 238 return of(Month.of(month), dayOfMonth); 239 } 240 241 //----------------------------------------------------------------------- 242 /** 243 * Obtains an instance of {@code MonthDay} from a temporal object. 244 * <p> 245 * This obtains a month-day based on the specified temporal. 246 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 247 * which this factory converts to an instance of {@code MonthDay}. 248 * <p> 249 * The conversion extracts the {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and 250 * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} fields. 251 * The extraction is only permitted if the temporal object has an ISO 252 * chronology, or can be converted to a {@code LocalDate}. 253 * <p> 254 * This method matches the signature of the functional interface {@link TemporalQuery} 255 * allowing it to be used as a query via method reference, {@code MonthDay::from}. 256 * 257 * @param temporal the temporal object to convert, not null 258 * @return the month-day, not null 259 * @throws DateTimeException if unable to convert to a {@code MonthDay} 260 */ from(TemporalAccessor temporal)261 public static MonthDay from(TemporalAccessor temporal) { 262 if (temporal instanceof MonthDay) { 263 return (MonthDay) temporal; 264 } 265 try { 266 if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { 267 temporal = LocalDate.from(temporal); 268 } 269 return of(temporal.get(MONTH_OF_YEAR), temporal.get(DAY_OF_MONTH)); 270 } catch (DateTimeException ex) { 271 throw new DateTimeException("Unable to obtain MonthDay from TemporalAccessor: " + 272 temporal + " of type " + temporal.getClass().getName(), ex); 273 } 274 } 275 276 //----------------------------------------------------------------------- 277 /** 278 * Obtains an instance of {@code MonthDay} from a text string such as {@code --12-03}. 279 * <p> 280 * The string must represent a valid month-day. 281 * The format is {@code --MM-dd}. 282 * 283 * @param text the text to parse such as "--12-03", not null 284 * @return the parsed month-day, not null 285 * @throws DateTimeParseException if the text cannot be parsed 286 */ parse(CharSequence text)287 public static MonthDay parse(CharSequence text) { 288 return parse(text, PARSER); 289 } 290 291 /** 292 * Obtains an instance of {@code MonthDay} from a text string using a specific formatter. 293 * <p> 294 * The text is parsed using the formatter, returning a month-day. 295 * 296 * @param text the text to parse, not null 297 * @param formatter the formatter to use, not null 298 * @return the parsed month-day, not null 299 * @throws DateTimeParseException if the text cannot be parsed 300 */ parse(CharSequence text, DateTimeFormatter formatter)301 public static MonthDay parse(CharSequence text, DateTimeFormatter formatter) { 302 Objects.requireNonNull(formatter, "formatter"); 303 return formatter.parse(text, MonthDay::from); 304 } 305 306 //----------------------------------------------------------------------- 307 /** 308 * Constructor, previously validated. 309 * 310 * @param month the month-of-year to represent, validated from 1 to 12 311 * @param dayOfMonth the day-of-month to represent, validated from 1 to 29-31 312 */ MonthDay(int month, int dayOfMonth)313 private MonthDay(int month, int dayOfMonth) { 314 this.month = month; 315 this.day = dayOfMonth; 316 } 317 318 //----------------------------------------------------------------------- 319 /** 320 * Checks if the specified field is supported. 321 * <p> 322 * This checks if this month-day can be queried for the specified field. 323 * If false, then calling the {@link #range(TemporalField) range} and 324 * {@link #get(TemporalField) get} methods will throw an exception. 325 * <p> 326 * If the field is a {@link ChronoField} then the query is implemented here. 327 * The supported fields are: 328 * <ul> 329 * <li>{@code MONTH_OF_YEAR} 330 * <li>{@code YEAR} 331 * </ul> 332 * All other {@code ChronoField} instances will return false. 333 * <p> 334 * If the field is not a {@code ChronoField}, then the result of this method 335 * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} 336 * passing {@code this} as the argument. 337 * Whether the field is supported is determined by the field. 338 * 339 * @param field the field to check, null returns false 340 * @return true if the field is supported on this month-day, false if not 341 */ 342 @Override isSupported(TemporalField field)343 public boolean isSupported(TemporalField field) { 344 if (field instanceof ChronoField) { 345 return field == MONTH_OF_YEAR || field == DAY_OF_MONTH; 346 } 347 return field != null && field.isSupportedBy(this); 348 } 349 350 /** 351 * Gets the range of valid values for the specified field. 352 * <p> 353 * The range object expresses the minimum and maximum valid values for a field. 354 * This month-day is used to enhance the accuracy of the returned range. 355 * If it is not possible to return the range, because the field is not supported 356 * or for some other reason, an exception is thrown. 357 * <p> 358 * If the field is a {@link ChronoField} then the query is implemented here. 359 * The {@link #isSupported(TemporalField) supported fields} will return 360 * appropriate range instances. 361 * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. 362 * <p> 363 * If the field is not a {@code ChronoField}, then the result of this method 364 * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)} 365 * passing {@code this} as the argument. 366 * Whether the range can be obtained is determined by the field. 367 * 368 * @param field the field to query the range for, not null 369 * @return the range of valid values for the field, not null 370 * @throws DateTimeException if the range for the field cannot be obtained 371 * @throws UnsupportedTemporalTypeException if the field is not supported 372 */ 373 @Override range(TemporalField field)374 public ValueRange range(TemporalField field) { 375 if (field == MONTH_OF_YEAR) { 376 return field.range(); 377 } else if (field == DAY_OF_MONTH) { 378 return ValueRange.of(1, getMonth().minLength(), getMonth().maxLength()); 379 } 380 return TemporalAccessor.super.range(field); 381 } 382 383 /** 384 * Gets the value of the specified field from this month-day as an {@code int}. 385 * <p> 386 * This queries this month-day for the value of the specified field. 387 * The returned value will always be within the valid range of values for the field. 388 * If it is not possible to return the value, because the field is not supported 389 * or for some other reason, an exception is thrown. 390 * <p> 391 * If the field is a {@link ChronoField} then the query is implemented here. 392 * The {@link #isSupported(TemporalField) supported fields} will return valid 393 * values based on this month-day. 394 * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. 395 * <p> 396 * If the field is not a {@code ChronoField}, then the result of this method 397 * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} 398 * passing {@code this} as the argument. Whether the value can be obtained, 399 * and what the value represents, is determined by the field. 400 * 401 * @param field the field to get, not null 402 * @return the value for the field 403 * @throws DateTimeException if a value for the field cannot be obtained or 404 * the value is outside the range of valid values for the field 405 * @throws UnsupportedTemporalTypeException if the field is not supported or 406 * the range of values exceeds an {@code int} 407 * @throws ArithmeticException if numeric overflow occurs 408 */ 409 @Override // override for Javadoc get(TemporalField field)410 public int get(TemporalField field) { 411 return range(field).checkValidIntValue(getLong(field), field); 412 } 413 414 /** 415 * Gets the value of the specified field from this month-day as a {@code long}. 416 * <p> 417 * This queries this month-day for the value of the specified field. 418 * If it is not possible to return the value, because the field is not supported 419 * or for some other reason, an exception is thrown. 420 * <p> 421 * If the field is a {@link ChronoField} then the query is implemented here. 422 * The {@link #isSupported(TemporalField) supported fields} will return valid 423 * values based on this month-day. 424 * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. 425 * <p> 426 * If the field is not a {@code ChronoField}, then the result of this method 427 * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} 428 * passing {@code this} as the argument. Whether the value can be obtained, 429 * and what the value represents, is determined by the field. 430 * 431 * @param field the field to get, not null 432 * @return the value for the field 433 * @throws DateTimeException if a value for the field cannot be obtained 434 * @throws UnsupportedTemporalTypeException if the field is not supported 435 * @throws ArithmeticException if numeric overflow occurs 436 */ 437 @Override getLong(TemporalField field)438 public long getLong(TemporalField field) { 439 if (field instanceof ChronoField) { 440 switch ((ChronoField) field) { 441 // alignedDOW and alignedWOM not supported because they cannot be set in with() 442 case DAY_OF_MONTH: return day; 443 case MONTH_OF_YEAR: return month; 444 } 445 throw new UnsupportedTemporalTypeException("Unsupported field: " + field); 446 } 447 return field.getFrom(this); 448 } 449 450 //----------------------------------------------------------------------- 451 /** 452 * Gets the month-of-year field from 1 to 12. 453 * <p> 454 * This method returns the month as an {@code int} from 1 to 12. 455 * Application code is frequently clearer if the enum {@link Month} 456 * is used by calling {@link #getMonth()}. 457 * 458 * @return the month-of-year, from 1 to 12 459 * @see #getMonth() 460 */ getMonthValue()461 public int getMonthValue() { 462 return month; 463 } 464 465 /** 466 * Gets the month-of-year field using the {@code Month} enum. 467 * <p> 468 * This method returns the enum {@link Month} for the month. 469 * This avoids confusion as to what {@code int} values mean. 470 * If you need access to the primitive {@code int} value then the enum 471 * provides the {@link Month#getValue() int value}. 472 * 473 * @return the month-of-year, not null 474 * @see #getMonthValue() 475 */ getMonth()476 public Month getMonth() { 477 return Month.of(month); 478 } 479 480 /** 481 * Gets the day-of-month field. 482 * <p> 483 * This method returns the primitive {@code int} value for the day-of-month. 484 * 485 * @return the day-of-month, from 1 to 31 486 */ getDayOfMonth()487 public int getDayOfMonth() { 488 return day; 489 } 490 491 //----------------------------------------------------------------------- 492 /** 493 * Checks if the year is valid for this month-day. 494 * <p> 495 * This method checks whether this month and day and the input year form 496 * a valid date. This can only return false for February 29th. 497 * 498 * @param year the year to validate 499 * @return true if the year is valid for this month-day 500 * @see Year#isValidMonthDay(MonthDay) 501 */ isValidYear(int year)502 public boolean isValidYear(int year) { 503 return (day == 29 && month == 2 && Year.isLeap(year) == false) == false; 504 } 505 506 //----------------------------------------------------------------------- 507 /** 508 * Returns a copy of this {@code MonthDay} with the month-of-year altered. 509 * <p> 510 * This returns a month-day with the specified month. 511 * If the day-of-month is invalid for the specified month, the day will 512 * be adjusted to the last valid day-of-month. 513 * <p> 514 * This instance is immutable and unaffected by this method call. 515 * 516 * @param month the month-of-year to set in the returned month-day, from 1 (January) to 12 (December) 517 * @return a {@code MonthDay} based on this month-day with the requested month, not null 518 * @throws DateTimeException if the month-of-year value is invalid 519 */ withMonth(int month)520 public MonthDay withMonth(int month) { 521 return with(Month.of(month)); 522 } 523 524 /** 525 * Returns a copy of this {@code MonthDay} with the month-of-year altered. 526 * <p> 527 * This returns a month-day with the specified month. 528 * If the day-of-month is invalid for the specified month, the day will 529 * be adjusted to the last valid day-of-month. 530 * <p> 531 * This instance is immutable and unaffected by this method call. 532 * 533 * @param month the month-of-year to set in the returned month-day, not null 534 * @return a {@code MonthDay} based on this month-day with the requested month, not null 535 */ with(Month month)536 public MonthDay with(Month month) { 537 Objects.requireNonNull(month, "month"); 538 if (month.getValue() == this.month) { 539 return this; 540 } 541 int day = Math.min(this.day, month.maxLength()); 542 return new MonthDay(month.getValue(), day); 543 } 544 545 /** 546 * Returns a copy of this {@code MonthDay} with the day-of-month altered. 547 * <p> 548 * This returns a month-day with the specified day-of-month. 549 * If the day-of-month is invalid for the month, an exception is thrown. 550 * <p> 551 * This instance is immutable and unaffected by this method call. 552 * 553 * @param dayOfMonth the day-of-month to set in the return month-day, from 1 to 31 554 * @return a {@code MonthDay} based on this month-day with the requested day, not null 555 * @throws DateTimeException if the day-of-month value is invalid, 556 * or if the day-of-month is invalid for the month 557 */ withDayOfMonth(int dayOfMonth)558 public MonthDay withDayOfMonth(int dayOfMonth) { 559 if (dayOfMonth == this.day) { 560 return this; 561 } 562 return of(month, dayOfMonth); 563 } 564 565 //----------------------------------------------------------------------- 566 /** 567 * Queries this month-day using the specified query. 568 * <p> 569 * This queries this month-day using the specified query strategy object. 570 * The {@code TemporalQuery} object defines the logic to be used to 571 * obtain the result. Read the documentation of the query to understand 572 * what the result of this method will be. 573 * <p> 574 * The result of this method is obtained by invoking the 575 * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the 576 * specified query passing {@code this} as the argument. 577 * 578 * @param <R> the type of the result 579 * @param query the query to invoke, not null 580 * @return the query result, null may be returned (defined by the query) 581 * @throws DateTimeException if unable to query (defined by the query) 582 * @throws ArithmeticException if numeric overflow occurs (defined by the query) 583 */ 584 @SuppressWarnings("unchecked") 585 @Override query(TemporalQuery<R> query)586 public <R> R query(TemporalQuery<R> query) { 587 if (query == TemporalQueries.chronology()) { 588 return (R) IsoChronology.INSTANCE; 589 } 590 return TemporalAccessor.super.query(query); 591 } 592 593 /** 594 * Adjusts the specified temporal object to have this month-day. 595 * <p> 596 * This returns a temporal object of the same observable type as the input 597 * with the month and day-of-month changed to be the same as this. 598 * <p> 599 * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} 600 * twice, passing {@link ChronoField#MONTH_OF_YEAR} and 601 * {@link ChronoField#DAY_OF_MONTH} as the fields. 602 * If the specified temporal object does not use the ISO calendar system then 603 * a {@code DateTimeException} is thrown. 604 * <p> 605 * In most cases, it is clearer to reverse the calling pattern by using 606 * {@link Temporal#with(TemporalAdjuster)}: 607 * <pre> 608 * // these two lines are equivalent, but the second approach is recommended 609 * temporal = thisMonthDay.adjustInto(temporal); 610 * temporal = temporal.with(thisMonthDay); 611 * </pre> 612 * <p> 613 * This instance is immutable and unaffected by this method call. 614 * 615 * @param temporal the target object to be adjusted, not null 616 * @return the adjusted object, not null 617 * @throws DateTimeException if unable to make the adjustment 618 * @throws ArithmeticException if numeric overflow occurs 619 */ 620 @Override adjustInto(Temporal temporal)621 public Temporal adjustInto(Temporal temporal) { 622 if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) { 623 throw new DateTimeException("Adjustment only supported on ISO date-time"); 624 } 625 temporal = temporal.with(MONTH_OF_YEAR, month); 626 return temporal.with(DAY_OF_MONTH, Math.min(temporal.range(DAY_OF_MONTH).getMaximum(), day)); 627 } 628 629 /** 630 * Formats this month-day using the specified formatter. 631 * <p> 632 * This month-day will be passed to the formatter to produce a string. 633 * 634 * @param formatter the formatter to use, not null 635 * @return the formatted month-day string, not null 636 * @throws DateTimeException if an error occurs during printing 637 */ format(DateTimeFormatter formatter)638 public String format(DateTimeFormatter formatter) { 639 Objects.requireNonNull(formatter, "formatter"); 640 return formatter.format(this); 641 } 642 643 //----------------------------------------------------------------------- 644 /** 645 * Combines this month-day with a year to create a {@code LocalDate}. 646 * <p> 647 * This returns a {@code LocalDate} formed from this month-day and the specified year. 648 * <p> 649 * A month-day of February 29th will be adjusted to February 28th in the resulting 650 * date if the year is not a leap year. 651 * <p> 652 * This instance is immutable and unaffected by this method call. 653 * 654 * @param year the year to use, from MIN_YEAR to MAX_YEAR 655 * @return the local date formed from this month-day and the specified year, not null 656 * @throws DateTimeException if the year is outside the valid range of years 657 */ atYear(int year)658 public LocalDate atYear(int year) { 659 return LocalDate.of(year, month, isValidYear(year) ? day : 28); 660 } 661 662 //----------------------------------------------------------------------- 663 /** 664 * Compares this month-day to another month-day. 665 * <p> 666 * The comparison is based first on value of the month, then on the value of the day. 667 * It is "consistent with equals", as defined by {@link Comparable}. 668 * 669 * @param other the other month-day to compare to, not null 670 * @return the comparator value, negative if less, positive if greater 671 */ 672 @Override compareTo(MonthDay other)673 public int compareTo(MonthDay other) { 674 int cmp = (month - other.month); 675 if (cmp == 0) { 676 cmp = (day - other.day); 677 } 678 return cmp; 679 } 680 681 /** 682 * Checks if this month-day is after the specified month-day. 683 * 684 * @param other the other month-day to compare to, not null 685 * @return true if this is after the specified month-day 686 */ isAfter(MonthDay other)687 public boolean isAfter(MonthDay other) { 688 return compareTo(other) > 0; 689 } 690 691 /** 692 * Checks if this month-day is before the specified month-day. 693 * 694 * @param other the other month-day to compare to, not null 695 * @return true if this point is before the specified month-day 696 */ isBefore(MonthDay other)697 public boolean isBefore(MonthDay other) { 698 return compareTo(other) < 0; 699 } 700 701 //----------------------------------------------------------------------- 702 /** 703 * Checks if this month-day is equal to another month-day. 704 * <p> 705 * The comparison is based on the time-line position of the month-day within a year. 706 * 707 * @param obj the object to check, null returns false 708 * @return true if this is equal to the other month-day 709 */ 710 @Override equals(Object obj)711 public boolean equals(Object obj) { 712 if (this == obj) { 713 return true; 714 } 715 if (obj instanceof MonthDay) { 716 MonthDay other = (MonthDay) obj; 717 return month == other.month && day == other.day; 718 } 719 return false; 720 } 721 722 /** 723 * A hash code for this month-day. 724 * 725 * @return a suitable hash code 726 */ 727 @Override hashCode()728 public int hashCode() { 729 return (month << 6) + day; 730 } 731 732 //----------------------------------------------------------------------- 733 /** 734 * Outputs this month-day as a {@code String}, such as {@code --12-03}. 735 * <p> 736 * The output will be in the format {@code --MM-dd}: 737 * 738 * @return a string representation of this month-day, not null 739 */ 740 @Override toString()741 public String toString() { 742 return new StringBuilder(10).append("--") 743 .append(month < 10 ? "0" : "").append(month) 744 .append(day < 10 ? "-0" : "-").append(day) 745 .toString(); 746 } 747 748 //----------------------------------------------------------------------- 749 /** 750 * Writes the object using a 751 * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>. 752 * @serialData 753 * <pre> 754 * out.writeByte(13); // identifies a MonthDay 755 * out.writeByte(month); 756 * out.writeByte(day); 757 * </pre> 758 * 759 * @return the instance of {@code Ser}, not null 760 */ 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 */ readObject(ObjectInputStream s)771 private void readObject(ObjectInputStream s) throws InvalidObjectException { 772 throw new InvalidObjectException("Deserialization via serialization delegate"); 773 } 774 writeExternal(DataOutput out)775 void writeExternal(DataOutput out) throws IOException { 776 out.writeByte(month); 777 out.writeByte(day); 778 } 779 readExternal(DataInput in)780 static MonthDay readExternal(DataInput in) throws IOException { 781 byte month = in.readByte(); 782 byte day = in.readByte(); 783 return MonthDay.of(month, day); 784 } 785 786 } 787