1 /* 2 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * * Neither the name of JSR-310 nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.threeten.bp.chrono; 33 34 import static org.threeten.bp.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; 35 import static org.threeten.bp.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; 36 import static org.threeten.bp.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; 37 import static org.threeten.bp.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; 38 import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH; 39 import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK; 40 import static org.threeten.bp.temporal.ChronoField.DAY_OF_YEAR; 41 import static org.threeten.bp.temporal.ChronoField.EPOCH_DAY; 42 import static org.threeten.bp.temporal.ChronoField.ERA; 43 import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR; 44 import static org.threeten.bp.temporal.ChronoField.PROLEPTIC_MONTH; 45 import static org.threeten.bp.temporal.ChronoField.YEAR; 46 import static org.threeten.bp.temporal.ChronoField.YEAR_OF_ERA; 47 import static org.threeten.bp.temporal.ChronoUnit.DAYS; 48 import static org.threeten.bp.temporal.ChronoUnit.MONTHS; 49 import static org.threeten.bp.temporal.ChronoUnit.WEEKS; 50 import static org.threeten.bp.temporal.TemporalAdjusters.nextOrSame; 51 52 import java.io.Serializable; 53 import java.util.Arrays; 54 import java.util.Calendar; 55 import java.util.HashMap; 56 import java.util.List; 57 import java.util.Locale; 58 import java.util.Map; 59 60 import org.threeten.bp.Clock; 61 import org.threeten.bp.DateTimeException; 62 import org.threeten.bp.DayOfWeek; 63 import org.threeten.bp.Instant; 64 import org.threeten.bp.LocalDate; 65 import org.threeten.bp.ZoneId; 66 import org.threeten.bp.format.ResolverStyle; 67 import org.threeten.bp.jdk8.Jdk8Methods; 68 import org.threeten.bp.temporal.ChronoField; 69 import org.threeten.bp.temporal.TemporalAccessor; 70 import org.threeten.bp.temporal.TemporalField; 71 import org.threeten.bp.temporal.ValueRange; 72 73 /** 74 * The Japanese Imperial calendar system. 75 * <p> 76 * This chronology defines the rules of the Japanese Imperial calendar system. 77 * This calendar system is primarily used in Japan. 78 * The Japanese Imperial calendar system is the same as the ISO calendar system 79 * apart from the era-based year numbering. 80 * <p> 81 * Japan introduced the Gregorian calendar starting with Meiji 6. 82 * Only Meiji and later eras are supported; 83 * dates before Meiji 6, January 1 are not supported. 84 * <p> 85 * The supported {@code ChronoField} instances are: 86 * <ul> 87 * <li>{@code DAY_OF_WEEK} 88 * <li>{@code DAY_OF_MONTH} 89 * <li>{@code DAY_OF_YEAR} 90 * <li>{@code EPOCH_DAY} 91 * <li>{@code MONTH_OF_YEAR} 92 * <li>{@code PROLEPTIC_MONTH} 93 * <li>{@code YEAR_OF_ERA} 94 * <li>{@code YEAR} 95 * <li>{@code ERA} 96 * </ul> 97 * 98 * <h3>Specification for implementors</h3> 99 * This class is immutable and thread-safe. 100 */ 101 public final class JapaneseChronology extends Chronology implements Serializable { 102 103 // Locale for creating a JapaneseImpericalCalendar. 104 static final Locale LOCALE = new Locale("ja", "JP", "JP"); 105 106 /** 107 * Singleton instance for Japanese chronology. 108 */ 109 public static final JapaneseChronology INSTANCE = new JapaneseChronology(); 110 111 /** 112 * Serialization version. 113 */ 114 private static final long serialVersionUID = 459996390165777884L; 115 116 /** 117 * Narrow names for eras. 118 */ 119 private static final Map<String, String[]> ERA_NARROW_NAMES = new HashMap<String, String[]>(); 120 /** 121 * Short names for eras. 122 */ 123 private static final Map<String, String[]> ERA_SHORT_NAMES = new HashMap<String, String[]>(); 124 /** 125 * Full names for eras. 126 */ 127 private static final Map<String, String[]> ERA_FULL_NAMES = new HashMap<String, String[]>(); 128 /** 129 * Fallback language for the era names. 130 */ 131 private static final String FALLBACK_LANGUAGE = "en"; 132 /** 133 * Language that has the era names. 134 */ 135 private static final String TARGET_LANGUAGE = "ja"; 136 137 /** 138 * Name data. 139 */ 140 // TODO: replace all the hard-coded Maps with locale resources 141 static { ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"})142 ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"}); ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"})143 ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"}); ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"})144 ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"}); ERA_SHORT_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "\\u6176", "\\u660e", "\\u5927", "\\u662d", "\\u5e73"})145 ERA_SHORT_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "\u6176", "\u660e", "\u5927", "\u662d", "\u5e73"}); ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "Keio", "Meiji", "Taisho", "Showa", "Heisei"})146 ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "Keio", "Meiji", "Taisho", "Showa", "Heisei"}); ERA_FULL_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "\\u6176\\u5fdc", "\\u660e\\u6cbb", "\\u5927\\u6b63", "\\u662d\\u548c", "\\u5e73\\u6210"})147 ERA_FULL_NAMES.put(TARGET_LANGUAGE, 148 new String[]{"Unknown", "\u6176\u5fdc", "\u660e\u6cbb", "\u5927\u6b63", "\u662d\u548c", "\u5e73\u6210"}); 149 } 150 151 //----------------------------------------------------------------------- 152 /** 153 * Restricted constructor. 154 */ JapaneseChronology()155 private JapaneseChronology() { 156 } 157 158 /** 159 * Resolve singleton. 160 * 161 * @return the singleton instance, not null 162 */ readResolve()163 private Object readResolve() { 164 return INSTANCE; 165 } 166 167 //----------------------------------------------------------------------- 168 /** 169 * Gets the ID of the chronology - 'Japanese'. 170 * <p> 171 * The ID uniquely identifies the {@code Chronology}. 172 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 173 * 174 * @return the chronology ID - 'Japanese' 175 * @see #getCalendarType() 176 */ 177 @Override getId()178 public String getId() { 179 return "Japanese"; 180 } 181 182 /** 183 * Gets the calendar type of the underlying calendar system - 'japanese'. 184 * <p> 185 * The calendar type is an identifier defined by the 186 * <em>Unicode Locale Data Markup Language (LDML)</em> specification. 187 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 188 * It can also be used as part of a locale, accessible via 189 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. 190 * 191 * @return the calendar system type - 'japanese' 192 * @see #getId() 193 */ 194 @Override getCalendarType()195 public String getCalendarType() { 196 return "japanese"; 197 } 198 199 //----------------------------------------------------------------------- 200 @Override // override with covariant return type date(Era era, int yearOfEra, int month, int dayOfMonth)201 public JapaneseDate date(Era era, int yearOfEra, int month, int dayOfMonth) { 202 if (era instanceof JapaneseEra == false) { 203 throw new ClassCastException("Era must be JapaneseEra"); 204 } 205 return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth); 206 } 207 208 @Override // override with covariant return type date(int prolepticYear, int month, int dayOfMonth)209 public JapaneseDate date(int prolepticYear, int month, int dayOfMonth) { 210 return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth)); 211 } 212 213 /** 214 * Obtains a local date in Japanese calendar system from the 215 * era, year-of-era and day-of-year fields. 216 * <p> 217 * The day-of-year in this factory is expressed relative to the start of the year-of-era. 218 * This definition changes the normal meaning of day-of-year only in those years 219 * where the year-of-era is reset to one due to a change in the era. 220 * For example: 221 * <pre> 222 * 6th Jan Showa 64 = day-of-year 6 223 * 7th Jan Showa 64 = day-of-year 7 224 * 8th Jan Heisei 1 = day-of-year 1 225 * 9th Jan Heisei 1 = day-of-year 2 226 * </pre> 227 * 228 * @param era the Japanese era, not null 229 * @param yearOfEra the year-of-era 230 * @param dayOfYear the day-of-year 231 * @return the Japanese local date, not null 232 * @throws DateTimeException if unable to create the date 233 * @throws ClassCastException if the {@code era} is not a {@code JapaneseEra} 234 */ 235 @Override dateYearDay(Era era, int yearOfEra, int dayOfYear)236 public JapaneseDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { 237 if (era instanceof JapaneseEra == false) { 238 throw new ClassCastException("Era must be JapaneseEra"); 239 } 240 return JapaneseDate.ofYearDay((JapaneseEra) era, yearOfEra, dayOfYear); 241 } 242 243 /** 244 * Obtains a local date in Japanese calendar system from the 245 * proleptic-year and day-of-year fields. 246 * <p> 247 * The day-of-year in this factory is expressed relative to the start of the proleptic year. 248 * The Japanese proleptic year and day-of-year are the same as those in the ISO calendar system. 249 * They are not reset when the era changes. 250 * 251 * @param prolepticYear the proleptic-year 252 * @param dayOfYear the day-of-year 253 * @return the Japanese local date, not null 254 * @throws DateTimeException if unable to create the date 255 */ 256 @Override dateYearDay(int prolepticYear, int dayOfYear)257 public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) { 258 LocalDate date = LocalDate.ofYearDay(prolepticYear, dayOfYear); 259 return date(prolepticYear, date.getMonthValue(), date.getDayOfMonth()); 260 } 261 262 @Override dateEpochDay(long epochDay)263 public JapaneseDate dateEpochDay(long epochDay) { 264 return new JapaneseDate(LocalDate.ofEpochDay(epochDay)); 265 } 266 267 //----------------------------------------------------------------------- 268 @Override // override with covariant return type date(TemporalAccessor temporal)269 public JapaneseDate date(TemporalAccessor temporal) { 270 if (temporal instanceof JapaneseDate) { 271 return (JapaneseDate) temporal; 272 } 273 return new JapaneseDate(LocalDate.from(temporal)); 274 } 275 276 @SuppressWarnings("unchecked") 277 @Override // override with covariant return type localDateTime(TemporalAccessor temporal)278 public ChronoLocalDateTime<JapaneseDate> localDateTime(TemporalAccessor temporal) { 279 return (ChronoLocalDateTime<JapaneseDate>) super.localDateTime(temporal); 280 } 281 282 @SuppressWarnings("unchecked") 283 @Override // override with covariant return type zonedDateTime(TemporalAccessor temporal)284 public ChronoZonedDateTime<JapaneseDate> zonedDateTime(TemporalAccessor temporal) { 285 return (ChronoZonedDateTime<JapaneseDate>) super.zonedDateTime(temporal); 286 } 287 288 @SuppressWarnings("unchecked") 289 @Override // override with covariant return type zonedDateTime(Instant instant, ZoneId zone)290 public ChronoZonedDateTime<JapaneseDate> zonedDateTime(Instant instant, ZoneId zone) { 291 return (ChronoZonedDateTime<JapaneseDate>) super.zonedDateTime(instant, zone); 292 } 293 294 //----------------------------------------------------------------------- 295 @Override // override with covariant return type dateNow()296 public JapaneseDate dateNow() { 297 return (JapaneseDate) super.dateNow(); 298 } 299 300 @Override // override with covariant return type dateNow(ZoneId zone)301 public JapaneseDate dateNow(ZoneId zone) { 302 return (JapaneseDate) super.dateNow(zone); 303 } 304 305 @Override // override with covariant return type dateNow(Clock clock)306 public JapaneseDate dateNow(Clock clock) { 307 Jdk8Methods.requireNonNull(clock, "clock"); 308 return (JapaneseDate) super.dateNow(clock); 309 } 310 311 //----------------------------------------------------------------------- 312 /** 313 * Checks if the specified year is a leap year. 314 * <p> 315 * Japanese calendar leap years occur exactly in line with ISO leap years. 316 * This method does not validate the year passed in, and only has a 317 * well-defined result for years in the supported range. 318 * 319 * @param prolepticYear the proleptic-year to check, not validated for range 320 * @return true if the year is a leap year 321 */ 322 @Override isLeapYear(long prolepticYear)323 public boolean isLeapYear(long prolepticYear) { 324 return IsoChronology.INSTANCE.isLeapYear(prolepticYear); 325 } 326 327 @Override prolepticYear(Era era, int yearOfEra)328 public int prolepticYear(Era era, int yearOfEra) { 329 if (era instanceof JapaneseEra == false) { 330 throw new ClassCastException("Era must be JapaneseEra"); 331 } 332 JapaneseEra jera = (JapaneseEra) era; 333 int isoYear = jera.startDate().getYear() + yearOfEra - 1; 334 ValueRange range = ValueRange.of(1, jera.endDate().getYear() - jera.startDate().getYear() + 1); 335 range.checkValidValue(yearOfEra, YEAR_OF_ERA); 336 return isoYear; 337 } 338 339 /** 340 * Returns the calendar system era object from the given numeric value. 341 * 342 * See the description of each Era for the numeric values of: 343 * {@link JapaneseEra#HEISEI}, {@link JapaneseEra#SHOWA},{@link JapaneseEra#TAISHO}, 344 * {@link JapaneseEra#MEIJI}), only Meiji and later eras are supported. 345 * 346 * @param eraValue the era value 347 * @return the Japanese {@code Era} for the given numeric era value 348 * @throws DateTimeException if {@code eraValue} is invalid 349 */ 350 @Override eraOf(int eraValue)351 public JapaneseEra eraOf(int eraValue) { 352 return JapaneseEra.of(eraValue); 353 } 354 355 @Override eras()356 public List<Era> eras() { 357 return Arrays.<Era>asList(JapaneseEra.values()); 358 } 359 360 //----------------------------------------------------------------------- 361 @Override range(ChronoField field)362 public ValueRange range(ChronoField field) { 363 switch (field) { 364 case DAY_OF_MONTH: 365 case DAY_OF_WEEK: 366 case MICRO_OF_DAY: 367 case MICRO_OF_SECOND: 368 case HOUR_OF_DAY: 369 case HOUR_OF_AMPM: 370 case MINUTE_OF_DAY: 371 case MINUTE_OF_HOUR: 372 case SECOND_OF_DAY: 373 case SECOND_OF_MINUTE: 374 case MILLI_OF_DAY: 375 case MILLI_OF_SECOND: 376 case NANO_OF_DAY: 377 case NANO_OF_SECOND: 378 case CLOCK_HOUR_OF_DAY: 379 case CLOCK_HOUR_OF_AMPM: 380 case EPOCH_DAY: 381 case PROLEPTIC_MONTH: 382 return field.range(); 383 } 384 Calendar jcal = Calendar.getInstance(LOCALE); 385 switch (field) { 386 case ERA: { 387 JapaneseEra[] eras = JapaneseEra.values(); 388 return ValueRange.of(eras[0].getValue(), eras[eras.length - 1].getValue()); 389 } 390 case YEAR: { 391 JapaneseEra[] eras = JapaneseEra.values(); 392 return ValueRange.of(JapaneseDate.MIN_DATE.getYear(), eras[eras.length - 1].endDate().getYear()); 393 } 394 case YEAR_OF_ERA: { 395 JapaneseEra[] eras = JapaneseEra.values(); 396 int maxIso = eras[eras.length - 1].endDate().getYear(); 397 int maxJapanese = maxIso - eras[eras.length - 1].startDate().getYear() + 1; 398 int min = Integer.MAX_VALUE; 399 for (int i = 0; i < eras.length; i++) { 400 min = Math.min(min, eras[i].endDate().getYear() - eras[i].startDate().getYear() + 1); 401 } 402 return ValueRange.of(1, 6, min, maxJapanese); 403 } 404 case MONTH_OF_YEAR: 405 return ValueRange.of(jcal.getMinimum(Calendar.MONTH) + 1, jcal.getGreatestMinimum(Calendar.MONTH) + 1, 406 jcal.getLeastMaximum(Calendar.MONTH) + 1, jcal.getMaximum(Calendar.MONTH) + 1); 407 case DAY_OF_YEAR: { 408 JapaneseEra[] eras = JapaneseEra.values(); 409 int min = 366; 410 for (int i = 0; i < eras.length; i++) { 411 min = Math.min(min, eras[i].startDate().lengthOfYear() - eras[i].startDate().getDayOfYear() + 1); 412 } 413 return ValueRange.of(1, min, 366); 414 } 415 default: 416 // TODO: review the remaining fields 417 throw new UnsupportedOperationException("Unimplementable field: " + field); 418 } 419 } 420 421 @Override resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle)422 public JapaneseDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { 423 if (fieldValues.containsKey(EPOCH_DAY)) { 424 return dateEpochDay(fieldValues.remove(EPOCH_DAY)); 425 } 426 427 // normalize fields 428 Long prolepticMonth = fieldValues.remove(PROLEPTIC_MONTH); 429 if (prolepticMonth != null) { 430 if (resolverStyle != ResolverStyle.LENIENT) { 431 PROLEPTIC_MONTH.checkValidValue(prolepticMonth); 432 } 433 updateResolveMap(fieldValues, MONTH_OF_YEAR, Jdk8Methods.floorMod(prolepticMonth, 12) + 1); 434 updateResolveMap(fieldValues, YEAR, Jdk8Methods.floorDiv(prolepticMonth, 12)); 435 } 436 437 // eras 438 Long eraLong = fieldValues.get(ERA); 439 JapaneseEra era = null; 440 if (eraLong != null) { 441 era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); 442 } 443 Long yoeLong = fieldValues.get(YEAR_OF_ERA); 444 if (yoeLong != null) { 445 int yoe= range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); 446 if (era == null && resolverStyle != ResolverStyle.STRICT && fieldValues.containsKey(YEAR) == false) { 447 List<Era> eras = eras(); 448 era = (JapaneseEra) eras.get(eras.size() - 1); 449 } 450 // can only resolve to dates, not to proleptic-year 451 if (era != null && fieldValues.containsKey(MONTH_OF_YEAR) && fieldValues.containsKey(DAY_OF_MONTH)) { 452 fieldValues.remove(ERA); 453 fieldValues.remove(YEAR_OF_ERA); 454 return resolveEYMD(fieldValues, resolverStyle, era, yoe); 455 } 456 if (era != null && fieldValues.containsKey(DAY_OF_YEAR)) { 457 fieldValues.remove(ERA); 458 fieldValues.remove(YEAR_OF_ERA); 459 return resolveEYD(fieldValues, resolverStyle, era, yoe); 460 } 461 } 462 463 // build date 464 if (fieldValues.containsKey(YEAR)) { 465 if (fieldValues.containsKey(MONTH_OF_YEAR)) { 466 if (fieldValues.containsKey(DAY_OF_MONTH)) { 467 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 468 if (resolverStyle == ResolverStyle.LENIENT) { 469 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 470 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_MONTH), 1); 471 return date(y, 1, 1).plusMonths(months).plusDays(days); 472 } else { 473 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR); 474 int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH); 475 if (resolverStyle == ResolverStyle.SMART && dom > 28) { 476 dom = Math.min(dom, date(y, moy, 1).lengthOfMonth()); 477 } 478 return date(y, moy, dom); 479 } 480 } 481 if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) { 482 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) { 483 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 484 if (resolverStyle == ResolverStyle.LENIENT) { 485 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 486 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); 487 long days = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1); 488 return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS); 489 } 490 int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR)); 491 int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH)); 492 int ad = ALIGNED_DAY_OF_WEEK_IN_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH)); 493 JapaneseDate date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS); 494 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { 495 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 496 } 497 return date; 498 } 499 if (fieldValues.containsKey(DAY_OF_WEEK)) { 500 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 501 if (resolverStyle == ResolverStyle.LENIENT) { 502 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 503 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); 504 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_WEEK), 1); 505 return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS); 506 } 507 int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR)); 508 int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH)); 509 int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK)); 510 JapaneseDate date = date(y, moy, 1).plus(aw - 1, WEEKS).with(nextOrSame(DayOfWeek.of(dow))); 511 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { 512 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 513 } 514 return date; 515 } 516 } 517 } 518 if (fieldValues.containsKey(DAY_OF_YEAR)) { 519 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 520 if (resolverStyle == ResolverStyle.LENIENT) { 521 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_YEAR), 1); 522 return dateYearDay(y, 1).plusDays(days); 523 } 524 int doy = DAY_OF_YEAR.checkValidIntValue(fieldValues.remove(DAY_OF_YEAR)); 525 return dateYearDay(y, doy); 526 } 527 if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) { 528 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) { 529 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 530 if (resolverStyle == ResolverStyle.LENIENT) { 531 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); 532 long days = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1); 533 return date(y, 1, 1).plus(weeks, WEEKS).plus(days, DAYS); 534 } 535 int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR)); 536 int ad = ALIGNED_DAY_OF_WEEK_IN_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR)); 537 JapaneseDate date = date(y, 1, 1).plusDays((aw - 1) * 7 + (ad - 1)); 538 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { 539 throw new DateTimeException("Strict mode rejected date parsed to a different year"); 540 } 541 return date; 542 } 543 if (fieldValues.containsKey(DAY_OF_WEEK)) { 544 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 545 if (resolverStyle == ResolverStyle.LENIENT) { 546 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); 547 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_WEEK), 1); 548 return date(y, 1, 1).plus(weeks, WEEKS).plus(days, DAYS); 549 } 550 int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR)); 551 int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK)); 552 JapaneseDate date = date(y, 1, 1).plus(aw - 1, WEEKS).with(nextOrSame(DayOfWeek.of(dow))); 553 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { 554 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 555 } 556 return date; 557 } 558 } 559 } 560 return null; 561 } 562 resolveEYMD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle, JapaneseEra era, int yoe)563 private JapaneseDate resolveEYMD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle, JapaneseEra era, int yoe) { 564 if (resolverStyle == ResolverStyle.LENIENT) { 565 int y = era.startDate().getYear() + yoe - 1; 566 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 567 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_MONTH), 1); 568 return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS); 569 } 570 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR); 571 int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH); 572 if (resolverStyle == ResolverStyle.SMART) { // previous valid 573 if (yoe < 1) { 574 throw new DateTimeException("Invalid YearOfEra: " + yoe); 575 } 576 int y = era.startDate().getYear() + yoe - 1; 577 if (dom > 28) { 578 dom = Math.min(dom, date(y, moy, 1).lengthOfMonth()); 579 } 580 JapaneseDate jd = date(y, moy, dom); 581 if (jd.getEra() != era) { 582 // ensure within calendar year of change 583 if (Math.abs(jd.getEra().getValue() - era.getValue()) > 1) { 584 throw new DateTimeException("Invalid Era/YearOfEra: " + era + " " + yoe); 585 } 586 if (jd.get(YEAR_OF_ERA) != 1 && yoe != 1) { 587 throw new DateTimeException("Invalid Era/YearOfEra: " + era + " " + yoe); 588 } 589 } 590 return jd; 591 } 592 return date(era, yoe, moy, dom); 593 } 594 resolveEYD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle, JapaneseEra era, int yoe)595 private JapaneseDate resolveEYD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle, JapaneseEra era, int yoe) { 596 if (resolverStyle == ResolverStyle.LENIENT) { 597 int y = era.startDate().getYear() + yoe - 1; 598 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_YEAR), 1); 599 return dateYearDay(y, 1).plus(days, DAYS); 600 } 601 int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR); 602 return dateYearDay(era, yoe, doy); // smart is same as strict 603 } 604 605 } 606