001/*
002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos
003 *
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 *  * Redistributions of source code must retain the above copyright notice,
010 *    this list of conditions and the following disclaimer.
011 *
012 *  * Redistributions in binary form must reproduce the above copyright notice,
013 *    this list of conditions and the following disclaimer in the documentation
014 *    and/or other materials provided with the distribution.
015 *
016 *  * Neither the name of JSR-310 nor the names of its contributors
017 *    may be used to endorse or promote products derived from this software
018 *    without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032package org.threeten.bp.calendar;
033
034import static org.threeten.bp.temporal.ChronoField.YEAR;
035
036import java.io.Serializable;
037import java.util.Arrays;
038import java.util.List;
039import java.util.Locale;
040
041import org.threeten.bp.DateTimeException;
042import org.threeten.bp.LocalDate;
043import org.threeten.bp.temporal.Chrono;
044import org.threeten.bp.temporal.ChronoField;
045import org.threeten.bp.temporal.ChronoLocalDate;
046import org.threeten.bp.temporal.Era;
047import org.threeten.bp.temporal.ISOChrono;
048import org.threeten.bp.temporal.TemporalAccessor;
049import org.threeten.bp.temporal.ValueRange;
050
051/**
052 * The Minguo calendar system.
053 * <p>
054 * This chronology defines the rules of the Minguo calendar system.
055 * This calendar system is primarily used in the Republic of China, often known as Taiwan.
056 * Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1911-01-01 (ISO)}.
057 * <p>
058 * The fields are defined as follows:
059 * <p><ul>
060 * <li>era - There are two eras, the current 'Republic' (ERA_ROC) and the previous era (ERA_BEFORE_ROC).
061 * <li>year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one.
062 *  For the previous era the year increases from one as time goes backwards.
063 *  The value for the current era is equal to the ISO proleptic-year minus 1911.
064 * <li>proleptic-year - The proleptic year is the same as the year-of-era for the
065 *  current era. For the previous era, years have zero, then negative values.
066 *  The value is equal to the ISO proleptic-year minus 1911.
067 * <li>month-of-year - The Minguo month-of-year exactly matches ISO.
068 * <li>day-of-month - The Minguo day-of-month exactly matches ISO.
069 * <li>day-of-year - The Minguo day-of-year exactly matches ISO.
070 * <li>leap-year - The Minguo leap-year pattern exactly matches ISO, such that the two calendars
071 *  are never out of step.
072 * </ul><p>
073 *
074 * <h3>Specification for implementors</h3>
075 * This class is immutable and thread-safe.
076 */
077public final class MinguoChrono extends Chrono<MinguoChrono> implements Serializable {
078
079    /**
080     * Singleton instance for the Minguo chronology.
081     */
082    public static final MinguoChrono INSTANCE = new MinguoChrono();
083
084    /**
085     * The singleton instance for the era ROC.
086     */
087    public static final Era<MinguoChrono> ERA_ROC = MinguoEra.ROC;
088
089    /**
090     * The singleton instance for the era BEFORE_ROC.
091     */
092    public static final Era<MinguoChrono> ERA_BEFORE_ROC = MinguoEra.BEFORE_ROC;
093
094    /**
095     * Serialization version.
096     */
097    private static final long serialVersionUID = 1039765215346859963L;
098    /**
099     * The difference in years between ISO and Minguo.
100     */
101    static final int YEARS_DIFFERENCE = 1911;
102
103    /**
104     * Restricted constructor.
105     */
106    private MinguoChrono() {
107    }
108
109    /**
110     * Resolve singleton.
111     *
112     * @return the singleton instance, not null
113     */
114    private Object readResolve() {
115        return INSTANCE;
116    }
117
118    //-----------------------------------------------------------------------
119    /**
120     * Gets the ID of the chronology - 'Minguo'.
121     * <p>
122     * The ID uniquely identifies the {@code Chrono}.
123     * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
124     *
125     * @return the chronology ID - 'Minguo'
126     * @see #getCalendarType()
127     */
128    @Override
129    public String getId() {
130        return "Minguo";
131    }
132
133    /**
134     * Gets the calendar type of the underlying calendar system - 'roc'.
135     * <p>
136     * The calendar type is an identifier defined by the
137     * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
138     * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
139     * It can also be used as part of a locale, accessible via
140     * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
141     *
142     * @return the calendar system type - 'roc'
143     * @see #getId()
144     */
145    @Override
146    public String getCalendarType() {
147        return "roc";
148    }
149
150    //-----------------------------------------------------------------------
151    @Override
152    public ChronoLocalDate<MinguoChrono> date(int prolepticYear, int month, int dayOfMonth) {
153        return new MinguoDate(LocalDate.of(prolepticYear + YEARS_DIFFERENCE, month, dayOfMonth));
154    }
155
156    @Override
157    public ChronoLocalDate<MinguoChrono> dateYearDay(int prolepticYear, int dayOfYear) {
158        return new MinguoDate(LocalDate.ofYearDay(prolepticYear + YEARS_DIFFERENCE, dayOfYear));
159    }
160
161    @Override
162    public ChronoLocalDate<MinguoChrono> date(TemporalAccessor temporal) {
163        if (temporal instanceof MinguoDate) {
164            return (MinguoDate) temporal;
165        }
166        return new MinguoDate(LocalDate.from(temporal));
167    }
168
169    //-----------------------------------------------------------------------
170    /**
171     * Checks if the specified year is a leap year.
172     * <p>
173     * Minguo leap years occur exactly in line with ISO leap years.
174     * This method does not validate the year passed in, and only has a
175     * well-defined result for years in the supported range.
176     *
177     * @param prolepticYear  the proleptic-year to check, not validated for range
178     * @return true if the year is a leap year
179     */
180    @Override
181    public boolean isLeapYear(long prolepticYear) {
182        return ISOChrono.INSTANCE.isLeapYear(prolepticYear + YEARS_DIFFERENCE);
183    }
184
185    @Override
186    public int prolepticYear(Era<MinguoChrono> era, int yearOfEra) {
187        if (era instanceof MinguoEra == false) {
188            throw new DateTimeException("Era must be MinguoEra");
189        }
190        return (era == MinguoEra.ROC ? yearOfEra : 1 - yearOfEra);
191    }
192
193    @Override
194    public Era<MinguoChrono> eraOf(int eraValue) {
195        return MinguoEra.of(eraValue);
196    }
197
198    @Override
199    public List<Era<MinguoChrono>> eras() {
200        return Arrays.<Era<MinguoChrono>>asList(MinguoEra.values());
201    }
202
203    //-----------------------------------------------------------------------
204    @Override
205    public ValueRange range(ChronoField field) {
206        switch (field) {
207            case YEAR_OF_ERA: {
208                ValueRange range = YEAR.range();
209                return ValueRange.of(1, range.getMaximum() - YEARS_DIFFERENCE, -range.getMinimum() + 1 + YEARS_DIFFERENCE);
210            }
211            case YEAR: {
212                ValueRange range = YEAR.range();
213                return ValueRange.of(range.getMinimum() - YEARS_DIFFERENCE, range.getMaximum() - YEARS_DIFFERENCE);
214            }
215        }
216        return field.range();
217    }
218
219}