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.TemporalAdjusters.nextOrSame; 48 49 import java.io.Serializable; 50 import java.util.Arrays; 51 import java.util.List; 52 import java.util.Locale; 53 import java.util.Map; 54 55 import org.threeten.bp.Clock; 56 import org.threeten.bp.DateTimeException; 57 import org.threeten.bp.DayOfWeek; 58 import org.threeten.bp.Instant; 59 import org.threeten.bp.LocalDate; 60 import org.threeten.bp.LocalDateTime; 61 import org.threeten.bp.Month; 62 import org.threeten.bp.Year; 63 import org.threeten.bp.ZoneId; 64 import org.threeten.bp.ZonedDateTime; 65 import org.threeten.bp.format.ResolverStyle; 66 import org.threeten.bp.jdk8.Jdk8Methods; 67 import org.threeten.bp.temporal.ChronoField; 68 import org.threeten.bp.temporal.TemporalAccessor; 69 import org.threeten.bp.temporal.TemporalField; 70 import org.threeten.bp.temporal.ValueRange; 71 72 /** 73 * The ISO calendar system. 74 * <p> 75 * This chronology defines the rules of the ISO calendar system. 76 * This calendar system is based on the ISO-8601 standard, which is the 77 * <i>de facto</i> world calendar. 78 * <p> 79 * The fields are defined as follows: 80 * <p><ul> 81 * <li>era - There are two eras, 'Current Era' (CE) and 'Before Current Era' (BCE). 82 * <li>year-of-era - The year-of-era is the same as the proleptic-year for the current CE era. 83 * For the BCE era before the ISO epoch the year increases from 1 upwards as time goes backwards. 84 * <li>proleptic-year - The proleptic year is the same as the year-of-era for the 85 * current era. For the previous era, years have zero, then negative values. 86 * <li>month-of-year - There are 12 months in an ISO year, numbered from 1 to 12. 87 * <li>day-of-month - There are between 28 and 31 days in each of the ISO month, numbered from 1 to 31. 88 * Months 4, 6, 9 and 11 have 30 days, Months 1, 3, 5, 7, 8, 10 and 12 have 31 days. 89 * Month 2 has 28 days, or 29 in a leap year. 90 * <li>day-of-year - There are 365 days in a standard ISO year and 366 in a leap year. 91 * The days are numbered from 1 to 365 or 1 to 366. 92 * <li>leap-year - Leap years occur every 4 years, except where the year is divisble by 100 and not divisble by 400. 93 * </ul><p> 94 * 95 * <h3>Specification for implementors</h3> 96 * This class is immutable and thread-safe. 97 */ 98 public final class IsoChronology extends Chronology implements Serializable { 99 100 /** 101 * Singleton instance of the ISO chronology. 102 */ 103 public static final IsoChronology INSTANCE = new IsoChronology(); 104 105 /** 106 * Serialization version. 107 */ 108 private static final long serialVersionUID = -1440403870442975015L; 109 110 /** 111 * Restricted constructor. 112 */ IsoChronology()113 private IsoChronology() { 114 } 115 116 /** 117 * Resolve singleton. 118 * 119 * @return the singleton instance, not null 120 */ readResolve()121 private Object readResolve() { 122 return INSTANCE; 123 } 124 125 //----------------------------------------------------------------------- 126 /** 127 * Gets the ID of the chronology - 'ISO'. 128 * <p> 129 * The ID uniquely identifies the {@code Chronology}. 130 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 131 * 132 * @return the chronology ID - 'ISO' 133 * @see #getCalendarType() 134 */ 135 @Override getId()136 public String getId() { 137 return "ISO"; 138 } 139 140 /** 141 * Gets the calendar type of the underlying calendar system - 'iso8601'. 142 * <p> 143 * The calendar type is an identifier defined by the 144 * <em>Unicode Locale Data Markup Language (LDML)</em> specification. 145 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 146 * It can also be used as part of a locale, accessible via 147 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. 148 * 149 * @return the calendar system type - 'iso8601' 150 * @see #getId() 151 */ 152 @Override getCalendarType()153 public String getCalendarType() { 154 return "iso8601"; 155 } 156 157 //----------------------------------------------------------------------- 158 /** 159 * Obtains an ISO local date from the era, year-of-era, month-of-year 160 * and day-of-month fields. 161 * 162 * @param era the ISO era, not null 163 * @param yearOfEra the ISO year-of-era 164 * @param month the ISO month-of-year 165 * @param dayOfMonth the ISO day-of-month 166 * @return the ISO local date, not null 167 * @throws DateTimeException if unable to create the date 168 */ 169 @Override // override with covariant return type date(Era era, int yearOfEra, int month, int dayOfMonth)170 public LocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) { 171 return date(prolepticYear(era, yearOfEra), month, dayOfMonth); 172 } 173 174 /** 175 * Obtains an ISO local date from the proleptic-year, month-of-year 176 * and day-of-month fields. 177 * <p> 178 * This is equivalent to {@link LocalDate#of(int, int, int)}. 179 * 180 * @param prolepticYear the ISO proleptic-year 181 * @param month the ISO month-of-year 182 * @param dayOfMonth the ISO day-of-month 183 * @return the ISO local date, not null 184 * @throws DateTimeException if unable to create the date 185 */ 186 @Override // override with covariant return type date(int prolepticYear, int month, int dayOfMonth)187 public LocalDate date(int prolepticYear, int month, int dayOfMonth) { 188 return LocalDate.of(prolepticYear, month, dayOfMonth); 189 } 190 191 /** 192 * Obtains an ISO local date from the era, year-of-era and day-of-year fields. 193 * 194 * @param era the ISO era, not null 195 * @param yearOfEra the ISO year-of-era 196 * @param dayOfYear the ISO day-of-year 197 * @return the ISO local date, not null 198 * @throws DateTimeException if unable to create the date 199 */ 200 @Override // override with covariant return type dateYearDay(Era era, int yearOfEra, int dayOfYear)201 public LocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { 202 return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); 203 } 204 205 /** 206 * Obtains an ISO local date from the proleptic-year and day-of-year fields. 207 * <p> 208 * This is equivalent to {@link LocalDate#ofYearDay(int, int)}. 209 * 210 * @param prolepticYear the ISO proleptic-year 211 * @param dayOfYear the ISO day-of-year 212 * @return the ISO local date, not null 213 * @throws DateTimeException if unable to create the date 214 */ 215 @Override // override with covariant return type dateYearDay(int prolepticYear, int dayOfYear)216 public LocalDate dateYearDay(int prolepticYear, int dayOfYear) { 217 return LocalDate.ofYearDay(prolepticYear, dayOfYear); 218 } 219 220 @Override dateEpochDay(long epochDay)221 public LocalDate dateEpochDay(long epochDay) { 222 return LocalDate.ofEpochDay(epochDay); 223 } 224 225 //----------------------------------------------------------------------- 226 /** 227 * Obtains an ISO local date from another date-time object. 228 * <p> 229 * This is equivalent to {@link LocalDate#from(TemporalAccessor)}. 230 * 231 * @param temporal the date-time object to convert, not null 232 * @return the ISO local date, not null 233 * @throws DateTimeException if unable to create the date 234 */ 235 @Override // override with covariant return type date(TemporalAccessor temporal)236 public LocalDate date(TemporalAccessor temporal) { 237 return LocalDate.from(temporal); 238 } 239 240 /** 241 * Obtains an ISO local date-time from another date-time object. 242 * <p> 243 * This is equivalent to {@link LocalDateTime#from(TemporalAccessor)}. 244 * 245 * @param temporal the date-time object to convert, not null 246 * @return the ISO local date-time, not null 247 * @throws DateTimeException if unable to create the date-time 248 */ 249 @Override // override with covariant return type localDateTime(TemporalAccessor temporal)250 public LocalDateTime localDateTime(TemporalAccessor temporal) { 251 return LocalDateTime.from(temporal); 252 } 253 254 /** 255 * Obtains an ISO zoned date-time from another date-time object. 256 * <p> 257 * This is equivalent to {@link ZonedDateTime#from(TemporalAccessor)}. 258 * 259 * @param temporal the date-time object to convert, not null 260 * @return the ISO zoned date-time, not null 261 * @throws DateTimeException if unable to create the date-time 262 */ 263 @Override // override with covariant return type zonedDateTime(TemporalAccessor temporal)264 public ZonedDateTime zonedDateTime(TemporalAccessor temporal) { 265 return ZonedDateTime.from(temporal); 266 } 267 268 /** 269 * Obtains an ISO zoned date-time from an instant. 270 * <p> 271 * This is equivalent to {@link ZonedDateTime#ofInstant(Instant, ZoneId)}. 272 * 273 * @param instant the instant to convert, not null 274 * @param zone the zone to use, not null 275 * @return the ISO zoned date-time, not null 276 * @throws DateTimeException if unable to create the date-time 277 */ 278 @Override // override with covariant return type zonedDateTime(Instant instant, ZoneId zone)279 public ZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { 280 return ZonedDateTime.ofInstant(instant, zone); 281 } 282 283 //----------------------------------------------------------------------- 284 /** 285 * Obtains the current ISO local date from the system clock in the default time-zone. 286 * <p> 287 * This will query the {@link Clock#systemDefaultZone() system clock} in the default 288 * time-zone to obtain the current date. 289 * <p> 290 * Using this method will prevent the ability to use an alternate clock for testing 291 * because the clock is hard-coded. 292 * 293 * @return the current ISO local date using the system clock and default time-zone, not null 294 * @throws DateTimeException if unable to create the date 295 */ 296 @Override // override with covariant return type dateNow()297 public LocalDate dateNow() { 298 return dateNow(Clock.systemDefaultZone()); 299 } 300 301 /** 302 * Obtains the current ISO local date from the system clock in the specified time-zone. 303 * <p> 304 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. 305 * Specifying the time-zone avoids dependence on the default time-zone. 306 * <p> 307 * Using this method will prevent the ability to use an alternate clock for testing 308 * because the clock is hard-coded. 309 * 310 * @return the current ISO local date using the system clock, not null 311 * @throws DateTimeException if unable to create the date 312 */ 313 @Override // override with covariant return type dateNow(ZoneId zone)314 public LocalDate dateNow(ZoneId zone) { 315 return dateNow(Clock.system(zone)); 316 } 317 318 /** 319 * Obtains the current ISO local date from the specified clock. 320 * <p> 321 * This will query the specified clock to obtain the current date - today. 322 * Using this method allows the use of an alternate clock for testing. 323 * The alternate clock may be introduced using {@link Clock dependency injection}. 324 * 325 * @param clock the clock to use, not null 326 * @return the current ISO local date, not null 327 * @throws DateTimeException if unable to create the date 328 */ 329 @Override // override with covariant return type dateNow(Clock clock)330 public LocalDate dateNow(Clock clock) { 331 Jdk8Methods.requireNonNull(clock, "clock"); 332 return date(LocalDate.now(clock)); 333 } 334 335 //----------------------------------------------------------------------- 336 /** 337 * Checks if the year is a leap year, according to the ISO proleptic 338 * calendar system rules. 339 * <p> 340 * This method applies the current rules for leap years across the whole time-line. 341 * In general, a year is a leap year if it is divisible by four without 342 * remainder. However, years divisible by 100, are not leap years, with 343 * the exception of years divisible by 400 which are. 344 * <p> 345 * For example, 1904 is a leap year it is divisible by 4. 346 * 1900 was not a leap year as it is divisible by 100, however 2000 was a 347 * leap year as it is divisible by 400. 348 * <p> 349 * The calculation is proleptic - applying the same rules into the far future and far past. 350 * This is historically inaccurate, but is correct for the ISO-8601 standard. 351 * 352 * @param prolepticYear the ISO proleptic year to check 353 * @return true if the year is leap, false otherwise 354 */ 355 @Override isLeapYear(long prolepticYear)356 public boolean isLeapYear(long prolepticYear) { 357 return ((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0); 358 } 359 360 @Override prolepticYear(Era era, int yearOfEra)361 public int prolepticYear(Era era, int yearOfEra) { 362 if (era instanceof IsoEra == false) { 363 throw new ClassCastException("Era must be IsoEra"); 364 } 365 return (era == IsoEra.CE ? yearOfEra : 1 - yearOfEra); 366 } 367 368 @Override eraOf(int eraValue)369 public IsoEra eraOf(int eraValue) { 370 return IsoEra.of(eraValue); 371 } 372 373 @Override eras()374 public List<Era> eras() { 375 return Arrays.<Era>asList(IsoEra.values()); 376 } 377 378 //----------------------------------------------------------------------- 379 @Override range(ChronoField field)380 public ValueRange range(ChronoField field) { 381 return field.range(); 382 } 383 384 @Override resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle)385 public LocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { 386 if (fieldValues.containsKey(EPOCH_DAY)) { 387 return LocalDate.ofEpochDay(fieldValues.remove(EPOCH_DAY)); 388 } 389 390 // normalize fields 391 Long prolepticMonth = fieldValues.remove(PROLEPTIC_MONTH); 392 if (prolepticMonth != null) { 393 if (resolverStyle != ResolverStyle.LENIENT) { 394 PROLEPTIC_MONTH.checkValidValue(prolepticMonth); 395 } 396 updateResolveMap(fieldValues, MONTH_OF_YEAR, Jdk8Methods.floorMod(prolepticMonth, 12) + 1); 397 updateResolveMap(fieldValues, YEAR, Jdk8Methods.floorDiv(prolepticMonth, 12)); 398 } 399 400 // eras 401 Long yoeLong = fieldValues.remove(YEAR_OF_ERA); 402 if (yoeLong != null) { 403 if (resolverStyle != ResolverStyle.LENIENT) { 404 YEAR_OF_ERA.checkValidValue(yoeLong); 405 } 406 Long era = fieldValues.remove(ERA); 407 if (era == null) { 408 Long year = fieldValues.get(YEAR); 409 if (resolverStyle == ResolverStyle.STRICT) { 410 // do not invent era if strict, but do cross-check with year 411 if (year != null) { 412 updateResolveMap(fieldValues, YEAR, (year > 0 ? yoeLong: Jdk8Methods.safeSubtract(1, yoeLong))); 413 } else { 414 // reinstate the field removed earlier, no cross-check issues 415 fieldValues.put(YEAR_OF_ERA, yoeLong); 416 } 417 } else { 418 // invent era 419 updateResolveMap(fieldValues, YEAR, (year == null || year > 0 ? yoeLong: Jdk8Methods.safeSubtract(1, yoeLong))); 420 } 421 } else if (era.longValue() == 1L) { 422 updateResolveMap(fieldValues, YEAR, yoeLong); 423 } else if (era.longValue() == 0L) { 424 updateResolveMap(fieldValues, YEAR, Jdk8Methods.safeSubtract(1, yoeLong)); 425 } else { 426 throw new DateTimeException("Invalid value for era: " + era); 427 } 428 } else if (fieldValues.containsKey(ERA)) { 429 ERA.checkValidValue(fieldValues.get(ERA)); // always validated 430 } 431 432 // build date 433 if (fieldValues.containsKey(YEAR)) { 434 if (fieldValues.containsKey(MONTH_OF_YEAR)) { 435 if (fieldValues.containsKey(DAY_OF_MONTH)) { 436 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 437 int moy = Jdk8Methods.safeToInt(fieldValues.remove(MONTH_OF_YEAR)); 438 int dom = Jdk8Methods.safeToInt(fieldValues.remove(DAY_OF_MONTH)); 439 if (resolverStyle == ResolverStyle.LENIENT) { 440 long months = Jdk8Methods.safeSubtract(moy, 1); 441 long days = Jdk8Methods.safeSubtract(dom, 1); 442 return LocalDate.of(y, 1, 1).plusMonths(months).plusDays(days); 443 } else if (resolverStyle == ResolverStyle.SMART){ 444 DAY_OF_MONTH.checkValidValue(dom); 445 if (moy == 4 || moy == 6 || moy == 9 || moy == 11) { 446 dom = Math.min(dom, 30); 447 } else if (moy == 2) { 448 dom = Math.min(dom, Month.FEBRUARY.length(Year.isLeap(y))); 449 } 450 return LocalDate.of(y, moy, dom); 451 } else { 452 return LocalDate.of(y, moy, dom); 453 } 454 } 455 if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) { 456 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) { 457 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 458 if (resolverStyle == ResolverStyle.LENIENT) { 459 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 460 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); 461 long days = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1); 462 return LocalDate.of(y, 1, 1).plusMonths(months).plusWeeks(weeks).plusDays(days); 463 } 464 int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR)); 465 int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH)); 466 int ad = ALIGNED_DAY_OF_WEEK_IN_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH)); 467 LocalDate date = LocalDate.of(y, moy, 1).plusDays((aw - 1) * 7 + (ad - 1)); 468 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { 469 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 470 } 471 return date; 472 } 473 if (fieldValues.containsKey(DAY_OF_WEEK)) { 474 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 475 if (resolverStyle == ResolverStyle.LENIENT) { 476 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 477 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); 478 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_WEEK), 1); 479 return LocalDate.of(y, 1, 1).plusMonths(months).plusWeeks(weeks).plusDays(days); 480 } 481 int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR)); 482 int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH)); 483 int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK)); 484 LocalDate date = LocalDate.of(y, moy, 1).plusWeeks(aw - 1).with(nextOrSame(DayOfWeek.of(dow))); 485 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { 486 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 487 } 488 return date; 489 } 490 } 491 } 492 if (fieldValues.containsKey(DAY_OF_YEAR)) { 493 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 494 if (resolverStyle == ResolverStyle.LENIENT) { 495 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_YEAR), 1); 496 return LocalDate.ofYearDay(y, 1).plusDays(days); 497 } 498 int doy = DAY_OF_YEAR.checkValidIntValue(fieldValues.remove(DAY_OF_YEAR)); 499 return LocalDate.ofYearDay(y, doy); 500 } 501 if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) { 502 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) { 503 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 504 if (resolverStyle == ResolverStyle.LENIENT) { 505 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); 506 long days = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1); 507 return LocalDate.of(y, 1, 1).plusWeeks(weeks).plusDays(days); 508 } 509 int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR)); 510 int ad = ALIGNED_DAY_OF_WEEK_IN_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR)); 511 LocalDate date = LocalDate.of(y, 1, 1).plusDays((aw - 1) * 7 + (ad - 1)); 512 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { 513 throw new DateTimeException("Strict mode rejected date parsed to a different year"); 514 } 515 return date; 516 } 517 if (fieldValues.containsKey(DAY_OF_WEEK)) { 518 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 519 if (resolverStyle == ResolverStyle.LENIENT) { 520 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); 521 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_WEEK), 1); 522 return LocalDate.of(y, 1, 1).plusWeeks(weeks).plusDays(days); 523 } 524 int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR)); 525 int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK)); 526 LocalDate date = LocalDate.of(y, 1, 1).plusWeeks(aw - 1).with(nextOrSame(DayOfWeek.of(dow))); 527 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { 528 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 529 } 530 return date; 531 } 532 } 533 } 534 return null; 535 } 536 537 } 538