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