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