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.chrono.ThaiBuddhistChronology.YEARS_DIFFERENCE;
     60 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
     61 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
     62 import static java.time.temporal.ChronoField.YEAR;
     63 
     64 import java.io.DataInput;
     65 import java.io.DataOutput;
     66 import java.io.IOException;
     67 import java.io.InvalidObjectException;
     68 import java.io.ObjectInputStream;
     69 import java.io.Serializable;
     70 import java.time.Clock;
     71 import java.time.DateTimeException;
     72 import java.time.LocalDate;
     73 import java.time.LocalTime;
     74 import java.time.Period;
     75 import java.time.ZoneId;
     76 import java.time.temporal.ChronoField;
     77 import java.time.temporal.TemporalAccessor;
     78 import java.time.temporal.TemporalAdjuster;
     79 import java.time.temporal.TemporalAmount;
     80 import java.time.temporal.TemporalField;
     81 import java.time.temporal.TemporalQuery;
     82 import java.time.temporal.TemporalUnit;
     83 import java.time.temporal.UnsupportedTemporalTypeException;
     84 import java.time.temporal.ValueRange;
     85 import java.util.Objects;
     86 
     87 // Android-changed: removed ValueBased paragraph.
     88 /**
     89  * A date in the Thai Buddhist calendar system.
     90  * <p>
     91  * This date operates using the {@linkplain ThaiBuddhistChronology Thai Buddhist calendar}.
     92  * This calendar system is primarily used in Thailand.
     93  * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}.
     94  *
     95  * @implSpec
     96  * This class is immutable and thread-safe.
     97  *
     98  * @since 1.8
     99  */
    100 public final class ThaiBuddhistDate
    101         extends ChronoLocalDateImpl<ThaiBuddhistDate>
    102         implements ChronoLocalDate, Serializable {
    103 
    104     /**
    105      * Serialization version.
    106      */
    107     private static final long serialVersionUID = -8722293800195731463L;
    108 
    109     /**
    110      * The underlying date.
    111      */
    112     private final transient LocalDate isoDate;
    113 
    114     //-----------------------------------------------------------------------
    115     /**
    116      * Obtains the current {@code ThaiBuddhistDate} from the system clock in the default time-zone.
    117      * <p>
    118      * This will query the {@link Clock#systemDefaultZone() system clock} in the default
    119      * time-zone to obtain the current date.
    120      * <p>
    121      * Using this method will prevent the ability to use an alternate clock for testing
    122      * because the clock is hard-coded.
    123      *
    124      * @return the current date using the system clock and default time-zone, not null
    125      */
    126     public static ThaiBuddhistDate now() {
    127         return now(Clock.systemDefaultZone());
    128     }
    129 
    130     /**
    131      * Obtains the current {@code ThaiBuddhistDate} from the system clock in the specified time-zone.
    132      * <p>
    133      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
    134      * Specifying the time-zone avoids dependence on the default time-zone.
    135      * <p>
    136      * Using this method will prevent the ability to use an alternate clock for testing
    137      * because the clock is hard-coded.
    138      *
    139      * @param zone  the zone ID to use, not null
    140      * @return the current date using the system clock, not null
    141      */
    142     public static ThaiBuddhistDate now(ZoneId zone) {
    143         return now(Clock.system(zone));
    144     }
    145 
    146     /**
    147      * Obtains the current {@code ThaiBuddhistDate} from the specified clock.
    148      * <p>
    149      * This will query the specified clock to obtain the current date - today.
    150      * Using this method allows the use of an alternate clock for testing.
    151      * The alternate clock may be introduced using {@linkplain Clock dependency injection}.
    152      *
    153      * @param clock  the clock to use, not null
    154      * @return the current date, not null
    155      * @throws DateTimeException if the current date cannot be obtained
    156      */
    157     public static ThaiBuddhistDate now(Clock clock) {
    158         return new ThaiBuddhistDate(LocalDate.now(clock));
    159     }
    160 
    161     /**
    162      * Obtains a {@code ThaiBuddhistDate} representing a date in the Thai Buddhist calendar
    163      * system from the proleptic-year, month-of-year and day-of-month fields.
    164      * <p>
    165      * This returns a {@code ThaiBuddhistDate} with the specified fields.
    166      * The day must be valid for the year and month, otherwise an exception will be thrown.
    167      *
    168      * @param prolepticYear  the Thai Buddhist proleptic-year
    169      * @param month  the Thai Buddhist month-of-year, from 1 to 12
    170      * @param dayOfMonth  the Thai Buddhist day-of-month, from 1 to 31
    171      * @return the date in Thai Buddhist calendar system, not null
    172      * @throws DateTimeException if the value of any field is out of range,
    173      *  or if the day-of-month is invalid for the month-year
    174      */
    175     public static ThaiBuddhistDate of(int prolepticYear, int month, int dayOfMonth) {
    176         return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth));
    177     }
    178 
    179     /**
    180      * Obtains a {@code ThaiBuddhistDate} from a temporal object.
    181      * <p>
    182      * This obtains a date in the Thai Buddhist calendar system based on the specified temporal.
    183      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
    184      * which this factory converts to an instance of {@code ThaiBuddhistDate}.
    185      * <p>
    186      * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
    187      * field, which is standardized across calendar systems.
    188      * <p>
    189      * This method matches the signature of the functional interface {@link TemporalQuery}
    190      * allowing it to be used as a query via method reference, {@code ThaiBuddhistDate::from}.
    191      *
    192      * @param temporal  the temporal object to convert, not null
    193      * @return the date in Thai Buddhist calendar system, not null
    194      * @throws DateTimeException if unable to convert to a {@code ThaiBuddhistDate}
    195      */
    196     public static ThaiBuddhistDate from(TemporalAccessor temporal) {
    197         return ThaiBuddhistChronology.INSTANCE.date(temporal);
    198     }
    199 
    200     //-----------------------------------------------------------------------
    201     /**
    202      * Creates an instance from an ISO date.
    203      *
    204      * @param isoDate  the standard local date, validated not null
    205      */
    206     ThaiBuddhistDate(LocalDate isoDate) {
    207         Objects.requireNonNull(isoDate, "isoDate");
    208         this.isoDate = isoDate;
    209     }
    210 
    211     //-----------------------------------------------------------------------
    212     /**
    213      * Gets the chronology of this date, which is the Thai Buddhist calendar system.
    214      * <p>
    215      * The {@code Chronology} represents the calendar system in use.
    216      * The era and other fields in {@link ChronoField} are defined by the chronology.
    217      *
    218      * @return the Thai Buddhist chronology, not null
    219      */
    220     @Override
    221     public ThaiBuddhistChronology getChronology() {
    222         return ThaiBuddhistChronology.INSTANCE;
    223     }
    224 
    225     /**
    226      * Gets the era applicable at this date.
    227      * <p>
    228      * The Thai Buddhist calendar system has two eras, 'BE' and 'BEFORE_BE',
    229      * defined by {@link ThaiBuddhistEra}.
    230      *
    231      * @return the era applicable at this date, not null
    232      */
    233     @Override
    234     public ThaiBuddhistEra getEra() {
    235         return (getProlepticYear() >= 1 ? ThaiBuddhistEra.BE : ThaiBuddhistEra.BEFORE_BE);
    236     }
    237 
    238     /**
    239      * Returns the length of the month represented by this date.
    240      * <p>
    241      * This returns the length of the month in days.
    242      * Month lengths match those of the ISO calendar system.
    243      *
    244      * @return the length of the month in days
    245      */
    246     @Override
    247     public int lengthOfMonth() {
    248         return isoDate.lengthOfMonth();
    249     }
    250 
    251     //-----------------------------------------------------------------------
    252     @Override
    253     public ValueRange range(TemporalField field) {
    254         if (field instanceof ChronoField) {
    255             if (isSupported(field)) {
    256                 ChronoField f = (ChronoField) field;
    257                 switch (f) {
    258                     case DAY_OF_MONTH:
    259                     case DAY_OF_YEAR:
    260                     case ALIGNED_WEEK_OF_MONTH:
    261                         return isoDate.range(field);
    262                     case YEAR_OF_ERA: {
    263                         ValueRange range = YEAR.range();
    264                         long max = (getProlepticYear() <= 0 ? -(range.getMinimum() + YEARS_DIFFERENCE) + 1 : range.getMaximum() + YEARS_DIFFERENCE);
    265                         return ValueRange.of(1, max);
    266                     }
    267                 }
    268                 return getChronology().range(f);
    269             }
    270             throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    271         }
    272         return field.rangeRefinedBy(this);
    273     }
    274 
    275     @Override
    276     public long getLong(TemporalField field) {
    277         if (field instanceof ChronoField) {
    278             switch ((ChronoField) field) {
    279                 case PROLEPTIC_MONTH:
    280                     return getProlepticMonth();
    281                 case YEAR_OF_ERA: {
    282                     int prolepticYear = getProlepticYear();
    283                     return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear);
    284                 }
    285                 case YEAR:
    286                     return getProlepticYear();
    287                 case ERA:
    288                     return (getProlepticYear() >= 1 ? 1 : 0);
    289             }
    290             return isoDate.getLong(field);
    291         }
    292         return field.getFrom(this);
    293     }
    294 
    295     private long getProlepticMonth() {
    296         return getProlepticYear() * 12L + isoDate.getMonthValue() - 1;
    297     }
    298 
    299     private int getProlepticYear() {
    300         return isoDate.getYear() + YEARS_DIFFERENCE;
    301     }
    302 
    303     //-----------------------------------------------------------------------
    304     @Override
    305     public ThaiBuddhistDate with(TemporalField field, long newValue) {
    306         if (field instanceof ChronoField) {
    307             ChronoField f = (ChronoField) field;
    308             if (getLong(f) == newValue) {
    309                 return this;
    310             }
    311             switch (f) {
    312                 case PROLEPTIC_MONTH:
    313                     getChronology().range(f).checkValidValue(newValue, f);
    314                     return plusMonths(newValue - getProlepticMonth());
    315                 case YEAR_OF_ERA:
    316                 case YEAR:
    317                 case ERA: {
    318                     int nvalue = getChronology().range(f).checkValidIntValue(newValue, f);
    319                     switch (f) {
    320                         case YEAR_OF_ERA:
    321                             return with(isoDate.withYear((getProlepticYear() >= 1 ? nvalue : 1 - nvalue)  - YEARS_DIFFERENCE));
    322                         case YEAR:
    323                             return with(isoDate.withYear(nvalue - YEARS_DIFFERENCE));
    324                         case ERA:
    325                             return with(isoDate.withYear((1 - getProlepticYear()) - YEARS_DIFFERENCE));
    326                     }
    327                 }
    328             }
    329             return with(isoDate.with(field, newValue));
    330         }
    331         return super.with(field, newValue);
    332     }
    333 
    334     /**
    335      * {@inheritDoc}
    336      * @throws DateTimeException {@inheritDoc}
    337      * @throws ArithmeticException {@inheritDoc}
    338      */
    339     @Override
    340     public  ThaiBuddhistDate with(TemporalAdjuster adjuster) {
    341         return super.with(adjuster);
    342     }
    343 
    344     /**
    345      * {@inheritDoc}
    346      * @throws DateTimeException {@inheritDoc}
    347      * @throws ArithmeticException {@inheritDoc}
    348      */
    349     @Override
    350     public ThaiBuddhistDate plus(TemporalAmount amount) {
    351         return super.plus(amount);
    352     }
    353 
    354     /**
    355      * {@inheritDoc}
    356      * @throws DateTimeException {@inheritDoc}
    357      * @throws ArithmeticException {@inheritDoc}
    358      */
    359     @Override
    360     public ThaiBuddhistDate minus(TemporalAmount amount) {
    361         return super.minus(amount);
    362     }
    363 
    364     //-----------------------------------------------------------------------
    365     @Override
    366     ThaiBuddhistDate plusYears(long years) {
    367         return with(isoDate.plusYears(years));
    368     }
    369 
    370     @Override
    371     ThaiBuddhistDate plusMonths(long months) {
    372         return with(isoDate.plusMonths(months));
    373     }
    374 
    375     @Override
    376     ThaiBuddhistDate plusWeeks(long weeksToAdd) {
    377         return super.plusWeeks(weeksToAdd);
    378     }
    379 
    380     @Override
    381     ThaiBuddhistDate plusDays(long days) {
    382         return with(isoDate.plusDays(days));
    383     }
    384 
    385     @Override
    386     public ThaiBuddhistDate plus(long amountToAdd, TemporalUnit unit) {
    387         return super.plus(amountToAdd, unit);
    388     }
    389 
    390     @Override
    391     public ThaiBuddhistDate minus(long amountToAdd, TemporalUnit unit) {
    392         return super.minus(amountToAdd, unit);
    393     }
    394 
    395     @Override
    396     ThaiBuddhistDate minusYears(long yearsToSubtract) {
    397         return super.minusYears(yearsToSubtract);
    398     }
    399 
    400     @Override
    401     ThaiBuddhistDate minusMonths(long monthsToSubtract) {
    402         return super.minusMonths(monthsToSubtract);
    403     }
    404 
    405     @Override
    406     ThaiBuddhistDate minusWeeks(long weeksToSubtract) {
    407         return super.minusWeeks(weeksToSubtract);
    408     }
    409 
    410     @Override
    411     ThaiBuddhistDate minusDays(long daysToSubtract) {
    412         return super.minusDays(daysToSubtract);
    413     }
    414 
    415     private ThaiBuddhistDate with(LocalDate newDate) {
    416         return (newDate.equals(isoDate) ? this : new ThaiBuddhistDate(newDate));
    417     }
    418 
    419     @Override        // for javadoc and covariant return type
    420     @SuppressWarnings("unchecked")
    421     public final ChronoLocalDateTime<ThaiBuddhistDate> atTime(LocalTime localTime) {
    422         return (ChronoLocalDateTime<ThaiBuddhistDate>) super.atTime(localTime);
    423     }
    424 
    425     @Override
    426     public ChronoPeriod until(ChronoLocalDate endDate) {
    427         Period period = isoDate.until(endDate);
    428         return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
    429     }
    430 
    431     @Override  // override for performance
    432     public long toEpochDay() {
    433         return isoDate.toEpochDay();
    434     }
    435 
    436     //-------------------------------------------------------------------------
    437     /**
    438      * Compares this date to another date, including the chronology.
    439      * <p>
    440      * Compares this {@code ThaiBuddhistDate} with another ensuring that the date is the same.
    441      * <p>
    442      * Only objects of type {@code ThaiBuddhistDate} are compared, other types return false.
    443      * To compare the dates of two {@code TemporalAccessor} instances, including dates
    444      * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
    445      *
    446      * @param obj  the object to check, null returns false
    447      * @return true if this is equal to the other date
    448      */
    449     @Override  // override for performance
    450     public boolean equals(Object obj) {
    451         if (this == obj) {
    452             return true;
    453         }
    454         if (obj instanceof ThaiBuddhistDate) {
    455             ThaiBuddhistDate otherDate = (ThaiBuddhistDate) obj;
    456             return this.isoDate.equals(otherDate.isoDate);
    457         }
    458         return false;
    459     }
    460 
    461     /**
    462      * A hash code for this date.
    463      *
    464      * @return a suitable hash code based only on the Chronology and the date
    465      */
    466     @Override  // override for performance
    467     public int hashCode() {
    468         return getChronology().getId().hashCode() ^ isoDate.hashCode();
    469     }
    470 
    471     //-----------------------------------------------------------------------
    472     /**
    473      * Defend against malicious streams.
    474      *
    475      * @param s the stream to read
    476      * @throws InvalidObjectException always
    477      */
    478     private void readObject(ObjectInputStream s) throws InvalidObjectException {
    479         throw new InvalidObjectException("Deserialization via serialization delegate");
    480     }
    481 
    482     /**
    483      * Writes the object using a
    484      * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
    485      * @serialData
    486      * <pre>
    487      *  out.writeByte(10);                // identifies a ThaiBuddhistDate
    488      *  out.writeInt(get(YEAR));
    489      *  out.writeByte(get(MONTH_OF_YEAR));
    490      *  out.writeByte(get(DAY_OF_MONTH));
    491      * </pre>
    492      *
    493      * @return the instance of {@code Ser}, not null
    494      */
    495     private Object writeReplace() {
    496         return new Ser(Ser.THAIBUDDHIST_DATE_TYPE, this);
    497     }
    498 
    499     void writeExternal(DataOutput out) throws IOException {
    500         // ThaiBuddhistChronology is implicit in the THAIBUDDHIST_DATE_TYPE
    501         out.writeInt(this.get(YEAR));
    502         out.writeByte(this.get(MONTH_OF_YEAR));
    503         out.writeByte(this.get(DAY_OF_MONTH));
    504     }
    505 
    506     static ThaiBuddhistDate readExternal(DataInput in) throws IOException {
    507         int year = in.readInt();
    508         int month = in.readByte();
    509         int dayOfMonth = in.readByte();
    510         return ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth);
    511     }
    512 
    513 }
    514