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 java.io.Serializable;
035import java.util.Arrays;
036import java.util.Calendar;
037import java.util.HashMap;
038import java.util.List;
039import java.util.Locale;
040import java.util.Map;
041
042import org.threeten.bp.DateTimeException;
043import org.threeten.bp.LocalDate;
044import org.threeten.bp.Year;
045import org.threeten.bp.temporal.Chrono;
046import org.threeten.bp.temporal.ChronoField;
047import org.threeten.bp.temporal.ChronoLocalDate;
048import org.threeten.bp.temporal.Era;
049import org.threeten.bp.temporal.ISOChrono;
050import org.threeten.bp.temporal.TemporalAccessor;
051import org.threeten.bp.temporal.ValueRange;
052
053import sun.util.calendar.CalendarSystem;
054import sun.util.calendar.LocalGregorianCalendar;
055
056/**
057 * The Japanese Imperial calendar system.
058 * <p>
059 * This chronology defines the rules of the Japanese Imperial calendar system.
060 * This calendar system is primarily used in Japan.
061 * The Japanese Imperial calendar system is the same as the ISO calendar system
062 * apart from the era-based year numbering.
063 * <p>
064 * Only Meiji (1865-04-07 - 1868-09-07) and later eras are supported.
065 * Older eras are handled as an unknown era where the year-of-era is the ISO year.
066 *
067 * <h3>Specification for implementors</h3>
068 * This class is immutable and thread-safe.
069 */
070public final class JapaneseChrono extends Chrono<JapaneseChrono> implements Serializable {
071    // TODO: definition for unknown era may break requirement that year-of-era >= 1
072
073    static final LocalGregorianCalendar JCAL =
074        (LocalGregorianCalendar) CalendarSystem.forName("japanese");
075
076    // Locale for creating a JapaneseImpericalCalendar.
077    static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
078
079    /**
080     * Singleton instance for Japanese chronology.
081     */
082    public static final JapaneseChrono INSTANCE = new JapaneseChrono();
083
084    /**
085     * The singleton instance for the before Meiji era ( - 1868-09-07)
086     * which has the value -999.
087     */
088    public static final Era<JapaneseChrono> ERA_SEIREKI = JapaneseEra.SEIREKI;
089    /**
090     * The singleton instance for the Meiji era (1868-09-08 - 1912-07-29)
091     * which has the value -1.
092     */
093    public static final Era<JapaneseChrono> ERA_MEIJI = JapaneseEra.MEIJI;
094    /**
095     * The singleton instance for the Taisho era (1912-07-30 - 1926-12-24)
096     * which has the value 0.
097     */
098    public static final Era<JapaneseChrono> ERA_TAISHO = JapaneseEra.TAISHO;
099    /**
100     * The singleton instance for the Showa era (1926-12-25 - 1989-01-07)
101     * which has the value 1.
102     */
103    public static final Era<JapaneseChrono> ERA_SHOWA = JapaneseEra.SHOWA;
104    /**
105     * The singleton instance for the Heisei era (1989-01-08 - current)
106     * which has the value 2.
107     */
108    public static final Era<JapaneseChrono> ERA_HEISEI = JapaneseEra.HEISEI;
109    /**
110     * Serialization version.
111     */
112    private static final long serialVersionUID = 459996390165777884L;
113
114    /**
115     * Narrow names for eras.
116     */
117    private static final Map<String, String[]> ERA_NARROW_NAMES = new HashMap<>();
118    /**
119     * Short names for eras.
120     */
121    private static final Map<String, String[]> ERA_SHORT_NAMES = new HashMap<>();
122    /**
123     * Full names for eras.
124     */
125    private static final Map<String, String[]> ERA_FULL_NAMES = new HashMap<>();
126    /**
127     * Fallback language for the era names.
128     */
129    private static final String FALLBACK_LANGUAGE = "en";
130    /**
131     * Language that has the era names.
132     */
133    private static final String TARGET_LANGUAGE = "ja";
134
135    /**
136     * Name data.
137     */
138    // TODO: replace all the hard-coded Maps with locale resources
139    static {
140        ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"});
141        ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"});
142        ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"});
143        ERA_SHORT_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "\u6176", "\u660e", "\u5927", "\u662d", "\u5e73"});
144        ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "Keio", "Meiji", "Taisho", "Showa", "Heisei"});
145        ERA_FULL_NAMES.put(TARGET_LANGUAGE,
146                new String[]{"Unknown", "\u6176\u5fdc", "\u660e\u6cbb", "\u5927\u6b63", "\u662d\u548c", "\u5e73\u6210"});
147    }
148
149    //-----------------------------------------------------------------------
150    /**
151     * Restricted constructor.
152     */
153    private JapaneseChrono() {
154    }
155
156    /**
157     * Resolve singleton.
158     *
159     * @return the singleton instance, not null
160     */
161    private Object readResolve() {
162        return INSTANCE;
163    }
164
165    //-----------------------------------------------------------------------
166    /**
167     * Gets the ID of the chronology - 'Japanese'.
168     * <p>
169     * The ID uniquely identifies the {@code Chrono}.
170     * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
171     *
172     * @return the chronology ID - 'Japanese'
173     * @see #getCalendarType()
174     */
175    @Override
176    public String getId() {
177        return "Japanese";
178    }
179
180    /**
181     * Gets the calendar type of the underlying calendar system - 'japanese'.
182     * <p>
183     * The calendar type is an identifier defined by the
184     * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
185     * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
186     * It can also be used as part of a locale, accessible via
187     * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
188     *
189     * @return the calendar system type - 'japanese'
190     * @see #getId()
191     */
192    @Override
193    public String getCalendarType() {
194        return "japanese";
195    }
196
197    //-----------------------------------------------------------------------
198    @Override
199    public ChronoLocalDate<JapaneseChrono> date(Era<JapaneseChrono> era, int yearOfEra, int month, int dayOfMonth) {
200        if (era instanceof JapaneseEra == false) {
201            throw new DateTimeException("Era must be JapaneseEra");
202        }
203        return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth);
204    }
205
206    @Override
207    public ChronoLocalDate<JapaneseChrono> date(int prolepticYear, int month, int dayOfMonth) {
208        return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth));
209    }
210
211    @Override
212    public ChronoLocalDate<JapaneseChrono> dateYearDay(int prolepticYear, int dayOfYear) {
213        LocalDate date = LocalDate.ofYearDay(prolepticYear, dayOfYear);
214        return date(prolepticYear, date.getMonthValue(), date.getDayOfMonth());
215    }
216
217    @Override
218    public ChronoLocalDate<JapaneseChrono> date(TemporalAccessor temporal) {
219        if (temporal instanceof JapaneseDate) {
220            return (JapaneseDate) temporal;
221        }
222        return new JapaneseDate(LocalDate.from(temporal));
223    }
224
225    //-----------------------------------------------------------------------
226    /**
227     * Checks if the specified year is a leap year.
228     * <p>
229     * Japanese calendar leap years occur exactly in line with ISO leap years.
230     * This method does not validate the year passed in, and only has a
231     * well-defined result for years in the supported range.
232     *
233     * @param prolepticYear  the proleptic-year to check, not validated for range
234     * @return true if the year is a leap year
235     */
236    @Override
237    public boolean isLeapYear(long prolepticYear) {
238        return ISOChrono.INSTANCE.isLeapYear(prolepticYear);
239    }
240
241    @Override
242    public int prolepticYear(Era<JapaneseChrono> era, int yearOfEra) {
243        if (era instanceof JapaneseEra == false) {
244            throw new DateTimeException("Era must be JapaneseEra");
245        }
246        JapaneseEra jera = (JapaneseEra) era;
247        int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
248        if (yearOfEra == 1) {
249            return gregorianYear;
250        }
251        LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null);
252        jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1);
253        JCAL.normalize(jdate);
254        if (jdate.getNormalizedYear() == gregorianYear) {
255            return gregorianYear;
256        }
257        throw new DateTimeException("invalid yearOfEra value");
258    }
259
260    /**
261     * Returns the calendar system era object from the given numeric value.
262     *
263     * See the description of each Era for the numeric values of:
264     * {@link #ERA_HEISEI}, {@link #ERA_SHOWA},{@link #ERA_TAISHO},
265     * {@link #ERA_MEIJI}), only Meiji and later eras are supported.
266     * Prior to Meiji {@link #ERA_SEIREKI} is used.
267     *
268     * @param eraValue  the era value
269     * @return the Japanese {@code Era} for the given numeric era value
270     * @throws DateTimeException if {@code eraValue} is invalid
271     */
272    @Override
273    public Era<JapaneseChrono> eraOf(int eraValue) {
274        return JapaneseEra.of(eraValue);
275    }
276
277    @Override
278    public List<Era<JapaneseChrono>> eras() {
279        return Arrays.<Era<JapaneseChrono>>asList(JapaneseEra.values());
280    }
281
282    //-----------------------------------------------------------------------
283    @Override
284    public ValueRange range(ChronoField field) {
285        switch (field) {
286            case DAY_OF_MONTH:
287            case DAY_OF_WEEK:
288            case MICRO_OF_DAY:
289            case MICRO_OF_SECOND:
290            case HOUR_OF_DAY:
291            case HOUR_OF_AMPM:
292            case MINUTE_OF_DAY:
293            case MINUTE_OF_HOUR:
294            case SECOND_OF_DAY:
295            case SECOND_OF_MINUTE:
296            case MILLI_OF_DAY:
297            case MILLI_OF_SECOND:
298            case NANO_OF_DAY:
299            case NANO_OF_SECOND:
300            case CLOCK_HOUR_OF_DAY:
301            case CLOCK_HOUR_OF_AMPM:
302            case EPOCH_DAY:
303            case EPOCH_MONTH:
304                return field.range();
305        }
306        Calendar jcal = Calendar.getInstance(LOCALE);
307        int fieldIndex;
308        switch (field) {
309            case ERA:
310                return ValueRange.of(jcal.getMinimum(Calendar.ERA) - JapaneseEra.ERA_OFFSET,
311                                             jcal.getMaximum(Calendar.ERA) - JapaneseEra.ERA_OFFSET);
312            case YEAR:
313            case YEAR_OF_ERA:
314                return ValueRange.of(Year.MIN_VALUE, jcal.getGreatestMinimum(Calendar.YEAR),
315                                             jcal.getLeastMaximum(Calendar.YEAR), Year.MAX_VALUE);
316            case MONTH_OF_YEAR:
317                return ValueRange.of(jcal.getMinimum(Calendar.MONTH) + 1, jcal.getGreatestMinimum(Calendar.MONTH) + 1,
318                                             jcal.getLeastMaximum(Calendar.MONTH) + 1, jcal.getMaximum(Calendar.MONTH) + 1);
319            case DAY_OF_YEAR:
320                fieldIndex = Calendar.DAY_OF_YEAR;
321                break;
322            default:
323                 // TODO: review the remaining fields
324                throw new UnsupportedOperationException("Unimplementable field: " + field);
325        }
326        return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex),
327                                     jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex));
328    }
329
330}