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