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.ERA;
  65 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  66 import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
  67 import static java.time.temporal.ChronoField.YEAR;
  68 import static java.time.temporal.ChronoField.YEAR_OF_ERA;
  69 import static java.time.temporal.ChronoUnit.CENTURIES;
  70 import static java.time.temporal.ChronoUnit.DECADES;
  71 import static java.time.temporal.ChronoUnit.ERAS;
  72 import static java.time.temporal.ChronoUnit.MILLENNIA;
  73 import static java.time.temporal.ChronoUnit.MONTHS;
  74 import static java.time.temporal.ChronoUnit.YEARS;
  75 
  76 import java.io.DataInput;
  77 import java.io.DataOutput;
  78 import java.io.IOException;
  79 import java.io.InvalidObjectException;
  80 import java.io.ObjectInputStream;
  81 import java.io.Serializable;
  82 import java.time.chrono.Chronology;
  83 import java.time.chrono.IsoChronology;
  84 import java.time.format.DateTimeFormatter;
  85 import java.time.format.DateTimeFormatterBuilder;
  86 import java.time.format.DateTimeParseException;
  87 import java.time.format.SignStyle;
  88 import java.time.temporal.ChronoField;
  89 import java.time.temporal.ChronoUnit;
  90 import java.time.temporal.Temporal;
  91 import java.time.temporal.TemporalAccessor;
  92 import java.time.temporal.TemporalAdjuster;
  93 import java.time.temporal.TemporalAmount;
  94 import java.time.temporal.TemporalField;
  95 import java.time.temporal.TemporalQueries;
  96 import java.time.temporal.TemporalQuery;
  97 import java.time.temporal.TemporalUnit;
  98 import java.time.temporal.UnsupportedTemporalTypeException;
  99 import java.time.temporal.ValueRange;
 100 import java.util.Objects;
 101 
 102 /**
 103  * A year-month in the ISO-8601 calendar system, such as {@code 2007-12}.
 104  * <p>
 105  * {@code YearMonth} is an immutable date-time object that represents the combination
 106  * of a year and month. Any field that can be derived from a year and month, such as
 107  * quarter-of-year, can be obtained.
 108  * <p>
 109  * This class does not store or represent a day, time or time-zone.
 110  * For example, the value "October 2007" can be stored in a {@code YearMonth}.
 111  * <p>
 112  * The ISO-8601 calendar system is the modern civil calendar system used today
 113  * in most of the world. It is equivalent to the proleptic Gregorian calendar
 114  * system, in which today's rules for leap years are applied for all time.
 115  * For most applications written today, the ISO-8601 rules are entirely suitable.
 116  * However, any application that makes use of historical dates, and requires them
 117  * to be accurate will find the ISO-8601 approach unsuitable.
 118  * <p>
 119  * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
 120  * class; programmers should treat instances that are
 121  * {@linkplain #equals(Object) equal} as interchangeable and should not
 122  * use instances for synchronization, or unpredictable behavior may
 123  * occur. For example, in a future release, synchronization may fail.
 124  * The {@code equals} method should be used for comparisons.
 125  *
 126  * @implSpec
 127  * This class is immutable and thread-safe.
 128  *
 129  * @since 1.8
 130  */
 131 @jdk.internal.ValueBased
 132 @jdk.internal.MigratedValueClass
 133 public final class YearMonth
 134         implements Temporal, TemporalAdjuster, Comparable<YearMonth>, Serializable {
 135 
 136     /**
 137      * Serialization version.
 138      */
 139     @java.io.Serial
 140     private static final long serialVersionUID = 4183400860270640070L;
 141     /**
 142      * Parser.
 143      */
 144     private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
 145         .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
 146         .appendLiteral('-')
 147         .appendValue(MONTH_OF_YEAR, 2)
 148         .toFormatter();
 149 
 150     /**
 151      * The year.
 152      */
 153     private final int year;
 154     /**
 155      * The month-of-year, not null.
 156      */
 157     private final int month;
 158 
 159     //-----------------------------------------------------------------------
 160     /**
 161      * Obtains the current year-month from the system clock in the default time-zone.
 162      * <p>
 163      * This will query the {@link Clock#systemDefaultZone() system clock} in the default
 164      * time-zone to obtain the current year-month.
 165      * <p>
 166      * Using this method will prevent the ability to use an alternate clock for testing
 167      * because the clock is hard-coded.
 168      *
 169      * @return the current year-month using the system clock and default time-zone, not null
 170      */
 171     public static YearMonth now() {
 172         return now(Clock.systemDefaultZone());
 173     }
 174 
 175     /**
 176      * Obtains the current year-month from the system clock in the specified time-zone.
 177      * <p>
 178      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current year-month.
 179      * Specifying the time-zone avoids dependence on the default time-zone.
 180      * <p>
 181      * Using this method will prevent the ability to use an alternate clock for testing
 182      * because the clock is hard-coded.
 183      *
 184      * @param zone  the zone ID to use, not null
 185      * @return the current year-month using the system clock, not null
 186      */
 187     public static YearMonth now(ZoneId zone) {
 188         return now(Clock.system(zone));
 189     }
 190 
 191     /**
 192      * Obtains the current year-month from the specified clock.
 193      * <p>
 194      * This will query the specified clock to obtain the current year-month.
 195      * Using this method allows the use of an alternate clock for testing.
 196      * The alternate clock may be introduced using {@link Clock dependency injection}.
 197      *
 198      * @param clock  the clock to use, not null
 199      * @return the current year-month, not null
 200      */
 201     public static YearMonth now(Clock clock) {
 202         final LocalDate now = LocalDate.now(clock);  // called once
 203         return YearMonth.of(now.getYear(), now.getMonth());
 204     }
 205 
 206     //-----------------------------------------------------------------------
 207     /**
 208      * Obtains an instance of {@code YearMonth} from a year and month.
 209      *
 210      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
 211      * @param month  the month-of-year to represent, not null
 212      * @return the year-month, not null
 213      * @throws DateTimeException if the year value is invalid
 214      */
 215     public static YearMonth of(int year, Month month) {
 216         Objects.requireNonNull(month, "month");
 217         return of(year, month.getValue());
 218     }
 219 
 220     /**
 221      * Obtains an instance of {@code YearMonth} from a year and month.
 222      *
 223      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
 224      * @param month  the month-of-year to represent, from 1 (January) to 12 (December)
 225      * @return the year-month, not null
 226      * @throws DateTimeException if either field value is invalid
 227      */
 228     public static YearMonth of(int year, int month) {
 229         YEAR.checkValidValue(year);
 230         MONTH_OF_YEAR.checkValidValue(month);
 231         return new YearMonth(year, month);
 232     }
 233 
 234     //-----------------------------------------------------------------------
 235     /**
 236      * Obtains an instance of {@code YearMonth} from a temporal object.
 237      * <p>
 238      * This obtains a year-month based on the specified temporal.
 239      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
 240      * which this factory converts to an instance of {@code YearMonth}.
 241      * <p>
 242      * The conversion extracts the {@link ChronoField#YEAR YEAR} and
 243      * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} fields.
 244      * The extraction is only permitted if the temporal object has an ISO
 245      * chronology, or can be converted to a {@code LocalDate}.
 246      * <p>
 247      * This method matches the signature of the functional interface {@link TemporalQuery}
 248      * allowing it to be used as a query via method reference, {@code YearMonth::from}.
 249      *
 250      * @param temporal  the temporal object to convert, not null
 251      * @return the year-month, not null
 252      * @throws DateTimeException if unable to convert to a {@code YearMonth}
 253      */
 254     public static YearMonth from(TemporalAccessor temporal) {
 255         if (temporal instanceof YearMonth) {
 256             return (YearMonth) temporal;
 257         }
 258         Objects.requireNonNull(temporal, "temporal");
 259         try {
 260             if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
 261                 temporal = LocalDate.from(temporal);
 262             }
 263             return of(temporal.get(YEAR), temporal.get(MONTH_OF_YEAR));
 264         } catch (DateTimeException ex) {
 265             throw new DateTimeException("Unable to obtain YearMonth from TemporalAccessor: " +
 266                     temporal + " of type " + temporal.getClass().getName(), ex);
 267         }
 268     }
 269 
 270     //-----------------------------------------------------------------------
 271     /**
 272      * Obtains an instance of {@code YearMonth} from a text string such as {@code 2007-12}.
 273      * <p>
 274      * The string must represent a valid year-month.
 275      * The format must be {@code uuuu-MM}.
 276      * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol.
 277      *
 278      * @param text  the text to parse such as "2007-12", not null
 279      * @return the parsed year-month, not null
 280      * @throws DateTimeParseException if the text cannot be parsed
 281      */
 282     public static YearMonth parse(CharSequence text) {
 283         return parse(text, PARSER);
 284     }
 285 
 286     /**
 287      * Obtains an instance of {@code YearMonth} from a text string using a specific formatter.
 288      * <p>
 289      * The text is parsed using the formatter, returning a year-month.
 290      *
 291      * @param text  the text to parse, not null
 292      * @param formatter  the formatter to use, not null
 293      * @return the parsed year-month, not null
 294      * @throws DateTimeParseException if the text cannot be parsed
 295      */
 296     public static YearMonth parse(CharSequence text, DateTimeFormatter formatter) {
 297         Objects.requireNonNull(formatter, "formatter");
 298         return formatter.parse(text, YearMonth::from);
 299     }
 300 
 301     //-----------------------------------------------------------------------
 302     /**
 303      * Constructor.
 304      *
 305      * @param year  the year to represent, validated from MIN_YEAR to MAX_YEAR
 306      * @param month  the month-of-year to represent, validated from 1 (January) to 12 (December)
 307      */
 308     private YearMonth(int year, int month) {
 309         this.year = year;
 310         this.month = month;
 311     }
 312 
 313     /**
 314      * Returns a copy of this year-month with the new year and month, checking
 315      * to see if a new object is in fact required.
 316      *
 317      * @param newYear  the year to represent, validated from MIN_YEAR to MAX_YEAR
 318      * @param newMonth  the month-of-year to represent, validated not null
 319      * @return the year-month, not null
 320      */
 321     private YearMonth with(int newYear, int newMonth) {
 322         if (year == newYear && month == newMonth) {
 323             return this;
 324         }
 325         return new YearMonth(newYear, newMonth);
 326     }
 327 
 328     //-----------------------------------------------------------------------
 329     /**
 330      * Checks if the specified field is supported.
 331      * <p>
 332      * This checks if this year-month can be queried for the specified field.
 333      * If false, then calling the {@link #range(TemporalField) range},
 334      * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
 335      * methods will throw an exception.
 336      * <p>
 337      * If the field is a {@link ChronoField} then the query is implemented here.
 338      * The supported fields are:
 339      * <ul>
 340      * <li>{@code MONTH_OF_YEAR}
 341      * <li>{@code PROLEPTIC_MONTH}
 342      * <li>{@code YEAR_OF_ERA}
 343      * <li>{@code YEAR}
 344      * <li>{@code ERA}
 345      * </ul>
 346      * All other {@code ChronoField} instances will return false.
 347      * <p>
 348      * If the field is not a {@code ChronoField}, then the result of this method
 349      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
 350      * passing {@code this} as the argument.
 351      * Whether the field is supported is determined by the field.
 352      *
 353      * @param field  the field to check, null returns false
 354      * @return true if the field is supported on this year-month, false if not
 355      */
 356     @Override
 357     public boolean isSupported(TemporalField field) {
 358         if (field instanceof ChronoField) {
 359             return field == YEAR || field == MONTH_OF_YEAR ||
 360                     field == PROLEPTIC_MONTH || field == YEAR_OF_ERA || field == ERA;
 361         }
 362         return field != null && field.isSupportedBy(this);
 363     }
 364 
 365     /**
 366      * Checks if the specified unit is supported.
 367      * <p>
 368      * This checks if the specified unit can be added to, or subtracted from, this year-month.
 369      * If false, then calling the {@link #plus(long, TemporalUnit)} and
 370      * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
 371      * <p>
 372      * If the unit is a {@link ChronoUnit} then the query is implemented here.
 373      * The supported units are:
 374      * <ul>
 375      * <li>{@code MONTHS}
 376      * <li>{@code YEARS}
 377      * <li>{@code DECADES}
 378      * <li>{@code CENTURIES}
 379      * <li>{@code MILLENNIA}
 380      * <li>{@code ERAS}
 381      * </ul>
 382      * All other {@code ChronoUnit} instances will return false.
 383      * <p>
 384      * If the unit is not a {@code ChronoUnit}, then the result of this method
 385      * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
 386      * passing {@code this} as the argument.
 387      * Whether the unit is supported is determined by the unit.
 388      *
 389      * @param unit  the unit to check, null returns false
 390      * @return true if the unit can be added/subtracted, false if not
 391      */
 392     @Override
 393     public boolean isSupported(TemporalUnit unit) {
 394         if (unit instanceof ChronoUnit) {
 395             return unit == MONTHS || unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS;
 396         }
 397         return unit != null && unit.isSupportedBy(this);
 398     }
 399 
 400     //-----------------------------------------------------------------------
 401     /**
 402      * Gets the range of valid values for the specified field.
 403      * <p>
 404      * The range object expresses the minimum and maximum valid values for a field.
 405      * This year-month is used to enhance the accuracy of the returned range.
 406      * If it is not possible to return the range, because the field is not supported
 407      * or for some other reason, an exception is thrown.
 408      * <p>
 409      * If the field is a {@link ChronoField} then the query is implemented here.
 410      * The {@link #isSupported(TemporalField) supported fields} will return
 411      * appropriate range instances.
 412      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
 413      * <p>
 414      * If the field is not a {@code ChronoField}, then the result of this method
 415      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
 416      * passing {@code this} as the argument.
 417      * Whether the range can be obtained is determined by the field.
 418      *
 419      * @param field  the field to query the range for, not null
 420      * @return the range of valid values for the field, not null
 421      * @throws DateTimeException if the range for the field cannot be obtained
 422      * @throws UnsupportedTemporalTypeException if the field is not supported
 423      */
 424     @Override
 425     public ValueRange range(TemporalField field) {
 426         if (field == YEAR_OF_ERA) {
 427             return (getYear() <= 0 ? ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE));
 428         }
 429         return Temporal.super.range(field);
 430     }
 431 
 432     /**
 433      * Gets the value of the specified field from this year-month as an {@code int}.
 434      * <p>
 435      * This queries this year-month for the value of the specified field.
 436      * The returned value will always be within the valid range of values for the field.
 437      * If it is not possible to return the value, because the field is not supported
 438      * or for some other reason, an exception is thrown.
 439      * <p>
 440      * If the field is a {@link ChronoField} then the query is implemented here.
 441      * The {@link #isSupported(TemporalField) supported fields} will return valid
 442      * values based on this year-month, except {@code PROLEPTIC_MONTH} which is too
 443      * large to fit in an {@code int} and throw a {@code DateTimeException}.
 444      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
 445      * <p>
 446      * If the field is not a {@code ChronoField}, then the result of this method
 447      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
 448      * passing {@code this} as the argument. Whether the value can be obtained,
 449      * and what the value represents, is determined by the field.
 450      *
 451      * @param field  the field to get, not null
 452      * @return the value for the field
 453      * @throws DateTimeException if a value for the field cannot be obtained or
 454      *         the value is outside the range of valid values for the field
 455      * @throws UnsupportedTemporalTypeException if the field is not supported or
 456      *         the range of values exceeds an {@code int}
 457      * @throws ArithmeticException if numeric overflow occurs
 458      */
 459     @Override  // override for Javadoc
 460     public int get(TemporalField field) {
 461         return range(field).checkValidIntValue(getLong(field), field);
 462     }
 463 
 464     /**
 465      * Gets the value of the specified field from this year-month as a {@code long}.
 466      * <p>
 467      * This queries this year-month for the value of the specified field.
 468      * If it is not possible to return the value, because the field is not supported
 469      * or for some other reason, an exception is thrown.
 470      * <p>
 471      * If the field is a {@link ChronoField} then the query is implemented here.
 472      * The {@link #isSupported(TemporalField) supported fields} will return valid
 473      * values based on this year-month.
 474      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
 475      * <p>
 476      * If the field is not a {@code ChronoField}, then the result of this method
 477      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
 478      * passing {@code this} as the argument. Whether the value can be obtained,
 479      * and what the value represents, is determined by the field.
 480      *
 481      * @param field  the field to get, not null
 482      * @return the value for the field
 483      * @throws DateTimeException if a value for the field cannot be obtained
 484      * @throws UnsupportedTemporalTypeException if the field is not supported
 485      * @throws ArithmeticException if numeric overflow occurs
 486      */
 487     @Override
 488     public long getLong(TemporalField field) {
 489         if (field instanceof ChronoField chronoField) {
 490             return switch (chronoField) {
 491                 case MONTH_OF_YEAR   -> month;
 492                 case PROLEPTIC_MONTH -> getProlepticMonth();
 493                 case YEAR_OF_ERA     -> (year < 1 ? 1 - year : year);
 494                 case YEAR            -> year;
 495                 case ERA             -> (year < 1 ? 0 : 1);
 496                 default -> throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
 497             };
 498         }
 499         return field.getFrom(this);
 500     }
 501 
 502     private long getProlepticMonth() {
 503         return (year * 12L + month - 1);
 504     }
 505 
 506     //-----------------------------------------------------------------------
 507     /**
 508      * Gets the year field.
 509      * <p>
 510      * This method returns the primitive {@code int} value for the year.
 511      * <p>
 512      * The year returned by this method is proleptic as per {@code get(YEAR)}.
 513      *
 514      * @return the year, from MIN_YEAR to MAX_YEAR
 515      */
 516     public int getYear() {
 517         return year;
 518     }
 519 
 520     /**
 521      * Gets the month-of-year field from 1 to 12.
 522      * <p>
 523      * This method returns the month as an {@code int} from 1 to 12.
 524      * Application code is frequently clearer if the enum {@link Month}
 525      * is used by calling {@link #getMonth()}.
 526      *
 527      * @return the month-of-year, from 1 to 12
 528      * @see #getMonth()
 529      */
 530     public int getMonthValue() {
 531         return month;
 532     }
 533 
 534     /**
 535      * Gets the month-of-year field using the {@code Month} enum.
 536      * <p>
 537      * This method returns the enum {@link Month} for the month.
 538      * This avoids confusion as to what {@code int} values mean.
 539      * If you need access to the primitive {@code int} value then the enum
 540      * provides the {@link Month#getValue() int value}.
 541      *
 542      * @return the month-of-year, not null
 543      * @see #getMonthValue()
 544      */
 545     public Month getMonth() {
 546         return Month.of(month);
 547     }
 548 
 549     //-----------------------------------------------------------------------
 550     /**
 551      * Checks if the year is a leap year, according to the ISO proleptic
 552      * calendar system rules.
 553      * <p>
 554      * This method applies the current rules for leap years across the whole time-line.
 555      * In general, a year is a leap year if it is divisible by four without
 556      * remainder. However, years divisible by 100, are not leap years, with
 557      * the exception of years divisible by 400 which are.
 558      * <p>
 559      * For example, 1904 is a leap year it is divisible by 4.
 560      * 1900 was not a leap year as it is divisible by 100, however 2000 was a
 561      * leap year as it is divisible by 400.
 562      * <p>
 563      * The calculation is proleptic - applying the same rules into the far future and far past.
 564      * This is historically inaccurate, but is correct for the ISO-8601 standard.
 565      *
 566      * @return true if the year is leap, false otherwise
 567      */
 568     public boolean isLeapYear() {
 569         return IsoChronology.INSTANCE.isLeapYear(year);
 570     }
 571 
 572     /**
 573      * Checks if the day-of-month is valid for this year-month.
 574      * <p>
 575      * This method checks whether this year and month and the input day form
 576      * a valid date.
 577      *
 578      * @param dayOfMonth  the day-of-month to validate, from 1 to 31, invalid value returns false
 579      * @return true if the day is valid for this year-month
 580      */
 581     public boolean isValidDay(int dayOfMonth) {
 582         return dayOfMonth >= 1 && dayOfMonth <= lengthOfMonth();
 583     }
 584 
 585     /**
 586      * Returns the length of the month, taking account of the year.
 587      * <p>
 588      * This returns the length of the month in days.
 589      * For example, a date in January would return 31.
 590      *
 591      * @return the length of the month in days, from 28 to 31
 592      */
 593     public int lengthOfMonth() {
 594         return getMonth().length(isLeapYear());
 595     }
 596 
 597     /**
 598      * Returns the length of the year.
 599      * <p>
 600      * This returns the length of the year in days, either 365 or 366.
 601      *
 602      * @return 366 if the year is leap, 365 otherwise
 603      */
 604     public int lengthOfYear() {
 605         return (isLeapYear() ? 366 : 365);
 606     }
 607 
 608     //-----------------------------------------------------------------------
 609     /**
 610      * Returns an adjusted copy of this year-month.
 611      * <p>
 612      * This returns a {@code YearMonth}, based on this one, with the year-month adjusted.
 613      * The adjustment takes place using the specified adjuster strategy object.
 614      * Read the documentation of the adjuster to understand what adjustment will be made.
 615      * <p>
 616      * A simple adjuster might simply set the one of the fields, such as the year field.
 617      * A more complex adjuster might set the year-month to the next month that
 618      * Halley's comet will pass the Earth.
 619      * <p>
 620      * The result of this method is obtained by invoking the
 621      * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
 622      * specified adjuster passing {@code this} as the argument.
 623      * <p>
 624      * This instance is immutable and unaffected by this method call.
 625      *
 626      * @param adjuster the adjuster to use, not null
 627      * @return a {@code YearMonth} based on {@code this} with the adjustment made, not null
 628      * @throws DateTimeException if the adjustment cannot be made
 629      * @throws ArithmeticException if numeric overflow occurs
 630      */
 631     @Override
 632     public YearMonth with(TemporalAdjuster adjuster) {
 633         return (YearMonth) adjuster.adjustInto(this);
 634     }
 635 
 636     /**
 637      * Returns a copy of this year-month with the specified field set to a new value.
 638      * <p>
 639      * This returns a {@code YearMonth}, based on this one, with the value
 640      * for the specified field changed.
 641      * This can be used to change any supported field, such as the year or month.
 642      * If it is not possible to set the value, because the field is not supported or for
 643      * some other reason, an exception is thrown.
 644      * <p>
 645      * If the field is a {@link ChronoField} then the adjustment is implemented here.
 646      * The supported fields behave as follows:
 647      * <ul>
 648      * <li>{@code MONTH_OF_YEAR} -
 649      *  Returns a {@code YearMonth} with the specified month-of-year.
 650      *  The year will be unchanged.
 651      * <li>{@code PROLEPTIC_MONTH} -
 652      *  Returns a {@code YearMonth} with the specified proleptic-month.
 653      *  This completely replaces the year and month of this object.
 654      * <li>{@code YEAR_OF_ERA} -
 655      *  Returns a {@code YearMonth} with the specified year-of-era
 656      *  The month and era will be unchanged.
 657      * <li>{@code YEAR} -
 658      *  Returns a {@code YearMonth} with the specified year.
 659      *  The month will be unchanged.
 660      * <li>{@code ERA} -
 661      *  Returns a {@code YearMonth} with the specified era.
 662      *  The month and year-of-era will be unchanged.
 663      * </ul>
 664      * <p>
 665      * In all cases, if the new value is outside the valid range of values for the field
 666      * then a {@code DateTimeException} will be thrown.
 667      * <p>
 668      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
 669      * <p>
 670      * If the field is not a {@code ChronoField}, then the result of this method
 671      * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
 672      * passing {@code this} as the argument. In this case, the field determines
 673      * whether and how to adjust the instant.
 674      * <p>
 675      * This instance is immutable and unaffected by this method call.
 676      *
 677      * @param field  the field to set in the result, not null
 678      * @param newValue  the new value of the field in the result
 679      * @return a {@code YearMonth} based on {@code this} with the specified field set, not null
 680      * @throws DateTimeException if the field cannot be set
 681      * @throws UnsupportedTemporalTypeException if the field is not supported
 682      * @throws ArithmeticException if numeric overflow occurs
 683      */
 684     @Override
 685     public YearMonth with(TemporalField field, long newValue) {
 686         if (field instanceof ChronoField chronoField) {
 687             chronoField.checkValidValue(newValue);
 688             return switch (chronoField) {
 689                 case MONTH_OF_YEAR   -> withMonth((int) newValue);
 690                 case PROLEPTIC_MONTH -> plusMonths(newValue - getProlepticMonth());
 691                 case YEAR_OF_ERA     -> withYear((int) (year < 1 ? 1 - newValue : newValue));
 692                 case YEAR            -> withYear((int) newValue);
 693                 case ERA             -> (getLong(ERA) == newValue ? this : withYear(1 - year));
 694                 default -> throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
 695             };
 696         }
 697         return field.adjustInto(this, newValue);
 698     }
 699 
 700     //-----------------------------------------------------------------------
 701     /**
 702      * Returns a copy of this {@code YearMonth} with the year altered.
 703      * <p>
 704      * This instance is immutable and unaffected by this method call.
 705      *
 706      * @param year  the year to set in the returned year-month, from MIN_YEAR to MAX_YEAR
 707      * @return a {@code YearMonth} based on this year-month with the requested year, not null
 708      * @throws DateTimeException if the year value is invalid
 709      */
 710     public YearMonth withYear(int year) {
 711         YEAR.checkValidValue(year);
 712         return with(year, month);
 713     }
 714 
 715     /**
 716      * Returns a copy of this {@code YearMonth} with the month-of-year altered.
 717      * <p>
 718      * This instance is immutable and unaffected by this method call.
 719      *
 720      * @param month  the month-of-year to set in the returned year-month, from 1 (January) to 12 (December)
 721      * @return a {@code YearMonth} based on this year-month with the requested month, not null
 722      * @throws DateTimeException if the month-of-year value is invalid
 723      */
 724     public YearMonth withMonth(int month) {
 725         MONTH_OF_YEAR.checkValidValue(month);
 726         return with(year, month);
 727     }
 728 
 729     //-----------------------------------------------------------------------
 730     /**
 731      * Returns a copy of this year-month with the specified amount added.
 732      * <p>
 733      * This returns a {@code YearMonth}, based on this one, with the specified amount added.
 734      * The amount is typically {@link Period} but may be any other type implementing
 735      * the {@link TemporalAmount} interface.
 736      * <p>
 737      * The calculation is delegated to the amount object by calling
 738      * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
 739      * to implement the addition in any way it wishes, however it typically
 740      * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
 741      * of the amount implementation to determine if it can be successfully added.
 742      * <p>
 743      * This instance is immutable and unaffected by this method call.
 744      *
 745      * @param amountToAdd  the amount to add, not null
 746      * @return a {@code YearMonth} based on this year-month with the addition made, not null
 747      * @throws DateTimeException if the addition cannot be made
 748      * @throws ArithmeticException if numeric overflow occurs
 749      */
 750     @Override
 751     public YearMonth plus(TemporalAmount amountToAdd) {
 752         return (YearMonth) amountToAdd.addTo(this);
 753     }
 754 
 755     /**
 756      * Returns a copy of this year-month with the specified amount added.
 757      * <p>
 758      * This returns a {@code YearMonth}, based on this one, with the amount
 759      * in terms of the unit added. If it is not possible to add the amount, because the
 760      * unit is not supported or for some other reason, an exception is thrown.
 761      * <p>
 762      * If the field is a {@link ChronoUnit} then the addition is implemented here.
 763      * The supported fields behave as follows:
 764      * <ul>
 765      * <li>{@code MONTHS} -
 766      *  Returns a {@code YearMonth} with the specified number of months added.
 767      *  This is equivalent to {@link #plusMonths(long)}.
 768      * <li>{@code YEARS} -
 769      *  Returns a {@code YearMonth} with the specified number of years added.
 770      *  This is equivalent to {@link #plusYears(long)}.
 771      * <li>{@code DECADES} -
 772      *  Returns a {@code YearMonth} with the specified number of decades added.
 773      *  This is equivalent to calling {@link #plusYears(long)} with the amount
 774      *  multiplied by 10.
 775      * <li>{@code CENTURIES} -
 776      *  Returns a {@code YearMonth} with the specified number of centuries added.
 777      *  This is equivalent to calling {@link #plusYears(long)} with the amount
 778      *  multiplied by 100.
 779      * <li>{@code MILLENNIA} -
 780      *  Returns a {@code YearMonth} with the specified number of millennia added.
 781      *  This is equivalent to calling {@link #plusYears(long)} with the amount
 782      *  multiplied by 1,000.
 783      * <li>{@code ERAS} -
 784      *  Returns a {@code YearMonth} with the specified number of eras added.
 785      *  Only two eras are supported so the amount must be one, zero or minus one.
 786      *  If the amount is non-zero then the year is changed such that the year-of-era
 787      *  is unchanged.
 788      * </ul>
 789      * <p>
 790      * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
 791      * <p>
 792      * If the field is not a {@code ChronoUnit}, then the result of this method
 793      * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
 794      * passing {@code this} as the argument. In this case, the unit determines
 795      * whether and how to perform the addition.
 796      * <p>
 797      * This instance is immutable and unaffected by this method call.
 798      *
 799      * @param amountToAdd  the amount of the unit to add to the result, may be negative
 800      * @param unit  the unit of the amount to add, not null
 801      * @return a {@code YearMonth} based on this year-month with the specified amount added, not null
 802      * @throws DateTimeException if the addition cannot be made
 803      * @throws UnsupportedTemporalTypeException if the unit is not supported
 804      * @throws ArithmeticException if numeric overflow occurs
 805      */
 806     @Override
 807     public YearMonth plus(long amountToAdd, TemporalUnit unit) {
 808         if (unit instanceof ChronoUnit chronoUnit) {
 809             return switch (chronoUnit) {
 810                 case MONTHS    -> plusMonths(amountToAdd);
 811                 case YEARS     -> plusYears(amountToAdd);
 812                 case DECADES   -> plusYears(Math.multiplyExact(amountToAdd, 10));
 813                 case CENTURIES -> plusYears(Math.multiplyExact(amountToAdd, 100));
 814                 case MILLENNIA -> plusYears(Math.multiplyExact(amountToAdd, 1000));
 815                 case ERAS      -> with(ERA, Math.addExact(getLong(ERA), amountToAdd));
 816                 default -> throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
 817             };
 818         }
 819         return unit.addTo(this, amountToAdd);
 820     }
 821 
 822     /**
 823      * Returns a copy of this {@code YearMonth} with the specified number of years added.
 824      * <p>
 825      * This instance is immutable and unaffected by this method call.
 826      *
 827      * @param yearsToAdd  the years to add, may be negative
 828      * @return a {@code YearMonth} based on this year-month with the years added, not null
 829      * @throws DateTimeException if the result exceeds the supported range
 830      */
 831     public YearMonth plusYears(long yearsToAdd) {
 832         if (yearsToAdd == 0) {
 833             return this;
 834         }
 835         int newYear = YEAR.checkValidIntValue(year + yearsToAdd);  // safe overflow
 836         return with(newYear, month);
 837     }
 838 
 839     /**
 840      * Returns a copy of this {@code YearMonth} with the specified number of months added.
 841      * <p>
 842      * This instance is immutable and unaffected by this method call.
 843      *
 844      * @param monthsToAdd  the months to add, may be negative
 845      * @return a {@code YearMonth} based on this year-month with the months added, not null
 846      * @throws DateTimeException if the result exceeds the supported range
 847      */
 848     public YearMonth plusMonths(long monthsToAdd) {
 849         if (monthsToAdd == 0) {
 850             return this;
 851         }
 852         long monthCount = year * 12L + (month - 1);
 853         long calcMonths = monthCount + monthsToAdd;  // safe overflow
 854         int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcMonths, 12));
 855         int newMonth = Math.floorMod(calcMonths, 12) + 1;
 856         return with(newYear, newMonth);
 857     }
 858 
 859     //-----------------------------------------------------------------------
 860     /**
 861      * Returns a copy of this year-month with the specified amount subtracted.
 862      * <p>
 863      * This returns a {@code YearMonth}, based on this one, with the specified amount subtracted.
 864      * The amount is typically {@link Period} but may be any other type implementing
 865      * the {@link TemporalAmount} interface.
 866      * <p>
 867      * The calculation is delegated to the amount object by calling
 868      * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
 869      * to implement the subtraction in any way it wishes, however it typically
 870      * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
 871      * of the amount implementation to determine if it can be successfully subtracted.
 872      * <p>
 873      * This instance is immutable and unaffected by this method call.
 874      *
 875      * @param amountToSubtract  the amount to subtract, not null
 876      * @return a {@code YearMonth} based on this year-month with the subtraction made, not null
 877      * @throws DateTimeException if the subtraction cannot be made
 878      * @throws ArithmeticException if numeric overflow occurs
 879      */
 880     @Override
 881     public YearMonth minus(TemporalAmount amountToSubtract) {
 882         return (YearMonth) amountToSubtract.subtractFrom(this);
 883     }
 884 
 885     /**
 886      * Returns a copy of this year-month with the specified amount subtracted.
 887      * <p>
 888      * This returns a {@code YearMonth}, based on this one, with the amount
 889      * in terms of the unit subtracted. If it is not possible to subtract the amount,
 890      * because the unit is not supported or for some other reason, an exception is thrown.
 891      * <p>
 892      * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
 893      * See that method for a full description of how addition, and thus subtraction, works.
 894      * <p>
 895      * This instance is immutable and unaffected by this method call.
 896      *
 897      * @param amountToSubtract  the amount of the unit to subtract from the result, may be negative
 898      * @param unit  the unit of the amount to subtract, not null
 899      * @return a {@code YearMonth} based on this year-month with the specified amount subtracted, not null
 900      * @throws DateTimeException if the subtraction cannot be made
 901      * @throws UnsupportedTemporalTypeException if the unit is not supported
 902      * @throws ArithmeticException if numeric overflow occurs
 903      */
 904     @Override
 905     public YearMonth minus(long amountToSubtract, TemporalUnit unit) {
 906         return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
 907     }
 908 
 909     /**
 910      * Returns a copy of this {@code YearMonth} with the specified number of years subtracted.
 911      * <p>
 912      * This instance is immutable and unaffected by this method call.
 913      *
 914      * @param yearsToSubtract  the years to subtract, may be negative
 915      * @return a {@code YearMonth} based on this year-month with the years subtracted, not null
 916      * @throws DateTimeException if the result exceeds the supported range
 917      */
 918     public YearMonth minusYears(long yearsToSubtract) {
 919         return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
 920     }
 921 
 922     /**
 923      * Returns a copy of this {@code YearMonth} with the specified number of months subtracted.
 924      * <p>
 925      * This instance is immutable and unaffected by this method call.
 926      *
 927      * @param monthsToSubtract  the months to subtract, may be negative
 928      * @return a {@code YearMonth} based on this year-month with the months subtracted, not null
 929      * @throws DateTimeException if the result exceeds the supported range
 930      */
 931     public YearMonth minusMonths(long monthsToSubtract) {
 932         return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));
 933     }
 934 
 935     //-----------------------------------------------------------------------
 936     /**
 937      * Queries this year-month using the specified query.
 938      * <p>
 939      * This queries this year-month using the specified query strategy object.
 940      * The {@code TemporalQuery} object defines the logic to be used to
 941      * obtain the result. Read the documentation of the query to understand
 942      * what the result of this method will be.
 943      * <p>
 944      * The result of this method is obtained by invoking the
 945      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
 946      * specified query passing {@code this} as the argument.
 947      *
 948      * @param <R> the type of the result
 949      * @param query  the query to invoke, not null
 950      * @return the query result, null may be returned (defined by the query)
 951      * @throws DateTimeException if unable to query (defined by the query)
 952      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
 953      */
 954     @SuppressWarnings("unchecked")
 955     @Override
 956     public <R> R query(TemporalQuery<R> query) {
 957         if (query == TemporalQueries.chronology()) {
 958             return (R) IsoChronology.INSTANCE;
 959         } else if (query == TemporalQueries.precision()) {
 960             return (R) MONTHS;
 961         }
 962         return Temporal.super.query(query);
 963     }
 964 
 965     /**
 966      * Adjusts the specified temporal object to have this year-month.
 967      * <p>
 968      * This returns a temporal object of the same observable type as the input
 969      * with the year and month changed to be the same as this.
 970      * <p>
 971      * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
 972      * passing {@link ChronoField#PROLEPTIC_MONTH} as the field.
 973      * If the specified temporal object does not use the ISO calendar system then
 974      * a {@code DateTimeException} is thrown.
 975      * <p>
 976      * In most cases, it is clearer to reverse the calling pattern by using
 977      * {@link Temporal#with(TemporalAdjuster)}:
 978      * <pre>
 979      *   // these two lines are equivalent, but the second approach is recommended
 980      *   temporal = thisYearMonth.adjustInto(temporal);
 981      *   temporal = temporal.with(thisYearMonth);
 982      * </pre>
 983      * <p>
 984      * This instance is immutable and unaffected by this method call.
 985      *
 986      * @param temporal  the target object to be adjusted, not null
 987      * @return the adjusted object, not null
 988      * @throws DateTimeException if unable to make the adjustment
 989      * @throws ArithmeticException if numeric overflow occurs
 990      */
 991     @Override
 992     public Temporal adjustInto(Temporal temporal) {
 993         if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) {
 994             throw new DateTimeException("Adjustment only supported on ISO date-time");
 995         }
 996         return temporal.with(PROLEPTIC_MONTH, getProlepticMonth());
 997     }
 998 
 999     /**
1000      * Calculates the amount of time until another year-month in terms of the specified unit.
1001      * <p>
1002      * This calculates the amount of time between two {@code YearMonth}
1003      * objects in terms of a single {@code TemporalUnit}.
1004      * The start and end points are {@code this} and the specified year-month.
1005      * The result will be negative if the end is before the start.
1006      * The {@code Temporal} passed to this method is converted to a
1007      * {@code YearMonth} using {@link #from(TemporalAccessor)}.
1008      * For example, the amount in years between two year-months can be calculated
1009      * using {@code startYearMonth.until(endYearMonth, YEARS)}.
1010      * <p>
1011      * The calculation returns a whole number, representing the number of
1012      * complete units between the two year-months.
1013      * For example, the amount in decades between 2012-06 and 2032-05
1014      * will only be one decade as it is one month short of two decades.
1015      * <p>
1016      * There are two equivalent ways of using this method.
1017      * The first is to invoke this method.
1018      * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
1019      * <pre>
1020      *   // these two lines are equivalent
1021      *   amount = start.until(end, MONTHS);
1022      *   amount = MONTHS.between(start, end);
1023      * </pre>
1024      * The choice should be made based on which makes the code more readable.
1025      * <p>
1026      * The calculation is implemented in this method for {@link ChronoUnit}.
1027      * The units {@code MONTHS}, {@code YEARS}, {@code DECADES},
1028      * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
1029      * Other {@code ChronoUnit} values will throw an exception.
1030      * <p>
1031      * If the unit is not a {@code ChronoUnit}, then the result of this method
1032      * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
1033      * passing {@code this} as the first argument and the converted input temporal
1034      * as the second argument.
1035      * <p>
1036      * This instance is immutable and unaffected by this method call.
1037      *
1038      * @param endExclusive  the end date, exclusive, which is converted to a {@code YearMonth}, not null
1039      * @param unit  the unit to measure the amount in, not null
1040      * @return the amount of time between this year-month and the end year-month
1041      * @throws DateTimeException if the amount cannot be calculated, or the end
1042      *  temporal cannot be converted to a {@code YearMonth}
1043      * @throws UnsupportedTemporalTypeException if the unit is not supported
1044      * @throws ArithmeticException if numeric overflow occurs
1045      */
1046     @Override
1047     public long until(Temporal endExclusive, TemporalUnit unit) {
1048         YearMonth end = YearMonth.from(endExclusive);
1049         if (unit instanceof ChronoUnit chronoUnit) {
1050             long monthsUntil = end.getProlepticMonth() - getProlepticMonth();  // no overflow
1051             return switch (chronoUnit) {
1052                 case MONTHS    -> monthsUntil;
1053                 case YEARS     -> monthsUntil / 12;
1054                 case DECADES   -> monthsUntil / 120;
1055                 case CENTURIES -> monthsUntil / 1200;
1056                 case MILLENNIA -> monthsUntil / 12000;
1057                 case ERAS      -> end.getLong(ERA) - getLong(ERA);
1058                 default -> throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
1059             };
1060         }
1061         return unit.between(this, end);
1062     }
1063 
1064     /**
1065      * Formats this year-month using the specified formatter.
1066      * <p>
1067      * This year-month will be passed to the formatter to produce a string.
1068      *
1069      * @param formatter  the formatter to use, not null
1070      * @return the formatted year-month string, not null
1071      * @throws DateTimeException if an error occurs during printing
1072      */
1073     public String format(DateTimeFormatter formatter) {
1074         Objects.requireNonNull(formatter, "formatter");
1075         return formatter.format(this);
1076     }
1077 
1078     //-----------------------------------------------------------------------
1079     /**
1080      * Combines this year-month with a day-of-month to create a {@code LocalDate}.
1081      * <p>
1082      * This returns a {@code LocalDate} formed from this year-month and the specified day-of-month.
1083      * <p>
1084      * The day-of-month value must be valid for the year-month.
1085      * <p>
1086      * This method can be used as part of a chain to produce a date:
1087      * <pre>
1088      *  LocalDate date = year.atMonth(month).atDay(day);
1089      * </pre>
1090      *
1091      * @param dayOfMonth  the day-of-month to use, from 1 to 31
1092      * @return the date formed from this year-month and the specified day, not null
1093      * @throws DateTimeException if the day is invalid for the year-month
1094      * @see #isValidDay(int)
1095      */
1096     public LocalDate atDay(int dayOfMonth) {
1097         return LocalDate.of(year, month, dayOfMonth);
1098     }
1099 
1100     /**
1101      * Returns a {@code LocalDate} at the end of the month.
1102      * <p>
1103      * This returns a {@code LocalDate} based on this year-month.
1104      * The day-of-month is set to the last valid day of the month, taking
1105      * into account leap years.
1106      * <p>
1107      * This method can be used as part of a chain to produce a date:
1108      * <pre>
1109      *  LocalDate date = year.atMonth(month).atEndOfMonth();
1110      * </pre>
1111      *
1112      * @return the last valid date of this year-month, not null
1113      */
1114     public LocalDate atEndOfMonth() {
1115         return LocalDate.of(year, month, lengthOfMonth());
1116     }
1117 
1118     //-----------------------------------------------------------------------
1119     /**
1120      * Compares this year-month to another year-month.
1121      * <p>
1122      * The comparison is based first on the value of the year, then on the value of the month.
1123      * It is "consistent with equals", as defined by {@link Comparable}.
1124      *
1125      * @param other  the other year-month to compare to, not null
1126      * @return the comparator value, that is less than zero if this is before {@code other},
1127      *          zero if they are equal, greater than zero if this is after {@code other}
1128      * @see #isBefore
1129      * @see #isAfter
1130      */
1131     @Override
1132     public int compareTo(YearMonth other) {
1133         int cmp = (year - other.year);
1134         if (cmp == 0) {
1135             cmp = (month - other.month);
1136         }
1137         return cmp;
1138     }
1139 
1140     /**
1141      * Checks if this year-month is after the specified year-month.
1142      *
1143      * @param other  the other year-month to compare to, not null
1144      * @return true if this is after the specified year-month
1145      */
1146     public boolean isAfter(YearMonth other) {
1147         return compareTo(other) > 0;
1148     }
1149 
1150     /**
1151      * Checks if this year-month is before the specified year-month.
1152      *
1153      * @param other  the other year-month to compare to, not null
1154      * @return true if this point is before the specified year-month
1155      */
1156     public boolean isBefore(YearMonth other) {
1157         return compareTo(other) < 0;
1158     }
1159 
1160     //-----------------------------------------------------------------------
1161     /**
1162      * Checks if this year-month is equal to another year-month.
1163      * <p>
1164      * The comparison is based on the time-line position of the year-months.
1165      *
1166      * @param obj  the object to check, null returns false
1167      * @return true if this is equal to the other year-month
1168      */
1169     @Override
1170     public boolean equals(Object obj) {
1171         if (this == obj) {
1172             return true;
1173         }
1174         return (obj instanceof YearMonth other)
1175                 && year == other.year
1176                 && month == other.month;
1177     }
1178 
1179     /**
1180      * A hash code for this year-month.
1181      *
1182      * @return a suitable hash code
1183      */
1184     @Override
1185     public int hashCode() {
1186         return year ^ (month << 27);
1187     }
1188 
1189     //-----------------------------------------------------------------------
1190     /**
1191      * Outputs this year-month as a {@code String}, such as {@code 2007-12}.
1192      * <p>
1193      * The output will be in the format {@code uuuu-MM}:
1194      *
1195      * @return a string representation of this year-month, not null
1196      */
1197     @Override
1198     public String toString() {
1199         int absYear = Math.abs(year);
1200         StringBuilder buf = new StringBuilder(9);
1201         if (absYear < 1000) {
1202             if (year < 0) {
1203                 buf.append(year - 10000).deleteCharAt(1);
1204             } else {
1205                 buf.append(year + 10000).deleteCharAt(0);
1206             }
1207         } else {
1208             buf.append(year);
1209         }
1210         return buf.append(month < 10 ? "-0" : "-")
1211             .append(month)
1212             .toString();
1213     }
1214 
1215     //-----------------------------------------------------------------------
1216     /**
1217      * Writes the object using a
1218      * <a href="{@docRoot}/serialized-form.html#java.time.Ser">dedicated serialized form</a>.
1219      * @serialData
1220      * <pre>
1221      *  out.writeByte(12);  // identifies a YearMonth
1222      *  out.writeInt(year);
1223      *  out.writeByte(month);
1224      * </pre>
1225      *
1226      * @return the instance of {@code Ser}, not null
1227      */
1228     @java.io.Serial
1229     private Object writeReplace() {
1230         return new Ser(Ser.YEAR_MONTH_TYPE, this);
1231     }
1232 
1233     /**
1234      * Defend against malicious streams.
1235      *
1236      * @param s the stream to read
1237      * @throws InvalidObjectException always
1238      */
1239     @java.io.Serial
1240     private void readObject(ObjectInputStream s) throws InvalidObjectException {
1241         throw new InvalidObjectException("Deserialization via serialization delegate");
1242     }
1243 
1244     void writeExternal(DataOutput out) throws IOException {
1245         out.writeInt(year);
1246         out.writeByte(month);
1247     }
1248 
1249     static YearMonth readExternal(DataInput in) throws IOException {
1250         int year = in.readInt();
1251         byte month = in.readByte();
1252         return YearMonth.of(year, month);
1253     }
1254 
1255 }