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 */
57package java.time.chrono;
58
59import static java.time.chrono.MinguoChronology.YEARS_DIFFERENCE;
60import static java.time.temporal.ChronoField.DAY_OF_MONTH;
61import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
62import static java.time.temporal.ChronoField.YEAR;
63
64import java.io.DataInput;
65import java.io.DataOutput;
66import java.io.IOException;
67import java.io.InvalidObjectException;
68import java.io.ObjectInputStream;
69import java.io.Serializable;
70import java.time.Clock;
71import java.time.DateTimeException;
72import java.time.LocalDate;
73import java.time.LocalTime;
74import java.time.Period;
75import java.time.ZoneId;
76import java.time.temporal.ChronoField;
77import java.time.temporal.TemporalAccessor;
78import java.time.temporal.TemporalAdjuster;
79import java.time.temporal.TemporalAmount;
80import java.time.temporal.TemporalField;
81import java.time.temporal.TemporalQuery;
82import java.time.temporal.TemporalUnit;
83import java.time.temporal.UnsupportedTemporalTypeException;
84import java.time.temporal.ValueRange;
85import java.util.Objects;
86
87// Android-changed: removed ValueBased paragraph.
88/**
89 * A date in the Minguo calendar system.
90 * <p>
91 * This date operates using the {@linkplain MinguoChronology Minguo calendar}.
92 * This calendar system is primarily used in the Republic of China, often known as Taiwan.
93 * Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1912-01-01 (ISO)}.
94 *
95 * @implSpec
96 * This class is immutable and thread-safe.
97 *
98 * @since 1.8
99 */
100public final class MinguoDate
101        extends ChronoLocalDateImpl<MinguoDate>
102        implements ChronoLocalDate, Serializable {
103
104    /**
105     * Serialization version.
106     */
107    private static final long serialVersionUID = 1300372329181994526L;
108
109    /**
110     * The underlying date.
111     */
112    private final transient LocalDate isoDate;
113
114    //-----------------------------------------------------------------------
115    /**
116     * Obtains the current {@code MinguoDate} 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 MinguoDate now() {
127        return now(Clock.systemDefaultZone());
128    }
129
130    /**
131     * Obtains the current {@code MinguoDate} 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 MinguoDate now(ZoneId zone) {
143        return now(Clock.system(zone));
144    }
145
146    /**
147     * Obtains the current {@code MinguoDate} 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 MinguoDate now(Clock clock) {
158        return new MinguoDate(LocalDate.now(clock));
159    }
160
161    /**
162     * Obtains a {@code MinguoDate} representing a date in the Minguo calendar
163     * system from the proleptic-year, month-of-year and day-of-month fields.
164     * <p>
165     * This returns a {@code MinguoDate} 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 Minguo proleptic-year
169     * @param month  the Minguo month-of-year, from 1 to 12
170     * @param dayOfMonth  the Minguo day-of-month, from 1 to 31
171     * @return the date in Minguo 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 MinguoDate of(int prolepticYear, int month, int dayOfMonth) {
176        return new MinguoDate(LocalDate.of(prolepticYear + YEARS_DIFFERENCE, month, dayOfMonth));
177    }
178
179    /**
180     * Obtains a {@code MinguoDate} from a temporal object.
181     * <p>
182     * This obtains a date in the Minguo 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 MinguoDate}.
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 MinguoDate::from}.
191     *
192     * @param temporal  the temporal object to convert, not null
193     * @return the date in Minguo calendar system, not null
194     * @throws DateTimeException if unable to convert to a {@code MinguoDate}
195     */
196    public static MinguoDate from(TemporalAccessor temporal) {
197        return MinguoChronology.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    MinguoDate(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 Minguo 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 Minguo chronology, not null
219     */
220    @Override
221    public MinguoChronology getChronology() {
222        return MinguoChronology.INSTANCE;
223    }
224
225    /**
226     * Gets the era applicable at this date.
227     * <p>
228     * The Minguo calendar system has two eras, 'ROC' and 'BEFORE_ROC',
229     * defined by {@link MinguoEra}.
230     *
231     * @return the era applicable at this date, not null
232     */
233    @Override
234    public MinguoEra getEra() {
235        return (getProlepticYear() >= 1 ? MinguoEra.ROC : MinguoEra.BEFORE_ROC);
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() + 1 + YEARS_DIFFERENCE : 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 MinguoDate 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 + YEARS_DIFFERENCE : (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  MinguoDate 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 MinguoDate 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 MinguoDate minus(TemporalAmount amount) {
361        return super.minus(amount);
362    }
363
364    //-----------------------------------------------------------------------
365    @Override
366    MinguoDate plusYears(long years) {
367        return with(isoDate.plusYears(years));
368    }
369
370    @Override
371    MinguoDate plusMonths(long months) {
372        return with(isoDate.plusMonths(months));
373    }
374
375    @Override
376    MinguoDate plusWeeks(long weeksToAdd) {
377        return super.plusWeeks(weeksToAdd);
378    }
379
380    @Override
381    MinguoDate plusDays(long days) {
382        return with(isoDate.plusDays(days));
383    }
384
385    @Override
386    public MinguoDate plus(long amountToAdd, TemporalUnit unit) {
387        return super.plus(amountToAdd, unit);
388    }
389
390    @Override
391    public MinguoDate minus(long amountToAdd, TemporalUnit unit) {
392        return super.minus(amountToAdd, unit);
393    }
394
395    @Override
396    MinguoDate minusYears(long yearsToSubtract) {
397        return super.minusYears(yearsToSubtract);
398    }
399
400    @Override
401    MinguoDate minusMonths(long monthsToSubtract) {
402        return super.minusMonths(monthsToSubtract);
403    }
404
405    @Override
406    MinguoDate minusWeeks(long weeksToSubtract) {
407        return super.minusWeeks(weeksToSubtract);
408    }
409
410    @Override
411    MinguoDate minusDays(long daysToSubtract) {
412        return super.minusDays(daysToSubtract);
413    }
414
415    private MinguoDate with(LocalDate newDate) {
416        return (newDate.equals(isoDate) ? this : new MinguoDate(newDate));
417    }
418
419    @Override        // for javadoc and covariant return type
420    @SuppressWarnings("unchecked")
421    public final ChronoLocalDateTime<MinguoDate> atTime(LocalTime localTime) {
422        return (ChronoLocalDateTime<MinguoDate>)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 MinguoDate} with another ensuring that the date is the same.
441     * <p>
442     * Only objects of type {@code MinguoDate} 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 MinguoDate) {
455            MinguoDate otherDate = (MinguoDate) 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(8);                 // identifies a MinguoDate
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.MINGUO_DATE_TYPE, this);
497    }
498
499    void writeExternal(DataOutput out) throws IOException {
500        // MinguoChronology is implicit in the MINGUO_DATE_TYPE
501        out.writeInt(get(YEAR));
502        out.writeByte(get(MONTH_OF_YEAR));
503        out.writeByte(get(DAY_OF_MONTH));
504    }
505
506    static MinguoDate readExternal(DataInput in) throws IOException {
507        int year = in.readInt();
508        int month = in.readByte();
509        int dayOfMonth = in.readByte();
510        return MinguoChronology.INSTANCE.date(year, month, dayOfMonth);
511    }
512
513}
514