Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/time/MonthDay.java
38829 views
/*1* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file:30*31* Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos32*33* All rights reserved.34*35* Redistribution and use in source and binary forms, with or without36* modification, are permitted provided that the following conditions are met:37*38* * Redistributions of source code must retain the above copyright notice,39* this list of conditions and the following disclaimer.40*41* * Redistributions in binary form must reproduce the above copyright notice,42* this list of conditions and the following disclaimer in the documentation43* and/or other materials provided with the distribution.44*45* * Neither the name of JSR-310 nor the names of its contributors46* may be used to endorse or promote products derived from this software47* without specific prior written permission.48*49* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS50* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT51* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR52* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR53* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,54* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,55* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR56* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF57* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING58* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS59* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.60*/61package java.time;6263import static java.time.temporal.ChronoField.DAY_OF_MONTH;64import static java.time.temporal.ChronoField.MONTH_OF_YEAR;6566import java.io.DataInput;67import java.io.DataOutput;68import java.io.IOException;69import java.io.InvalidObjectException;70import java.io.ObjectInputStream;71import java.io.Serializable;72import java.time.chrono.Chronology;73import java.time.chrono.IsoChronology;74import java.time.format.DateTimeFormatter;75import java.time.format.DateTimeFormatterBuilder;76import java.time.format.DateTimeParseException;77import java.time.temporal.ChronoField;78import java.time.temporal.Temporal;79import java.time.temporal.TemporalAccessor;80import java.time.temporal.TemporalAdjuster;81import java.time.temporal.TemporalField;82import java.time.temporal.TemporalQueries;83import java.time.temporal.TemporalQuery;84import java.time.temporal.UnsupportedTemporalTypeException;85import java.time.temporal.ValueRange;86import java.util.Objects;8788/**89* A month-day in the ISO-8601 calendar system, such as {@code --12-03}.90* <p>91* {@code MonthDay} is an immutable date-time object that represents the combination92* of a month and day-of-month. Any field that can be derived from a month and day,93* such as quarter-of-year, can be obtained.94* <p>95* This class does not store or represent a year, time or time-zone.96* For example, the value "December 3rd" can be stored in a {@code MonthDay}.97* <p>98* Since a {@code MonthDay} does not possess a year, the leap day of99* February 29th is considered valid.100* <p>101* This class implements {@link TemporalAccessor} rather than {@link Temporal}.102* This is because it is not possible to define whether February 29th is valid or not103* without external information, preventing the implementation of plus/minus.104* Related to this, {@code MonthDay} only provides access to query and set the fields105* {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH}.106* <p>107* The ISO-8601 calendar system is the modern civil calendar system used today108* in most of the world. It is equivalent to the proleptic Gregorian calendar109* system, in which today's rules for leap years are applied for all time.110* For most applications written today, the ISO-8601 rules are entirely suitable.111* However, any application that makes use of historical dates, and requires them112* to be accurate will find the ISO-8601 approach unsuitable.113*114* <p>115* This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>116* class; use of identity-sensitive operations (including reference equality117* ({@code ==}), identity hash code, or synchronization) on instances of118* {@code MonthDay} may have unpredictable results and should be avoided.119* The {@code equals} method should be used for comparisons.120*121* @implSpec122* This class is immutable and thread-safe.123*124* @since 1.8125*/126public final class MonthDay127implements TemporalAccessor, TemporalAdjuster, Comparable<MonthDay>, Serializable {128129/**130* Serialization version.131*/132private static final long serialVersionUID = -939150713474957432L;133/**134* Parser.135*/136private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()137.appendLiteral("--")138.appendValue(MONTH_OF_YEAR, 2)139.appendLiteral('-')140.appendValue(DAY_OF_MONTH, 2)141.toFormatter();142143/**144* The month-of-year, not null.145*/146private final int month;147/**148* The day-of-month.149*/150private final int day;151152//-----------------------------------------------------------------------153/**154* Obtains the current month-day from the system clock in the default time-zone.155* <p>156* This will query the {@link Clock#systemDefaultZone() system clock} in the default157* time-zone to obtain the current month-day.158* <p>159* Using this method will prevent the ability to use an alternate clock for testing160* because the clock is hard-coded.161*162* @return the current month-day using the system clock and default time-zone, not null163*/164public static MonthDay now() {165return now(Clock.systemDefaultZone());166}167168/**169* Obtains the current month-day from the system clock in the specified time-zone.170* <p>171* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current month-day.172* Specifying the time-zone avoids dependence on the default time-zone.173* <p>174* Using this method will prevent the ability to use an alternate clock for testing175* because the clock is hard-coded.176*177* @param zone the zone ID to use, not null178* @return the current month-day using the system clock, not null179*/180public static MonthDay now(ZoneId zone) {181return now(Clock.system(zone));182}183184/**185* Obtains the current month-day from the specified clock.186* <p>187* This will query the specified clock to obtain the current month-day.188* Using this method allows the use of an alternate clock for testing.189* The alternate clock may be introduced using {@link Clock dependency injection}.190*191* @param clock the clock to use, not null192* @return the current month-day, not null193*/194public static MonthDay now(Clock clock) {195final LocalDate now = LocalDate.now(clock); // called once196return MonthDay.of(now.getMonth(), now.getDayOfMonth());197}198199//-----------------------------------------------------------------------200/**201* Obtains an instance of {@code MonthDay}.202* <p>203* The day-of-month must be valid for the month within a leap year.204* Hence, for February, day 29 is valid.205* <p>206* For example, passing in April and day 31 will throw an exception, as207* there can never be April 31st in any year. By contrast, passing in208* February 29th is permitted, as that month-day can sometimes be valid.209*210* @param month the month-of-year to represent, not null211* @param dayOfMonth the day-of-month to represent, from 1 to 31212* @return the month-day, not null213* @throws DateTimeException if the value of any field is out of range,214* or if the day-of-month is invalid for the month215*/216public static MonthDay of(Month month, int dayOfMonth) {217Objects.requireNonNull(month, "month");218DAY_OF_MONTH.checkValidValue(dayOfMonth);219if (dayOfMonth > month.maxLength()) {220throw new DateTimeException("Illegal value for DayOfMonth field, value " + dayOfMonth +221" is not valid for month " + month.name());222}223return new MonthDay(month.getValue(), dayOfMonth);224}225226/**227* Obtains an instance of {@code MonthDay}.228* <p>229* The day-of-month must be valid for the month within a leap year.230* Hence, for month 2 (February), day 29 is valid.231* <p>232* For example, passing in month 4 (April) and day 31 will throw an exception, as233* there can never be April 31st in any year. By contrast, passing in234* February 29th is permitted, as that month-day can sometimes be valid.235*236* @param month the month-of-year to represent, from 1 (January) to 12 (December)237* @param dayOfMonth the day-of-month to represent, from 1 to 31238* @return the month-day, not null239* @throws DateTimeException if the value of any field is out of range,240* or if the day-of-month is invalid for the month241*/242public static MonthDay of(int month, int dayOfMonth) {243return of(Month.of(month), dayOfMonth);244}245246//-----------------------------------------------------------------------247/**248* Obtains an instance of {@code MonthDay} from a temporal object.249* <p>250* This obtains a month-day based on the specified temporal.251* A {@code TemporalAccessor} represents an arbitrary set of date and time information,252* which this factory converts to an instance of {@code MonthDay}.253* <p>254* The conversion extracts the {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and255* {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} fields.256* The extraction is only permitted if the temporal object has an ISO257* chronology, or can be converted to a {@code LocalDate}.258* <p>259* This method matches the signature of the functional interface {@link TemporalQuery}260* allowing it to be used as a query via method reference, {@code MonthDay::from}.261*262* @param temporal the temporal object to convert, not null263* @return the month-day, not null264* @throws DateTimeException if unable to convert to a {@code MonthDay}265*/266public static MonthDay from(TemporalAccessor temporal) {267if (temporal instanceof MonthDay) {268return (MonthDay) temporal;269}270try {271if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {272temporal = LocalDate.from(temporal);273}274return of(temporal.get(MONTH_OF_YEAR), temporal.get(DAY_OF_MONTH));275} catch (DateTimeException ex) {276throw new DateTimeException("Unable to obtain MonthDay from TemporalAccessor: " +277temporal + " of type " + temporal.getClass().getName(), ex);278}279}280281//-----------------------------------------------------------------------282/**283* Obtains an instance of {@code MonthDay} from a text string such as {@code --12-03}.284* <p>285* The string must represent a valid month-day.286* The format is {@code --MM-dd}.287*288* @param text the text to parse such as "--12-03", not null289* @return the parsed month-day, not null290* @throws DateTimeParseException if the text cannot be parsed291*/292public static MonthDay parse(CharSequence text) {293return parse(text, PARSER);294}295296/**297* Obtains an instance of {@code MonthDay} from a text string using a specific formatter.298* <p>299* The text is parsed using the formatter, returning a month-day.300*301* @param text the text to parse, not null302* @param formatter the formatter to use, not null303* @return the parsed month-day, not null304* @throws DateTimeParseException if the text cannot be parsed305*/306public static MonthDay parse(CharSequence text, DateTimeFormatter formatter) {307Objects.requireNonNull(formatter, "formatter");308return formatter.parse(text, MonthDay::from);309}310311//-----------------------------------------------------------------------312/**313* Constructor, previously validated.314*315* @param month the month-of-year to represent, validated from 1 to 12316* @param dayOfMonth the day-of-month to represent, validated from 1 to 29-31317*/318private MonthDay(int month, int dayOfMonth) {319this.month = month;320this.day = dayOfMonth;321}322323//-----------------------------------------------------------------------324/**325* Checks if the specified field is supported.326* <p>327* This checks if this month-day can be queried for the specified field.328* If false, then calling the {@link #range(TemporalField) range} and329* {@link #get(TemporalField) get} methods will throw an exception.330* <p>331* If the field is a {@link ChronoField} then the query is implemented here.332* The supported fields are:333* <ul>334* <li>{@code MONTH_OF_YEAR}335* <li>{@code YEAR}336* </ul>337* All other {@code ChronoField} instances will return false.338* <p>339* If the field is not a {@code ChronoField}, then the result of this method340* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}341* passing {@code this} as the argument.342* Whether the field is supported is determined by the field.343*344* @param field the field to check, null returns false345* @return true if the field is supported on this month-day, false if not346*/347@Override348public boolean isSupported(TemporalField field) {349if (field instanceof ChronoField) {350return field == MONTH_OF_YEAR || field == DAY_OF_MONTH;351}352return field != null && field.isSupportedBy(this);353}354355/**356* Gets the range of valid values for the specified field.357* <p>358* The range object expresses the minimum and maximum valid values for a field.359* This month-day is used to enhance the accuracy of the returned range.360* If it is not possible to return the range, because the field is not supported361* or for some other reason, an exception is thrown.362* <p>363* If the field is a {@link ChronoField} then the query is implemented here.364* The {@link #isSupported(TemporalField) supported fields} will return365* appropriate range instances.366* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.367* <p>368* If the field is not a {@code ChronoField}, then the result of this method369* is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}370* passing {@code this} as the argument.371* Whether the range can be obtained is determined by the field.372*373* @param field the field to query the range for, not null374* @return the range of valid values for the field, not null375* @throws DateTimeException if the range for the field cannot be obtained376* @throws UnsupportedTemporalTypeException if the field is not supported377*/378@Override379public ValueRange range(TemporalField field) {380if (field == MONTH_OF_YEAR) {381return field.range();382} else if (field == DAY_OF_MONTH) {383return ValueRange.of(1, getMonth().minLength(), getMonth().maxLength());384}385return TemporalAccessor.super.range(field);386}387388/**389* Gets the value of the specified field from this month-day as an {@code int}.390* <p>391* This queries this month-day for the value of the specified field.392* The returned value will always be within the valid range of values for the field.393* If it is not possible to return the value, because the field is not supported394* or for some other reason, an exception is thrown.395* <p>396* If the field is a {@link ChronoField} then the query is implemented here.397* The {@link #isSupported(TemporalField) supported fields} will return valid398* values based on this month-day.399* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.400* <p>401* If the field is not a {@code ChronoField}, then the result of this method402* is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}403* passing {@code this} as the argument. Whether the value can be obtained,404* and what the value represents, is determined by the field.405*406* @param field the field to get, not null407* @return the value for the field408* @throws DateTimeException if a value for the field cannot be obtained or409* the value is outside the range of valid values for the field410* @throws UnsupportedTemporalTypeException if the field is not supported or411* the range of values exceeds an {@code int}412* @throws ArithmeticException if numeric overflow occurs413*/414@Override // override for Javadoc415public int get(TemporalField field) {416return range(field).checkValidIntValue(getLong(field), field);417}418419/**420* Gets the value of the specified field from this month-day as a {@code long}.421* <p>422* This queries this month-day for the value of the specified field.423* If it is not possible to return the value, because the field is not supported424* or for some other reason, an exception is thrown.425* <p>426* If the field is a {@link ChronoField} then the query is implemented here.427* The {@link #isSupported(TemporalField) supported fields} will return valid428* values based on this month-day.429* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.430* <p>431* If the field is not a {@code ChronoField}, then the result of this method432* is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}433* passing {@code this} as the argument. Whether the value can be obtained,434* and what the value represents, is determined by the field.435*436* @param field the field to get, not null437* @return the value for the field438* @throws DateTimeException if a value for the field cannot be obtained439* @throws UnsupportedTemporalTypeException if the field is not supported440* @throws ArithmeticException if numeric overflow occurs441*/442@Override443public long getLong(TemporalField field) {444if (field instanceof ChronoField) {445switch ((ChronoField) field) {446// alignedDOW and alignedWOM not supported because they cannot be set in with()447case DAY_OF_MONTH: return day;448case MONTH_OF_YEAR: return month;449}450throw new UnsupportedTemporalTypeException("Unsupported field: " + field);451}452return field.getFrom(this);453}454455//-----------------------------------------------------------------------456/**457* Gets the month-of-year field from 1 to 12.458* <p>459* This method returns the month as an {@code int} from 1 to 12.460* Application code is frequently clearer if the enum {@link Month}461* is used by calling {@link #getMonth()}.462*463* @return the month-of-year, from 1 to 12464* @see #getMonth()465*/466public int getMonthValue() {467return month;468}469470/**471* Gets the month-of-year field using the {@code Month} enum.472* <p>473* This method returns the enum {@link Month} for the month.474* This avoids confusion as to what {@code int} values mean.475* If you need access to the primitive {@code int} value then the enum476* provides the {@link Month#getValue() int value}.477*478* @return the month-of-year, not null479* @see #getMonthValue()480*/481public Month getMonth() {482return Month.of(month);483}484485/**486* Gets the day-of-month field.487* <p>488* This method returns the primitive {@code int} value for the day-of-month.489*490* @return the day-of-month, from 1 to 31491*/492public int getDayOfMonth() {493return day;494}495496//-----------------------------------------------------------------------497/**498* Checks if the year is valid for this month-day.499* <p>500* This method checks whether this month and day and the input year form501* a valid date. This can only return false for February 29th.502*503* @param year the year to validate504* @return true if the year is valid for this month-day505* @see Year#isValidMonthDay(MonthDay)506*/507public boolean isValidYear(int year) {508return (day == 29 && month == 2 && Year.isLeap(year) == false) == false;509}510511//-----------------------------------------------------------------------512/**513* Returns a copy of this {@code MonthDay} with the month-of-year altered.514* <p>515* This returns a month-day with the specified month.516* If the day-of-month is invalid for the specified month, the day will517* be adjusted to the last valid day-of-month.518* <p>519* This instance is immutable and unaffected by this method call.520*521* @param month the month-of-year to set in the returned month-day, from 1 (January) to 12 (December)522* @return a {@code MonthDay} based on this month-day with the requested month, not null523* @throws DateTimeException if the month-of-year value is invalid524*/525public MonthDay withMonth(int month) {526return with(Month.of(month));527}528529/**530* Returns a copy of this {@code MonthDay} with the month-of-year altered.531* <p>532* This returns a month-day with the specified month.533* If the day-of-month is invalid for the specified month, the day will534* be adjusted to the last valid day-of-month.535* <p>536* This instance is immutable and unaffected by this method call.537*538* @param month the month-of-year to set in the returned month-day, not null539* @return a {@code MonthDay} based on this month-day with the requested month, not null540*/541public MonthDay with(Month month) {542Objects.requireNonNull(month, "month");543if (month.getValue() == this.month) {544return this;545}546int day = Math.min(this.day, month.maxLength());547return new MonthDay(month.getValue(), day);548}549550/**551* Returns a copy of this {@code MonthDay} with the day-of-month altered.552* <p>553* This returns a month-day with the specified day-of-month.554* If the day-of-month is invalid for the month, an exception is thrown.555* <p>556* This instance is immutable and unaffected by this method call.557*558* @param dayOfMonth the day-of-month to set in the return month-day, from 1 to 31559* @return a {@code MonthDay} based on this month-day with the requested day, not null560* @throws DateTimeException if the day-of-month value is invalid,561* or if the day-of-month is invalid for the month562*/563public MonthDay withDayOfMonth(int dayOfMonth) {564if (dayOfMonth == this.day) {565return this;566}567return of(month, dayOfMonth);568}569570//-----------------------------------------------------------------------571/**572* Queries this month-day using the specified query.573* <p>574* This queries this month-day using the specified query strategy object.575* The {@code TemporalQuery} object defines the logic to be used to576* obtain the result. Read the documentation of the query to understand577* what the result of this method will be.578* <p>579* The result of this method is obtained by invoking the580* {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the581* specified query passing {@code this} as the argument.582*583* @param <R> the type of the result584* @param query the query to invoke, not null585* @return the query result, null may be returned (defined by the query)586* @throws DateTimeException if unable to query (defined by the query)587* @throws ArithmeticException if numeric overflow occurs (defined by the query)588*/589@SuppressWarnings("unchecked")590@Override591public <R> R query(TemporalQuery<R> query) {592if (query == TemporalQueries.chronology()) {593return (R) IsoChronology.INSTANCE;594}595return TemporalAccessor.super.query(query);596}597598/**599* Adjusts the specified temporal object to have this month-day.600* <p>601* This returns a temporal object of the same observable type as the input602* with the month and day-of-month changed to be the same as this.603* <p>604* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}605* twice, passing {@link ChronoField#MONTH_OF_YEAR} and606* {@link ChronoField#DAY_OF_MONTH} as the fields.607* If the specified temporal object does not use the ISO calendar system then608* a {@code DateTimeException} is thrown.609* <p>610* In most cases, it is clearer to reverse the calling pattern by using611* {@link Temporal#with(TemporalAdjuster)}:612* <pre>613* // these two lines are equivalent, but the second approach is recommended614* temporal = thisMonthDay.adjustInto(temporal);615* temporal = temporal.with(thisMonthDay);616* </pre>617* <p>618* This instance is immutable and unaffected by this method call.619*620* @param temporal the target object to be adjusted, not null621* @return the adjusted object, not null622* @throws DateTimeException if unable to make the adjustment623* @throws ArithmeticException if numeric overflow occurs624*/625@Override626public Temporal adjustInto(Temporal temporal) {627if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) {628throw new DateTimeException("Adjustment only supported on ISO date-time");629}630temporal = temporal.with(MONTH_OF_YEAR, month);631return temporal.with(DAY_OF_MONTH, Math.min(temporal.range(DAY_OF_MONTH).getMaximum(), day));632}633634/**635* Formats this month-day using the specified formatter.636* <p>637* This month-day will be passed to the formatter to produce a string.638*639* @param formatter the formatter to use, not null640* @return the formatted month-day string, not null641* @throws DateTimeException if an error occurs during printing642*/643public String format(DateTimeFormatter formatter) {644Objects.requireNonNull(formatter, "formatter");645return formatter.format(this);646}647648//-----------------------------------------------------------------------649/**650* Combines this month-day with a year to create a {@code LocalDate}.651* <p>652* This returns a {@code LocalDate} formed from this month-day and the specified year.653* <p>654* A month-day of February 29th will be adjusted to February 28th in the resulting655* date if the year is not a leap year.656* <p>657* This instance is immutable and unaffected by this method call.658*659* @param year the year to use, from MIN_YEAR to MAX_YEAR660* @return the local date formed from this month-day and the specified year, not null661* @throws DateTimeException if the year is outside the valid range of years662*/663public LocalDate atYear(int year) {664return LocalDate.of(year, month, isValidYear(year) ? day : 28);665}666667//-----------------------------------------------------------------------668/**669* Compares this month-day to another month-day.670* <p>671* The comparison is based first on value of the month, then on the value of the day.672* It is "consistent with equals", as defined by {@link Comparable}.673*674* @param other the other month-day to compare to, not null675* @return the comparator value, negative if less, positive if greater676*/677@Override678public int compareTo(MonthDay other) {679int cmp = (month - other.month);680if (cmp == 0) {681cmp = (day - other.day);682}683return cmp;684}685686/**687* Checks if this month-day is after the specified month-day.688*689* @param other the other month-day to compare to, not null690* @return true if this is after the specified month-day691*/692public boolean isAfter(MonthDay other) {693return compareTo(other) > 0;694}695696/**697* Checks if this month-day is before the specified month-day.698*699* @param other the other month-day to compare to, not null700* @return true if this point is before the specified month-day701*/702public boolean isBefore(MonthDay other) {703return compareTo(other) < 0;704}705706//-----------------------------------------------------------------------707/**708* Checks if this month-day is equal to another month-day.709* <p>710* The comparison is based on the time-line position of the month-day within a year.711*712* @param obj the object to check, null returns false713* @return true if this is equal to the other month-day714*/715@Override716public boolean equals(Object obj) {717if (this == obj) {718return true;719}720if (obj instanceof MonthDay) {721MonthDay other = (MonthDay) obj;722return month == other.month && day == other.day;723}724return false;725}726727/**728* A hash code for this month-day.729*730* @return a suitable hash code731*/732@Override733public int hashCode() {734return (month << 6) + day;735}736737//-----------------------------------------------------------------------738/**739* Outputs this month-day as a {@code String}, such as {@code --12-03}.740* <p>741* The output will be in the format {@code --MM-dd}:742*743* @return a string representation of this month-day, not null744*/745@Override746public String toString() {747return new StringBuilder(10).append("--")748.append(month < 10 ? "0" : "").append(month)749.append(day < 10 ? "-0" : "-").append(day)750.toString();751}752753//-----------------------------------------------------------------------754/**755* Writes the object using a756* <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.757* @serialData758* <pre>759* out.writeByte(13); // identifies a MonthDay760* out.writeByte(month);761* out.writeByte(day);762* </pre>763*764* @return the instance of {@code Ser}, not null765*/766private Object writeReplace() {767return new Ser(Ser.MONTH_DAY_TYPE, this);768}769770/**771* Defend against malicious streams.772*773* @param s the stream to read774* @throws InvalidObjectException always775*/776private void readObject(ObjectInputStream s) throws InvalidObjectException {777throw new InvalidObjectException("Deserialization via serialization delegate");778}779780void writeExternal(DataOutput out) throws IOException {781out.writeByte(month);782out.writeByte(day);783}784785static MonthDay readExternal(DataInput in) throws IOException {786byte month = in.readByte();787byte day = in.readByte();788return MonthDay.of(month, day);789}790791}792793794