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.Arrays; 85 import java.util.Calendar; 86 import java.util.List; 87 import java.util.Locale; 88 import java.util.Map; 89 90 import sun.util.calendar.CalendarSystem; 91 import 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 */ 123 public 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 }