Home | History | Annotate | Download | only in chrono
      1 /*
      2  * Copyright (c) 2012, 2013, 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  * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
     28  *
     29  * All rights reserved.
     30  *
     31  * Redistribution and use in source and binary forms, with or without
     32  * modification, are permitted provided that the following conditions are met:
     33  *
     34  *  * Redistributions of source code must retain the above copyright notice,
     35  *    this list of conditions and the following disclaimer.
     36  *
     37  *  * Redistributions in binary form must reproduce the above copyright notice,
     38  *    this list of conditions and the following disclaimer in the documentation
     39  *    and/or other materials provided with the distribution.
     40  *
     41  *  * Neither the name of JSR-310 nor the names of its contributors
     42  *    may be used to endorse or promote products derived from this software
     43  *    without specific prior written permission.
     44  *
     45  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     46  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     47  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     48  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     49  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     50  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     51  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     52  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     53  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     54  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     55  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     56  */
     57 package java.time.chrono;
     58 
     59 import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
     60 import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
     61 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
     62 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
     63 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
     64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
     65 import static java.time.temporal.ChronoField.YEAR;
     66 
     67 import java.io.IOException;
     68 import java.io.InvalidObjectException;
     69 import java.io.ObjectInput;
     70 import java.io.ObjectInputStream;
     71 import java.io.ObjectOutput;
     72 import java.io.Serializable;
     73 import java.time.Clock;
     74 import java.time.DateTimeException;
     75 import java.time.LocalDate;
     76 import java.time.LocalTime;
     77 import java.time.ZoneId;
     78 import java.time.temporal.ChronoField;
     79 import java.time.temporal.TemporalAccessor;
     80 import java.time.temporal.TemporalAdjuster;
     81 import java.time.temporal.TemporalAmount;
     82 import java.time.temporal.TemporalField;
     83 import java.time.temporal.TemporalQuery;
     84 import java.time.temporal.TemporalUnit;
     85 import java.time.temporal.UnsupportedTemporalTypeException;
     86 import java.time.temporal.ValueRange;
     87 
     88 // Android-changed: removed ValueBased paragraph.
     89 /**
     90  * A date in the Hijrah calendar system.
     91  * <p>
     92  * This date operates using one of several variants of the
     93  * {@linkplain HijrahChronology Hijrah calendar}.
     94  * <p>
     95  * The Hijrah calendar has a different total of days in a year than
     96  * Gregorian calendar, and the length of each month is based on the period
     97  * of a complete revolution of the moon around the earth
     98  * (as between successive new moons).
     99  * Refer to the {@link HijrahChronology} for details of supported variants.
    100  * <p>
    101  * Each HijrahDate is created bound to a particular HijrahChronology,
    102  * The same chronology is propagated to each HijrahDate computed from the date.
    103  * To use a different Hijrah variant, its HijrahChronology can be used
    104  * to create new HijrahDate instances.
    105  * Alternatively, the {@link #withVariant} method can be used to convert
    106  * to a new HijrahChronology.
    107  *
    108  * @implSpec
    109  * This class is immutable and thread-safe.
    110  *
    111  * @since 1.8
    112  */
    113 public final class HijrahDate
    114         extends ChronoLocalDateImpl<HijrahDate>
    115         implements ChronoLocalDate, Serializable {
    116 
    117     /**
    118      * Serialization version.
    119      */
    120     private static final long serialVersionUID = -5207853542612002020L;
    121     /**
    122      * The Chronology of this HijrahDate.
    123      */
    124     private final transient HijrahChronology chrono;
    125     /**
    126      * The proleptic year.
    127      */
    128     private final transient int prolepticYear;
    129     /**
    130      * The month-of-year.
    131      */
    132     private final transient int monthOfYear;
    133     /**
    134      * The day-of-month.
    135      */
    136     private final transient int dayOfMonth;
    137 
    138     //-------------------------------------------------------------------------
    139     /**
    140      * Obtains an instance of {@code HijrahDate} from the Hijrah proleptic year,
    141      * month-of-year and day-of-month.
    142      *
    143      * @param prolepticYear  the proleptic year to represent in the Hijrah calendar
    144      * @param monthOfYear  the month-of-year to represent, from 1 to 12
    145      * @param dayOfMonth  the day-of-month to represent, from 1 to 30
    146      * @return the Hijrah date, never null
    147      * @throws DateTimeException if the value of any field is out of range
    148      */
    149     static HijrahDate of(HijrahChronology chrono, int prolepticYear, int monthOfYear, int dayOfMonth) {
    150         return new HijrahDate(chrono, prolepticYear, monthOfYear, dayOfMonth);
    151     }
    152 
    153     /**
    154      * Returns a HijrahDate for the chronology and epochDay.
    155      * @param chrono The Hijrah chronology
    156      * @param epochDay the epoch day
    157      * @return a HijrahDate for the epoch day; non-null
    158      */
    159     static HijrahDate ofEpochDay(HijrahChronology chrono, long epochDay) {
    160         return new HijrahDate(chrono, epochDay);
    161     }
    162 
    163     //-----------------------------------------------------------------------
    164     /**
    165      * Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
    166      * in the default time-zone.
    167      * <p>
    168      * This will query the {@link Clock#systemDefaultZone() system clock} in the default
    169      * time-zone to obtain the current date.
    170      * <p>
    171      * Using this method will prevent the ability to use an alternate clock for testing
    172      * because the clock is hard-coded.
    173      *
    174      * @return the current date using the system clock and default time-zone, not null
    175      */
    176     public static HijrahDate now() {
    177         return now(Clock.systemDefaultZone());
    178     }
    179 
    180     /**
    181      * Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
    182      * in the specified time-zone.
    183      * <p>
    184      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
    185      * Specifying the time-zone avoids dependence on the default time-zone.
    186      * <p>
    187      * Using this method will prevent the ability to use an alternate clock for testing
    188      * because the clock is hard-coded.
    189      *
    190      * @param zone  the zone ID to use, not null
    191      * @return the current date using the system clock, not null
    192      */
    193     public static HijrahDate now(ZoneId zone) {
    194         return now(Clock.system(zone));
    195     }
    196 
    197     /**
    198      * Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
    199      * from the specified clock.
    200      * <p>
    201      * This will query the specified clock to obtain the current date - today.
    202      * Using this method allows the use of an alternate clock for testing.
    203      * The alternate clock may be introduced using {@linkplain Clock dependency injection}.
    204      *
    205      * @param clock  the clock to use, not null
    206      * @return the current date, not null
    207      * @throws DateTimeException if the current date cannot be obtained
    208      */
    209     public static HijrahDate now(Clock clock) {
    210         return HijrahDate.ofEpochDay(HijrahChronology.INSTANCE, LocalDate.now(clock).toEpochDay());
    211     }
    212 
    213     /**
    214      * Obtains a {@code HijrahDate} of the Islamic Umm Al-Qura calendar
    215      * from the proleptic-year, month-of-year and day-of-month fields.
    216      * <p>
    217      * This returns a {@code HijrahDate} with the specified fields.
    218      * The day must be valid for the year and month, otherwise an exception will be thrown.
    219      *
    220      * @param prolepticYear  the Hijrah proleptic-year
    221      * @param month  the Hijrah month-of-year, from 1 to 12
    222      * @param dayOfMonth  the Hijrah day-of-month, from 1 to 30
    223      * @return the date in Hijrah calendar system, not null
    224      * @throws DateTimeException if the value of any field is out of range,
    225      *  or if the day-of-month is invalid for the month-year
    226      */
    227     public static HijrahDate of(int prolepticYear, int month, int dayOfMonth) {
    228         return HijrahChronology.INSTANCE.date(prolepticYear, month, dayOfMonth);
    229     }
    230 
    231     /**
    232      * Obtains a {@code HijrahDate} of the Islamic Umm Al-Qura calendar from a temporal object.
    233      * <p>
    234      * This obtains a date in the Hijrah calendar system based on the specified temporal.
    235      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
    236      * which this factory converts to an instance of {@code HijrahDate}.
    237      * <p>
    238      * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
    239      * field, which is standardized across calendar systems.
    240      * <p>
    241      * This method matches the signature of the functional interface {@link TemporalQuery}
    242      * allowing it to be used as a query via method reference, {@code HijrahDate::from}.
    243      *
    244      * @param temporal  the temporal object to convert, not null
    245      * @return the date in Hijrah calendar system, not null
    246      * @throws DateTimeException if unable to convert to a {@code HijrahDate}
    247      */
    248     public static HijrahDate from(TemporalAccessor temporal) {
    249         return HijrahChronology.INSTANCE.date(temporal);
    250     }
    251 
    252     //-----------------------------------------------------------------------
    253     /**
    254      * Constructs an {@code HijrahDate} with the proleptic-year, month-of-year and
    255      * day-of-month fields.
    256      *
    257      * @param chrono The chronology to create the date with
    258      * @param prolepticYear the proleptic year
    259      * @param monthOfYear the month of year
    260      * @param dayOfMonth the day of month
    261      */
    262     private HijrahDate(HijrahChronology chrono, int prolepticYear, int monthOfYear, int dayOfMonth) {
    263         // Computing the Gregorian day checks the valid ranges
    264         chrono.getEpochDay(prolepticYear, monthOfYear, dayOfMonth);
    265 
    266         this.chrono = chrono;
    267         this.prolepticYear = prolepticYear;
    268         this.monthOfYear = monthOfYear;
    269         this.dayOfMonth = dayOfMonth;
    270     }
    271 
    272     /**
    273      * Constructs an instance with the Epoch Day.
    274      *
    275      * @param epochDay  the epochDay
    276      */
    277     private HijrahDate(HijrahChronology chrono, long epochDay) {
    278         int[] dateInfo = chrono.getHijrahDateInfo((int)epochDay);
    279 
    280         this.chrono = chrono;
    281         this.prolepticYear = dateInfo[0];
    282         this.monthOfYear = dateInfo[1];
    283         this.dayOfMonth = dateInfo[2];
    284     }
    285 
    286     //-----------------------------------------------------------------------
    287     /**
    288      * Gets the chronology of this date, which is the Hijrah calendar system.
    289      * <p>
    290      * The {@code Chronology} represents the calendar system in use.
    291      * The era and other fields in {@link ChronoField} are defined by the chronology.
    292      *
    293      * @return the Hijrah chronology, not null
    294      */
    295     @Override
    296     public HijrahChronology getChronology() {
    297         return chrono;
    298     }
    299 
    300     /**
    301      * Gets the era applicable at this date.
    302      * <p>
    303      * The Hijrah calendar system has one era, 'AH',
    304      * defined by {@link HijrahEra}.
    305      *
    306      * @return the era applicable at this date, not null
    307      */
    308     @Override
    309     public HijrahEra getEra() {
    310         return HijrahEra.AH;
    311     }
    312 
    313     /**
    314      * Returns the length of the month represented by this date.
    315      * <p>
    316      * This returns the length of the month in days.
    317      * Month lengths in the Hijrah calendar system vary between 29 and 30 days.
    318      *
    319      * @return the length of the month in days
    320      */
    321     @Override
    322     public int lengthOfMonth() {
    323         return chrono.getMonthLength(prolepticYear, monthOfYear);
    324     }
    325 
    326     /**
    327      * Returns the length of the year represented by this date.
    328      * <p>
    329      * This returns the length of the year in days.
    330      * A Hijrah calendar system year is typically shorter than
    331      * that of the ISO calendar system.
    332      *
    333      * @return the length of the year in days
    334      */
    335     @Override
    336     public int lengthOfYear() {
    337         return chrono.getYearLength(prolepticYear);
    338     }
    339 
    340     //-----------------------------------------------------------------------
    341     @Override
    342     public ValueRange range(TemporalField field) {
    343         if (field instanceof ChronoField) {
    344             if (isSupported(field)) {
    345                 ChronoField f = (ChronoField) field;
    346                 switch (f) {
    347                     case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth());
    348                     case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear());
    349                     case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, 5);  // TODO
    350                     // TODO does the limited range of valid years cause years to
    351                     // start/end part way through? that would affect range
    352                 }
    353                 return getChronology().range(f);
    354             }
    355             throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    356         }
    357         return field.rangeRefinedBy(this);
    358     }
    359 
    360     @Override
    361     public long getLong(TemporalField field) {
    362         if (field instanceof ChronoField) {
    363             switch ((ChronoField) field) {
    364                 case DAY_OF_WEEK: return getDayOfWeek();
    365                 case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((getDayOfWeek() - 1) % 7) + 1;
    366                 case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
    367                 case DAY_OF_MONTH: return this.dayOfMonth;
    368                 case DAY_OF_YEAR: return this.getDayOfYear();
    369                 case EPOCH_DAY: return toEpochDay();
    370                 case ALIGNED_WEEK_OF_MONTH: return ((dayOfMonth - 1) / 7) + 1;
    371                 case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
    372                 case MONTH_OF_YEAR: return monthOfYear;
    373                 case PROLEPTIC_MONTH: return getProlepticMonth();
    374                 case YEAR_OF_ERA: return prolepticYear;
    375                 case YEAR: return prolepticYear;
    376                 case ERA: return getEraValue();
    377             }
    378             throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    379         }
    380         return field.getFrom(this);
    381     }
    382 
    383     private long getProlepticMonth() {
    384         return prolepticYear * 12L + monthOfYear - 1;
    385     }
    386 
    387     @Override
    388     public HijrahDate with(TemporalField field, long newValue) {
    389         if (field instanceof ChronoField) {
    390             ChronoField f = (ChronoField) field;
    391             // not using checkValidIntValue so EPOCH_DAY and PROLEPTIC_MONTH work
    392             chrono.range(f).checkValidValue(newValue, f);    // TODO: validate value
    393             int nvalue = (int) newValue;
    394             switch (f) {
    395                 case DAY_OF_WEEK: return plusDays(newValue - getDayOfWeek());
    396                 case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH));
    397                 case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR));
    398                 case DAY_OF_MONTH: return resolvePreviousValid(prolepticYear, monthOfYear, nvalue);
    399                 case DAY_OF_YEAR: return plusDays(Math.min(nvalue, lengthOfYear()) - getDayOfYear());
    400                 case EPOCH_DAY: return new HijrahDate(chrono, newValue);
    401                 case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * 7);
    402                 case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * 7);
    403                 case MONTH_OF_YEAR: return resolvePreviousValid(prolepticYear, nvalue, dayOfMonth);
    404                 case PROLEPTIC_MONTH: return plusMonths(newValue - getProlepticMonth());
    405                 case YEAR_OF_ERA: return resolvePreviousValid(prolepticYear >= 1 ? nvalue : 1 - nvalue, monthOfYear, dayOfMonth);
    406                 case YEAR: return resolvePreviousValid(nvalue, monthOfYear, dayOfMonth);
    407                 case ERA: return resolvePreviousValid(1 - prolepticYear, monthOfYear, dayOfMonth);
    408             }
    409             throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    410         }
    411         return super.with(field, newValue);
    412     }
    413 
    414     private HijrahDate resolvePreviousValid(int prolepticYear, int month, int day) {
    415         int monthDays = chrono.getMonthLength(prolepticYear, month);
    416         if (day > monthDays) {
    417             day = monthDays;
    418         }
    419         return HijrahDate.of(chrono, prolepticYear, month, day);
    420     }
    421 
    422     /**
    423      * {@inheritDoc}
    424      * @throws DateTimeException if unable to make the adjustment.
    425      *     For example, if the adjuster requires an ISO chronology
    426      * @throws ArithmeticException {@inheritDoc}
    427      */
    428     @Override
    429     public  HijrahDate with(TemporalAdjuster adjuster) {
    430         return super.with(adjuster);
    431     }
    432 
    433     /**
    434      * Returns a {@code HijrahDate} with the Chronology requested.
    435      * <p>
    436      * The year, month, and day are checked against the new requested
    437      * HijrahChronology.  If the chronology has a shorter month length
    438      * for the month, the day is reduced to be the last day of the month.
    439      *
    440      * @param chronology the new HijrahChonology, non-null
    441      * @return a HijrahDate with the requested HijrahChronology, non-null
    442      */
    443     public HijrahDate withVariant(HijrahChronology chronology) {
    444         if (chrono == chronology) {
    445             return this;
    446         }
    447         // Like resolvePreviousValid the day is constrained to stay in the same month
    448         int monthDays = chronology.getDayOfYear(prolepticYear, monthOfYear);
    449         return HijrahDate.of(chronology, prolepticYear, monthOfYear,(dayOfMonth > monthDays) ? monthDays : dayOfMonth );
    450     }
    451 
    452     /**
    453      * {@inheritDoc}
    454      * @throws DateTimeException {@inheritDoc}
    455      * @throws ArithmeticException {@inheritDoc}
    456      */
    457     @Override
    458     public HijrahDate plus(TemporalAmount amount) {
    459         return super.plus(amount);
    460     }
    461 
    462     /**
    463      * {@inheritDoc}
    464      * @throws DateTimeException {@inheritDoc}
    465      * @throws ArithmeticException {@inheritDoc}
    466      */
    467     @Override
    468     public HijrahDate minus(TemporalAmount amount) {
    469         return super.minus(amount);
    470     }
    471 
    472     @Override
    473     public long toEpochDay() {
    474         return chrono.getEpochDay(prolepticYear, monthOfYear, dayOfMonth);
    475     }
    476 
    477     /**
    478      * Gets the day-of-year field.
    479      * <p>
    480      * This method returns the primitive {@code int} value for the day-of-year.
    481      *
    482      * @return the day-of-year
    483      */
    484     private int getDayOfYear() {
    485         return chrono.getDayOfYear(prolepticYear, monthOfYear) + dayOfMonth;
    486     }
    487 
    488     /**
    489      * Gets the day-of-week value.
    490      *
    491      * @return the day-of-week; computed from the epochday
    492      */
    493     private int getDayOfWeek() {
    494         int dow0 = (int)Math.floorMod(toEpochDay() + 3, 7);
    495         return dow0 + 1;
    496     }
    497 
    498     /**
    499      * Gets the Era of this date.
    500      *
    501      * @return the Era of this date; computed from epochDay
    502      */
    503     private int getEraValue() {
    504         return (prolepticYear > 1 ? 1 : 0);
    505     }
    506 
    507     //-----------------------------------------------------------------------
    508     /**
    509      * Checks if the year is a leap year, according to the Hijrah calendar system rules.
    510      *
    511      * @return true if this date is in a leap year
    512      */
    513     @Override
    514     public boolean isLeapYear() {
    515         return chrono.isLeapYear(prolepticYear);
    516     }
    517 
    518     //-----------------------------------------------------------------------
    519     @Override
    520     HijrahDate plusYears(long years) {
    521         if (years == 0) {
    522             return this;
    523         }
    524         int newYear = Math.addExact(this.prolepticYear, (int)years);
    525         return resolvePreviousValid(newYear, monthOfYear, dayOfMonth);
    526     }
    527 
    528     @Override
    529     HijrahDate plusMonths(long monthsToAdd) {
    530         if (monthsToAdd == 0) {
    531             return this;
    532         }
    533         long monthCount = prolepticYear * 12L + (monthOfYear - 1);
    534         long calcMonths = monthCount + monthsToAdd;  // safe overflow
    535         int newYear = chrono.checkValidYear(Math.floorDiv(calcMonths, 12L));
    536         int newMonth = (int)Math.floorMod(calcMonths, 12L) + 1;
    537         return resolvePreviousValid(newYear, newMonth, dayOfMonth);
    538     }
    539 
    540     @Override
    541     HijrahDate plusWeeks(long weeksToAdd) {
    542         return super.plusWeeks(weeksToAdd);
    543     }
    544 
    545     @Override
    546     HijrahDate plusDays(long days) {
    547         return new HijrahDate(chrono, toEpochDay() + days);
    548     }
    549 
    550     @Override
    551     public HijrahDate plus(long amountToAdd, TemporalUnit unit) {
    552         return super.plus(amountToAdd, unit);
    553     }
    554 
    555     @Override
    556     public HijrahDate minus(long amountToSubtract, TemporalUnit unit) {
    557         return super.minus(amountToSubtract, unit);
    558     }
    559 
    560     @Override
    561     HijrahDate minusYears(long yearsToSubtract) {
    562         return super.minusYears(yearsToSubtract);
    563     }
    564 
    565     @Override
    566     HijrahDate minusMonths(long monthsToSubtract) {
    567         return super.minusMonths(monthsToSubtract);
    568     }
    569 
    570     @Override
    571     HijrahDate minusWeeks(long weeksToSubtract) {
    572         return super.minusWeeks(weeksToSubtract);
    573     }
    574 
    575     @Override
    576     HijrahDate minusDays(long daysToSubtract) {
    577         return super.minusDays(daysToSubtract);
    578     }
    579 
    580     @Override        // for javadoc and covariant return type
    581     @SuppressWarnings("unchecked")
    582     public final ChronoLocalDateTime<HijrahDate> atTime(LocalTime localTime) {
    583         return (ChronoLocalDateTime<HijrahDate>)super.atTime(localTime);
    584     }
    585 
    586     @Override
    587     public ChronoPeriod until(ChronoLocalDate endDate) {
    588         // TODO: untested
    589         HijrahDate end = getChronology().date(endDate);
    590         long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear);  // safe
    591         int days = end.dayOfMonth - this.dayOfMonth;
    592         if (totalMonths > 0 && days < 0) {
    593             totalMonths--;
    594             HijrahDate calcDate = this.plusMonths(totalMonths);
    595             days = (int) (end.toEpochDay() - calcDate.toEpochDay());  // safe
    596         } else if (totalMonths < 0 && days > 0) {
    597             totalMonths++;
    598             days -= end.lengthOfMonth();
    599         }
    600         long years = totalMonths / 12;  // safe
    601         int months = (int) (totalMonths % 12);  // safe
    602         return getChronology().period(Math.toIntExact(years), months, days);
    603     }
    604 
    605     //-------------------------------------------------------------------------
    606     /**
    607      * Compares this date to another date, including the chronology.
    608      * <p>
    609      * Compares this {@code HijrahDate} with another ensuring that the date is the same.
    610      * <p>
    611      * Only objects of type {@code HijrahDate} are compared, other types return false.
    612      * To compare the dates of two {@code TemporalAccessor} instances, including dates
    613      * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
    614      *
    615      * @param obj  the object to check, null returns false
    616      * @return true if this is equal to the other date and the Chronologies are equal
    617      */
    618     @Override  // override for performance
    619     public boolean equals(Object obj) {
    620         if (this == obj) {
    621             return true;
    622         }
    623         if (obj instanceof HijrahDate) {
    624             HijrahDate otherDate = (HijrahDate) obj;
    625             return prolepticYear == otherDate.prolepticYear
    626                 && this.monthOfYear == otherDate.monthOfYear
    627                 && this.dayOfMonth == otherDate.dayOfMonth
    628                 && getChronology().equals(otherDate.getChronology());
    629         }
    630         return false;
    631     }
    632 
    633     /**
    634      * A hash code for this date.
    635      *
    636      * @return a suitable hash code based only on the Chronology and the date
    637      */
    638     @Override  // override for performance
    639     public int hashCode() {
    640         int yearValue = prolepticYear;
    641         int monthValue = monthOfYear;
    642         int dayValue = dayOfMonth;
    643         return getChronology().getId().hashCode() ^ (yearValue & 0xFFFFF800)
    644                 ^ ((yearValue << 11) + (monthValue << 6) + (dayValue));
    645     }
    646 
    647     //-----------------------------------------------------------------------
    648     /**
    649      * Defend against malicious streams.
    650      *
    651      * @param s the stream to read
    652      * @throws InvalidObjectException always
    653      */
    654     private void readObject(ObjectInputStream s) throws InvalidObjectException {
    655         throw new InvalidObjectException("Deserialization via serialization delegate");
    656     }
    657 
    658     /**
    659      * Writes the object using a
    660      * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
    661      * @serialData
    662      * <pre>
    663      *  out.writeByte(6);                 // identifies a HijrahDate
    664      *  out.writeObject(chrono);          // the HijrahChronology variant
    665      *  out.writeInt(get(YEAR));
    666      *  out.writeByte(get(MONTH_OF_YEAR));
    667      *  out.writeByte(get(DAY_OF_MONTH));
    668      * </pre>
    669      *
    670      * @return the instance of {@code Ser}, not null
    671      */
    672     private Object writeReplace() {
    673         return new Ser(Ser.HIJRAH_DATE_TYPE, this);
    674     }
    675 
    676     void writeExternal(ObjectOutput out) throws IOException {
    677         // HijrahChronology is implicit in the Hijrah_DATE_TYPE
    678         out.writeObject(getChronology());
    679         out.writeInt(get(YEAR));
    680         out.writeByte(get(MONTH_OF_YEAR));
    681         out.writeByte(get(DAY_OF_MONTH));
    682     }
    683 
    684     static HijrahDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    685         HijrahChronology chrono = (HijrahChronology) in.readObject();
    686         int year = in.readInt();
    687         int month = in.readByte();
    688         int dayOfMonth = in.readByte();
    689         return chrono.date(year, month, dayOfMonth);
    690     }
    691 
    692 }
    693