JapaneseChronology.java revision 10444:f08705540498
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.temporal.ChronoField.DAY_OF_MONTH;
60import static java.time.temporal.ChronoField.DAY_OF_YEAR;
61import static java.time.temporal.ChronoField.ERA;
62import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
63import static java.time.temporal.ChronoField.YEAR;
64import static java.time.temporal.ChronoField.YEAR_OF_ERA;
65import static java.time.temporal.ChronoUnit.DAYS;
66import static java.time.temporal.ChronoUnit.MONTHS;
67
68import java.io.InvalidObjectException;
69import java.io.ObjectInputStream;
70import java.io.Serializable;
71import java.time.Clock;
72import java.time.DateTimeException;
73import java.time.Instant;
74import java.time.LocalDate;
75import java.time.Year;
76import java.time.ZoneId;
77import java.time.format.ResolverStyle;
78import java.time.temporal.ChronoField;
79import java.time.temporal.TemporalAccessor;
80import java.time.temporal.TemporalAdjusters;
81import java.time.temporal.TemporalField;
82import java.time.temporal.UnsupportedTemporalTypeException;
83import java.time.temporal.ValueRange;
84import java.util.Arrays;
85import java.util.Calendar;
86import java.util.List;
87import java.util.Locale;
88import java.util.Map;
89
90import sun.util.calendar.CalendarSystem;
91import sun.util.calendar.LocalGregorianCalendar;
92
93/**
94 * The Japanese Imperial calendar system.
95 * <p>
96 * This chronology defines the rules of the Japanese Imperial calendar system.
97 * This calendar system is primarily used in Japan.
98 * The Japanese Imperial calendar system is the same as the ISO calendar system
99 * apart from the era-based year numbering.
100 * <p>
101 * Japan introduced the Gregorian calendar starting with Meiji 6.
102 * Only Meiji and later eras are supported;
103 * dates before Meiji 6, January 1 are not supported.
104 * <p>
105 * The supported {@code ChronoField} instances are:
106 * <ul>
107 * <li>{@code DAY_OF_WEEK}
108 * <li>{@code DAY_OF_MONTH}
109 * <li>{@code DAY_OF_YEAR}
110 * <li>{@code EPOCH_DAY}
111 * <li>{@code MONTH_OF_YEAR}
112 * <li>{@code PROLEPTIC_MONTH}
113 * <li>{@code YEAR_OF_ERA}
114 * <li>{@code YEAR}
115 * <li>{@code ERA}
116 * </ul>
117 *
118 * @implSpec
119 * This class is immutable and thread-safe.
120 *
121 * @since 1.8
122 */
123public final class JapaneseChronology extends AbstractChronology implements Serializable {
124
125    static final LocalGregorianCalendar JCAL =
126        (LocalGregorianCalendar) CalendarSystem.forName("japanese");
127
128    // Locale for creating a JapaneseImpericalCalendar.
129    static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
130
131    /**
132     * Singleton instance for Japanese chronology.
133     */
134    public static final JapaneseChronology INSTANCE = new JapaneseChronology();
135
136    /**
137     * Serialization version.
138     */
139    private static final long serialVersionUID = 459996390165777884L;
140
141    //-----------------------------------------------------------------------
142    /**
143     * Restricted constructor.
144     */
145    private JapaneseChronology() {
146    }
147
148    //-----------------------------------------------------------------------
149    /**
150     * Gets the ID of the chronology - 'Japanese'.
151     * <p>
152     * The ID uniquely identifies the {@code Chronology}.
153     * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
154     *
155     * @return the chronology ID - 'Japanese'
156     * @see #getCalendarType()
157     */
158    @Override
159    public String getId() {
160        return "Japanese";
161    }
162
163    /**
164     * Gets the calendar type of the underlying calendar system - 'japanese'.
165     * <p>
166     * The calendar type is an identifier defined by the
167     * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
168     * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
169     * It can also be used as part of a locale, accessible via
170     * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
171     *
172     * @return the calendar system type - 'japanese'
173     * @see #getId()
174     */
175    @Override
176    public String getCalendarType() {
177        return "japanese";
178    }
179
180    //-----------------------------------------------------------------------
181    /**
182     * Obtains a local date in Japanese calendar system from the
183     * era, year-of-era, month-of-year and day-of-month fields.
184     * <p>
185     * The Japanese month and day-of-month are the same as those in the
186     * ISO calendar system. They are not reset when the era changes.
187     * For example:
188     * <pre>
189     *  6th Jan Showa 64 = ISO 1989-01-06
190     *  7th Jan Showa 64 = ISO 1989-01-07
191     *  8th Jan Heisei 1 = ISO 1989-01-08
192     *  9th Jan Heisei 1 = ISO 1989-01-09
193     * </pre>
194     *
195     * @param era  the Japanese era, not null
196     * @param yearOfEra  the year-of-era
197     * @param month  the month-of-year
198     * @param dayOfMonth  the day-of-month
199     * @return the Japanese local date, not null
200     * @throws DateTimeException if unable to create the date
201     * @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
202     */
203    @Override
204    public JapaneseDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
205        if (era instanceof JapaneseEra == false) {
206            throw new ClassCastException("Era must be JapaneseEra");
207        }
208        return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth);
209    }
210
211    /**
212     * Obtains a local date in Japanese calendar system from the
213     * proleptic-year, month-of-year and day-of-month fields.
214     * <p>
215     * The Japanese proleptic year, month and day-of-month are the same as those
216     * in the ISO calendar system. They are not reset when the era changes.
217     *
218     * @param prolepticYear  the proleptic-year
219     * @param month  the month-of-year
220     * @param dayOfMonth  the day-of-month
221     * @return the Japanese local date, not null
222     * @throws DateTimeException if unable to create the date
223     */
224    @Override
225    public JapaneseDate date(int prolepticYear, int month, int dayOfMonth) {
226        return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth));
227    }
228
229    /**
230     * Obtains a local date in Japanese calendar system from the
231     * era, year-of-era and day-of-year fields.
232     * <p>
233     * The day-of-year in this factory is expressed relative to the start of the year-of-era.
234     * This definition changes the normal meaning of day-of-year only in those years
235     * where the year-of-era is reset to one due to a change in the era.
236     * For example:
237     * <pre>
238     *  6th Jan Showa 64 = day-of-year 6
239     *  7th Jan Showa 64 = day-of-year 7
240     *  8th Jan Heisei 1 = day-of-year 1
241     *  9th Jan Heisei 1 = day-of-year 2
242     * </pre>
243     *
244     * @param era  the Japanese era, not null
245     * @param yearOfEra  the year-of-era
246     * @param dayOfYear  the day-of-year
247     * @return the Japanese local date, not null
248     * @throws DateTimeException if unable to create the date
249     * @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
250     */
251    @Override
252    public JapaneseDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
253        return JapaneseDate.ofYearDay((JapaneseEra) era, yearOfEra, dayOfYear);
254    }
255
256    /**
257     * Obtains a local date in Japanese calendar system from the
258     * proleptic-year and day-of-year fields.
259     * <p>
260     * The day-of-year in this factory is expressed relative to the start of the proleptic year.
261     * The Japanese proleptic year and day-of-year are the same as those in the ISO calendar system.
262     * They are not reset when the era changes.
263     *
264     * @param prolepticYear  the proleptic-year
265     * @param dayOfYear  the day-of-year
266     * @return the Japanese local date, not null
267     * @throws DateTimeException if unable to create the date
268     */
269    @Override
270    public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) {
271        return new JapaneseDate(LocalDate.ofYearDay(prolepticYear, dayOfYear));
272    }
273
274    /**
275     * Obtains a local date in the Japanese calendar system from the epoch-day.
276     *
277     * @param epochDay  the epoch day
278     * @return the Japanese local date, not null
279     * @throws DateTimeException if unable to create the date
280     */
281    @Override  // override with covariant return type
282    public JapaneseDate dateEpochDay(long epochDay) {
283        return new JapaneseDate(LocalDate.ofEpochDay(epochDay));
284    }
285
286    @Override
287    public JapaneseDate dateNow() {
288        return dateNow(Clock.systemDefaultZone());
289    }
290
291    @Override
292    public JapaneseDate dateNow(ZoneId zone) {
293        return dateNow(Clock.system(zone));
294    }
295
296    @Override
297    public JapaneseDate dateNow(Clock clock) {
298        return date(LocalDate.now(clock));
299    }
300
301    @Override
302    public JapaneseDate date(TemporalAccessor temporal) {
303        if (temporal instanceof JapaneseDate) {
304            return (JapaneseDate) temporal;
305        }
306        return new JapaneseDate(LocalDate.from(temporal));
307    }
308
309    @Override
310    @SuppressWarnings("unchecked")
311    public ChronoLocalDateTime<JapaneseDate> localDateTime(TemporalAccessor temporal) {
312        return (ChronoLocalDateTime<JapaneseDate>)super.localDateTime(temporal);
313    }
314
315    @Override
316    @SuppressWarnings("unchecked")
317    public ChronoZonedDateTime<JapaneseDate> zonedDateTime(TemporalAccessor temporal) {
318        return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(temporal);
319    }
320
321    @Override
322    @SuppressWarnings("unchecked")
323    public ChronoZonedDateTime<JapaneseDate> zonedDateTime(Instant instant, ZoneId zone) {
324        return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(instant, zone);
325    }
326
327    //-----------------------------------------------------------------------
328    /**
329     * Checks if the specified year is a leap year.
330     * <p>
331     * Japanese calendar leap years occur exactly in line with ISO leap years.
332     * This method does not validate the year passed in, and only has a
333     * well-defined result for years in the supported range.
334     *
335     * @param prolepticYear  the proleptic-year to check, not validated for range
336     * @return true if the year is a leap year
337     */
338    @Override
339    public boolean isLeapYear(long prolepticYear) {
340        return IsoChronology.INSTANCE.isLeapYear(prolepticYear);
341    }
342
343    @Override
344    public int prolepticYear(Era era, int yearOfEra) {
345        if (era instanceof JapaneseEra == false) {
346            throw new ClassCastException("Era must be JapaneseEra");
347        }
348
349        JapaneseEra jera = (JapaneseEra) era;
350        int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
351        if (yearOfEra == 1) {
352            return gregorianYear;
353        }
354        if (gregorianYear >= Year.MIN_VALUE && gregorianYear <= Year.MAX_VALUE) {
355            LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null);
356            jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1);
357            if (JapaneseChronology.JCAL.validate(jdate)) {
358                return gregorianYear;
359            }
360        }
361        throw new DateTimeException("Invalid yearOfEra value");
362    }
363
364    /**
365     * Returns the calendar system era object from the given numeric value.
366     *
367     * See the description of each Era for the numeric values of:
368     * {@link JapaneseEra#HEISEI}, {@link JapaneseEra#SHOWA},{@link JapaneseEra#TAISHO},
369     * {@link JapaneseEra#MEIJI}), only Meiji and later eras are supported.
370     *
371     * @param eraValue  the era value
372     * @return the Japanese {@code Era} for the given numeric era value
373     * @throws DateTimeException if {@code eraValue} is invalid
374     */
375    @Override
376    public JapaneseEra eraOf(int eraValue) {
377        return JapaneseEra.of(eraValue);
378    }
379
380    @Override
381    public List<Era> eras() {
382        return Arrays.<Era>asList(JapaneseEra.values());
383    }
384
385    JapaneseEra getCurrentEra() {
386        // Assume that the last JapaneseEra is the current one.
387        JapaneseEra[] eras = JapaneseEra.values();
388        return eras[eras.length - 1];
389    }
390
391    //-----------------------------------------------------------------------
392    @Override
393    public ValueRange range(ChronoField field) {
394        switch (field) {
395            case ALIGNED_DAY_OF_WEEK_IN_MONTH:
396            case ALIGNED_DAY_OF_WEEK_IN_YEAR:
397            case ALIGNED_WEEK_OF_MONTH:
398            case ALIGNED_WEEK_OF_YEAR:
399                throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
400            case YEAR_OF_ERA: {
401                Calendar jcal = Calendar.getInstance(LOCALE);
402                int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear();
403                return ValueRange.of(1, jcal.getGreatestMinimum(Calendar.YEAR),
404                        jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions
405                        Year.MAX_VALUE - startYear);
406            }
407            case DAY_OF_YEAR: {
408                Calendar jcal = Calendar.getInstance(LOCALE);
409                int fieldIndex = Calendar.DAY_OF_YEAR;
410                return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex),
411                        jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex));
412            }
413            case YEAR:
414                return ValueRange.of(JapaneseDate.MEIJI_6_ISODATE.getYear(), Year.MAX_VALUE);
415            case ERA:
416                return ValueRange.of(JapaneseEra.MEIJI.getValue(), getCurrentEra().getValue());
417            default:
418                return field.range();
419        }
420    }
421
422    //-----------------------------------------------------------------------
423    @Override  // override for return type
424    public JapaneseDate resolveDate(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
425        return (JapaneseDate) super.resolveDate(fieldValues, resolverStyle);
426    }
427
428    @Override  // override for special Japanese behavior
429    ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
430        // validate era and year-of-era
431        Long eraLong = fieldValues.get(ERA);
432        JapaneseEra era = null;
433        if (eraLong != null) {
434            era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA));  // always validated
435        }
436        Long yoeLong = fieldValues.get(YEAR_OF_ERA);
437        int yoe = 0;
438        if (yoeLong != null) {
439            yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);  // always validated
440        }
441        // if only year-of-era and no year then invent era unless strict
442        if (era == null && yoeLong != null && fieldValues.containsKey(YEAR) == false && resolverStyle != ResolverStyle.STRICT) {
443            era = JapaneseEra.values()[JapaneseEra.values().length - 1];
444        }
445        // if both present, then try to create date
446        if (yoeLong != null && era != null) {
447            if (fieldValues.containsKey(MONTH_OF_YEAR)) {
448                if (fieldValues.containsKey(DAY_OF_MONTH)) {
449                    return resolveYMD(era, yoe, fieldValues, resolverStyle);
450                }
451            }
452            if (fieldValues.containsKey(DAY_OF_YEAR)) {
453                return resolveYD(era, yoe, fieldValues, resolverStyle);
454            }
455        }
456        return null;
457    }
458
459    private int prolepticYearLenient(JapaneseEra era, int yearOfEra) {
460        return era.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
461    }
462
463     private ChronoLocalDate resolveYMD(JapaneseEra era, int yoe, Map<TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
464         fieldValues.remove(ERA);
465         fieldValues.remove(YEAR_OF_ERA);
466         if (resolverStyle == ResolverStyle.LENIENT) {
467             int y = prolepticYearLenient(era, yoe);
468             long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
469             long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
470             return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
471         }
472         int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
473         int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
474         if (resolverStyle == ResolverStyle.SMART) {  // previous valid
475             if (yoe < 1) {
476                 throw new DateTimeException("Invalid YearOfEra: " + yoe);
477             }
478             int y = prolepticYearLenient(era, yoe);
479             JapaneseDate result;
480             try {
481                 result = date(y, moy, dom);
482             } catch (DateTimeException ex) {
483                 result = date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth());
484             }
485             // handle the era being changed
486             // only allow if the new date is in the same Jan-Dec as the era change
487             // determine by ensuring either original yoe or result yoe is 1
488             if (result.getEra() != era && result.get(YEAR_OF_ERA) > 1 && yoe > 1) {
489                 throw new DateTimeException("Invalid YearOfEra for Era: " + era + " " + yoe);
490             }
491             return result;
492         }
493         return date(era, yoe, moy, dom);
494     }
495
496    private ChronoLocalDate resolveYD(JapaneseEra era, int yoe, Map <TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
497        fieldValues.remove(ERA);
498        fieldValues.remove(YEAR_OF_ERA);
499        if (resolverStyle == ResolverStyle.LENIENT) {
500            int y = prolepticYearLenient(era, yoe);
501            long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
502            return dateYearDay(y, 1).plus(days, DAYS);
503        }
504        int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
505        return dateYearDay(era, yoe, doy);  // smart is same as strict
506    }
507
508    //-----------------------------------------------------------------------
509    /**
510     * Writes the Chronology using a
511     * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
512     * @serialData
513     * <pre>
514     *  out.writeByte(1);     // identifies a Chronology
515     *  out.writeUTF(getId());
516     * </pre>
517     *
518     * @return the instance of {@code Ser}, not null
519     */
520    @Override
521    Object writeReplace() {
522        return super.writeReplace();
523    }
524
525    /**
526     * Defend against malicious streams.
527     *
528     * @param s the stream to read
529     * @throws InvalidObjectException always
530     */
531    private void readObject(ObjectInputStream s) throws InvalidObjectException {
532        throw new InvalidObjectException("Deserialization via serialization delegate");
533    }
534}
535