1 /* 2 * Copyright (c) 2012, 2019, 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 * The numeric values supported by this method are the same as the 367 * numeric values supported by {@link JapaneseEra#of(int)}. 368 * 369 * @param eraValue the era value 370 * @return the Japanese {@code Era} for the given numeric era value 371 * @throws DateTimeException if {@code eraValue} is invalid 372 */ 373 @Override 374 public JapaneseEra eraOf(int eraValue) { 375 return JapaneseEra.of(eraValue); 376 } 377 378 @Override 379 public List<Era> eras() { 380 return List.of(JapaneseEra.values()); 381 } 382 383 JapaneseEra getCurrentEra() { 384 // Assume that the last JapaneseEra is the current one. 385 JapaneseEra[] eras = JapaneseEra.values(); 386 return eras[eras.length - 1]; 387 } 388 389 //----------------------------------------------------------------------- 390 @Override 391 public ValueRange range(ChronoField field) { 392 switch (field) { 393 case ALIGNED_DAY_OF_WEEK_IN_MONTH: 394 case ALIGNED_DAY_OF_WEEK_IN_YEAR: 395 case ALIGNED_WEEK_OF_MONTH: 396 case ALIGNED_WEEK_OF_YEAR: 397 throw new UnsupportedTemporalTypeException("Unsupported field: " + field); 398 case YEAR_OF_ERA: { 399 Calendar jcal = Calendar.getInstance(LOCALE); 400 int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear(); 401 return ValueRange.of(1, jcal.getGreatestMinimum(Calendar.YEAR), 402 jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions 403 Year.MAX_VALUE - startYear); 404 } 405 case DAY_OF_YEAR: { 406 Calendar jcal = Calendar.getInstance(LOCALE); 407 int fieldIndex = Calendar.DAY_OF_YEAR; 408 return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex), 409 jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex)); 410 } 411 case YEAR: 412 return ValueRange.of(JapaneseDate.MEIJI_6_ISODATE.getYear(), Year.MAX_VALUE); 413 case ERA: 414 return ValueRange.of(JapaneseEra.MEIJI.getValue(), getCurrentEra().getValue()); 415 default: 416 return field.range(); 417 } 418 } 419 420 //----------------------------------------------------------------------- 421 @Override // override for return type 422 public JapaneseDate resolveDate(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { 423 return (JapaneseDate) super.resolveDate(fieldValues, resolverStyle); 424 } 425 426 @Override // override for special Japanese behavior 427 ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { 428 // validate era and year-of-era 429 Long eraLong = fieldValues.get(ERA); 430 JapaneseEra era = null; 431 if (eraLong != null) { 432 era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); // always validated 433 } 434 Long yoeLong = fieldValues.get(YEAR_OF_ERA); 435 int yoe = 0; 436 if (yoeLong != null) { 437 yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); // always validated 438 } 439 // if only year-of-era and no year then invent era unless strict 440 if (era == null && yoeLong != null && fieldValues.containsKey(YEAR) == false && resolverStyle != ResolverStyle.STRICT) { 441 era = JapaneseEra.values()[JapaneseEra.values().length - 1]; 442 } 443 // if both present, then try to create date 444 if (yoeLong != null && era != null) { 445 if (fieldValues.containsKey(MONTH_OF_YEAR)) { 446 if (fieldValues.containsKey(DAY_OF_MONTH)) { 447 return resolveYMD(era, yoe, fieldValues, resolverStyle); 448 } 449 } 450 if (fieldValues.containsKey(DAY_OF_YEAR)) { 451 return resolveYD(era, yoe, fieldValues, resolverStyle); 452 } 453 } 454 return null; 455 } 456 457 private int prolepticYearLenient(JapaneseEra era, int yearOfEra) { 458 return era.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1; 459 } 460 461 private ChronoLocalDate resolveYMD(JapaneseEra era, int yoe, Map<TemporalField,Long> fieldValues, ResolverStyle resolverStyle) { 462 fieldValues.remove(ERA); 463 fieldValues.remove(YEAR_OF_ERA); 464 if (resolverStyle == ResolverStyle.LENIENT) { 465 int y = prolepticYearLenient(era, yoe); 466 long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); 467 long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1); 468 return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS); 469 } 470 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR); 471 int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH); 472 if (resolverStyle == ResolverStyle.SMART) { // previous valid 473 if (yoe < 1) { 474 throw new DateTimeException("Invalid YearOfEra: " + yoe); 475 } 476 int y = prolepticYearLenient(era, yoe); 477 JapaneseDate result; 478 try { 479 result = date(y, moy, dom); 480 } catch (DateTimeException ex) { 481 result = date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth()); 482 } 483 // handle the era being changed 484 // only allow if the new date is in the same Jan-Dec as the era change 485 // determine by ensuring either original yoe or result yoe is 1 486 if (result.getEra() != era && result.get(YEAR_OF_ERA) > 1 && yoe > 1) { 487 throw new DateTimeException("Invalid YearOfEra for Era: " + era + " " + yoe); 488 } 489 return result; 490 } 491 return date(era, yoe, moy, dom); 492 } 493 494 private ChronoLocalDate resolveYD(JapaneseEra era, int yoe, Map <TemporalField,Long> fieldValues, ResolverStyle resolverStyle) { 495 fieldValues.remove(ERA); 496 fieldValues.remove(YEAR_OF_ERA); 497 if (resolverStyle == ResolverStyle.LENIENT) { 498 int y = prolepticYearLenient(era, yoe); 499 long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1); 500 return dateYearDay(y, 1).plus(days, DAYS); 501 } 502 int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR); 503 return dateYearDay(era, yoe, doy); // smart is same as strict 504 } 505 506 //----------------------------------------------------------------------- 507 /** 508 * Writes the Chronology using a 509 * <a href="{@docRoot}/serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>. 510 * @serialData 511 * <pre> 512 * out.writeByte(1); // identifies a Chronology 513 * out.writeUTF(getId()); 514 * </pre> 515 * 516 * @return the instance of {@code Ser}, not null 517 */ 518 @Override 519 Object writeReplace() { 520 return super.writeReplace(); 521 } 522 523 /** 524 * Defend against malicious streams. 525 * 526 * @param s the stream to read 527 * @throws InvalidObjectException always 528 */ 529 private void readObject(ObjectInputStream s) throws InvalidObjectException { 530 throw new InvalidObjectException("Deserialization via serialization delegate"); 531 } 532 }