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