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 33 package org.threeten.bp.chrono; 34 35 import static org.threeten.bp.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; 36 import static org.threeten.bp.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; 37 import static org.threeten.bp.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; 38 import static org.threeten.bp.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; 39 import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH; 40 import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK; 41 import static org.threeten.bp.temporal.ChronoField.DAY_OF_YEAR; 42 import static org.threeten.bp.temporal.ChronoField.EPOCH_DAY; 43 import static org.threeten.bp.temporal.ChronoField.ERA; 44 import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR; 45 import static org.threeten.bp.temporal.ChronoField.PROLEPTIC_MONTH; 46 import static org.threeten.bp.temporal.ChronoField.YEAR; 47 import static org.threeten.bp.temporal.ChronoField.YEAR_OF_ERA; 48 import static org.threeten.bp.temporal.ChronoUnit.DAYS; 49 import static org.threeten.bp.temporal.ChronoUnit.MONTHS; 50 import static org.threeten.bp.temporal.ChronoUnit.WEEKS; 51 import static org.threeten.bp.temporal.TemporalAdjusters.nextOrSame; 52 53 import java.io.Serializable; 54 import java.util.Arrays; 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 Hijrah calendar system. 75 * <p> 76 * This chronology defines the rules of the Hijrah calendar system. 77 * <p> 78 * The implementation follows the Freeman-Grenville algorithm (*1) and has following features. 79 * <p><ul> 80 * <li>A year has 12 months.</li> 81 * <li>Over a cycle of 30 years there are 11 leap years.</li> 82 * <li>There are 30 days in month number 1, 3, 5, 7, 9, and 11, 83 * and 29 days in month number 2, 4, 6, 8, 10, and 12.</li> 84 * <li>In a leap year month 12 has 30 days.</li> 85 * <li>In a 30 year cycle, year 2, 5, 7, 10, 13, 16, 18, 21, 24, 86 * 26, and 29 are leap years.</li> 87 * <li>Total of 10631 days in a 30 years cycle.</li> 88 * </ul><p> 89 * <P> 90 * The table shows the features described above. 91 * <blockquote> 92 * <table border="1"> 93 * <tbody> 94 * <tr> 95 * <th># of month</th> 96 * <th>Name of month</th> 97 * <th>Number of days</th> 98 * </tr> 99 * <tr> 100 * <td>1</td> 101 * <td>Muharram</td> 102 * <td>30</td> 103 * </tr> 104 * <tr> 105 * <td>2</td> 106 * <td>Safar</td> 107 * <td>29</td> 108 * </tr> 109 * <tr> 110 * <td>3</td> 111 * <td>Rabi'al-Awwal</td> 112 * <td>30</td> 113 * </tr> 114 * <tr> 115 * <td>4</td> 116 * <td>Rabi'ath-Thani</td> 117 * <td>29</td> 118 * </tr> 119 * <tr> 120 * <td>5</td> 121 * <td>Jumada l-Ula</td> 122 * <td>30</td> 123 * </tr> 124 * <tr> 125 * <td>6</td> 126 * <td>Jumada t-Tania</td> 127 * <td>29</td> 128 * </tr> 129 * <tr> 130 * <td>7</td> 131 * <td>Rajab</td> 132 * <td>30</td> 133 * </tr> 134 * <tr> 135 * <td>8</td> 136 * <td>Sha`ban</td> 137 * <td>29</td> 138 * </tr> 139 * <tr> 140 * <td>9</td> 141 * <td>Ramadan</td> 142 * <td>30</td> 143 * </tr> 144 * <tr> 145 * <td>10</td> 146 * <td>Shawwal</td> 147 * <td>29</td> 148 * </tr> 149 * <tr> 150 * <td>11</td> 151 * <td>Dhu 'l-Qa`da</td> 152 * <td>30</td> 153 * </tr> 154 * <tr> 155 * <td>12</td> 156 * <td>Dhu 'l-Hijja</td> 157 * <td>29, but 30 days in years 2, 5, 7, 10,<br> 158 * 13, 16, 18, 21, 24, 26, and 29</td> 159 * </tr> 160 * </tbody> 161 * </table> 162 * </blockquote> 163 * <p> 164 * (*1) The algorithm is taken from the book, 165 * The Muslim and Christian Calendars by G.S.P. Freeman-Grenville. 166 * <p> 167 * 168 * <h3>Specification for implementors</h3> 169 * This class is immutable and thread-safe. 170 */ 171 public final class HijrahChronology extends Chronology implements Serializable { 172 173 /** 174 * Singleton instance of the Hijrah chronology. 175 */ 176 public static final HijrahChronology INSTANCE = new HijrahChronology(); 177 178 /** 179 * Serialization version. 180 */ 181 private static final long serialVersionUID = 3127340209035924785L; 182 /** 183 * Narrow names for eras. 184 */ 185 private static final HashMap<String, String[]> ERA_NARROW_NAMES = new HashMap<String, String[]>(); 186 /** 187 * Short names for eras. 188 */ 189 private static final HashMap<String, String[]> ERA_SHORT_NAMES = new HashMap<String, String[]>(); 190 /** 191 * Full names for eras. 192 */ 193 private static final HashMap<String, String[]> ERA_FULL_NAMES = new HashMap<String, String[]>(); 194 /** 195 * Fallback language for the era names. 196 */ 197 private static final String FALLBACK_LANGUAGE = "en"; 198 199 /** 200 * Language that has the era names. 201 */ 202 //private static final String TARGET_LANGUAGE = "ar"; 203 /** 204 * Name data. 205 */ 206 static { ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"BH", "HE"})207 ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"BH", "HE"}); ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"B.H.", "H.E."})208 ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"B.H.", "H.E."}); ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Before Hijrah", "Hijrah Era"})209 ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Before Hijrah", "Hijrah Era"}); 210 } 211 212 /** 213 * Restrictive constructor. 214 */ HijrahChronology()215 private HijrahChronology() { 216 } 217 218 /** 219 * Resolve singleton. 220 * 221 * @return the singleton instance, not null 222 */ readResolve()223 private Object readResolve() { 224 return INSTANCE; 225 } 226 227 //----------------------------------------------------------------------- 228 /** 229 * Gets the ID of the chronology - 'Hijrah-umalqura'. 230 * <p> 231 * The ID uniquely identifies the {@code Chronology}. 232 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 233 * 234 * @return the chronology ID - 'Hijrah-umalqura' 235 * @see #getCalendarType() 236 */ 237 @Override getId()238 public String getId() { 239 return "Hijrah-umalqura"; 240 } 241 242 /** 243 * Gets the calendar type of the underlying calendar system - 'islamic-umalqura'. 244 * <p> 245 * The calendar type is an identifier defined by the 246 * <em>Unicode Locale Data Markup Language (LDML)</em> specification. 247 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 248 * It can also be used as part of a locale, accessible via 249 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. 250 * 251 * @return the calendar system type - 'islamic-umalqura' 252 * @see #getId() 253 */ 254 @Override getCalendarType()255 public String getCalendarType() { 256 return "islamic-umalqura"; 257 } 258 259 //----------------------------------------------------------------------- 260 @Override // override with covariant return type date(Era era, int yearOfEra, int month, int dayOfMonth)261 public HijrahDate date(Era era, int yearOfEra, int month, int dayOfMonth) { 262 return (HijrahDate) super.date(era, yearOfEra, month, dayOfMonth); 263 } 264 265 @Override // override with covariant return type date(int prolepticYear, int month, int dayOfMonth)266 public HijrahDate date(int prolepticYear, int month, int dayOfMonth) { 267 return HijrahDate.of(prolepticYear, month, dayOfMonth); 268 } 269 270 @Override // override with covariant return type dateYearDay(Era era, int yearOfEra, int dayOfYear)271 public HijrahDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { 272 return (HijrahDate) super.dateYearDay(era, yearOfEra, dayOfYear); 273 } 274 275 @Override // override with covariant return type dateYearDay(int prolepticYear, int dayOfYear)276 public HijrahDate dateYearDay(int prolepticYear, int dayOfYear) { 277 return HijrahDate.of(prolepticYear, 1, 1).plusDays(dayOfYear - 1); // TODO better 278 } 279 280 @Override dateEpochDay(long epochDay)281 public HijrahDate dateEpochDay(long epochDay) { 282 return HijrahDate.of(LocalDate.ofEpochDay(epochDay)); 283 } 284 285 //----------------------------------------------------------------------- 286 @Override // override with covariant return type date(TemporalAccessor temporal)287 public HijrahDate date(TemporalAccessor temporal) { 288 if (temporal instanceof HijrahDate) { 289 return (HijrahDate) temporal; 290 } 291 return HijrahDate.ofEpochDay(temporal.getLong(EPOCH_DAY)); 292 } 293 294 @SuppressWarnings("unchecked") 295 @Override // override with covariant return type localDateTime(TemporalAccessor temporal)296 public ChronoLocalDateTime<HijrahDate> localDateTime(TemporalAccessor temporal) { 297 return (ChronoLocalDateTime<HijrahDate>) super.localDateTime(temporal); 298 } 299 300 @SuppressWarnings("unchecked") 301 @Override // override with covariant return type zonedDateTime(TemporalAccessor temporal)302 public ChronoZonedDateTime<HijrahDate> zonedDateTime(TemporalAccessor temporal) { 303 return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(temporal); 304 } 305 306 @SuppressWarnings("unchecked") 307 @Override // override with covariant return type zonedDateTime(Instant instant, ZoneId zone)308 public ChronoZonedDateTime<HijrahDate> zonedDateTime(Instant instant, ZoneId zone) { 309 return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(instant, zone); 310 } 311 312 //----------------------------------------------------------------------- 313 @Override // override with covariant return type dateNow()314 public HijrahDate dateNow() { 315 return (HijrahDate) super.dateNow(); 316 } 317 318 @Override // override with covariant return type dateNow(ZoneId zone)319 public HijrahDate dateNow(ZoneId zone) { 320 return (HijrahDate) super.dateNow(zone); 321 } 322 323 @Override // override with covariant return type dateNow(Clock clock)324 public HijrahDate dateNow(Clock clock) { 325 Jdk8Methods.requireNonNull(clock, "clock"); 326 return (HijrahDate) super.dateNow(clock); 327 } 328 329 //----------------------------------------------------------------------- 330 @Override isLeapYear(long prolepticYear)331 public boolean isLeapYear(long prolepticYear) { 332 return HijrahDate.isLeapYear(prolepticYear); 333 } 334 335 @Override prolepticYear(Era era, int yearOfEra)336 public int prolepticYear(Era era, int yearOfEra) { 337 if (era instanceof HijrahEra == false) { 338 throw new ClassCastException("Era must be HijrahEra"); 339 } 340 return (era == HijrahEra.AH ? yearOfEra : 1 - yearOfEra); 341 } 342 343 @Override eraOf(int eraValue)344 public HijrahEra eraOf(int eraValue) { 345 switch (eraValue) { 346 case 0: 347 return HijrahEra.BEFORE_AH; 348 case 1: 349 return HijrahEra.AH; 350 default: 351 throw new DateTimeException("invalid Hijrah era"); 352 } 353 } 354 355 @Override eras()356 public List<Era> eras() { 357 return Arrays.<Era>asList(HijrahEra.values()); 358 } 359 360 //----------------------------------------------------------------------- 361 @Override range(ChronoField field)362 public ValueRange range(ChronoField field) { 363 return field.range(); 364 } 365 366 @Override resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle)367 public HijrahDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) { 368 if (fieldValues.containsKey(EPOCH_DAY)) { 369 return dateEpochDay(fieldValues.remove(EPOCH_DAY)); 370 } 371 372 // normalize fields 373 Long prolepticMonth = fieldValues.remove(PROLEPTIC_MONTH); 374 if (prolepticMonth != null) { 375 if (resolverStyle != ResolverStyle.LENIENT) { 376 PROLEPTIC_MONTH.checkValidValue(prolepticMonth); 377 } 378 updateResolveMap(fieldValues, MONTH_OF_YEAR, Jdk8Methods.floorMod(prolepticMonth, 12) + 1); 379 updateResolveMap(fieldValues, YEAR, Jdk8Methods.floorDiv(prolepticMonth, 12)); 380 } 381 382 // eras 383 Long yoeLong = fieldValues.remove(YEAR_OF_ERA); 384 if (yoeLong != null) { 385 if (resolverStyle != ResolverStyle.LENIENT) { 386 YEAR_OF_ERA.checkValidValue(yoeLong); 387 } 388 Long era = fieldValues.remove(ERA); 389 if (era == null) { 390 Long year = fieldValues.get(YEAR); 391 if (resolverStyle == ResolverStyle.STRICT) { 392 // do not invent era if strict, but do cross-check with year 393 if (year != null) { 394 updateResolveMap(fieldValues, YEAR, (year > 0 ? yoeLong: Jdk8Methods.safeSubtract(1, yoeLong))); 395 } else { 396 // reinstate the field removed earlier, no cross-check issues 397 fieldValues.put(YEAR_OF_ERA, yoeLong); 398 } 399 } else { 400 // invent era 401 updateResolveMap(fieldValues, YEAR, (year == null || year > 0 ? yoeLong: Jdk8Methods.safeSubtract(1, yoeLong))); 402 } 403 } else if (era.longValue() == 1L) { 404 updateResolveMap(fieldValues, YEAR, yoeLong); 405 } else if (era.longValue() == 0L) { 406 updateResolveMap(fieldValues, YEAR, Jdk8Methods.safeSubtract(1, yoeLong)); 407 } else { 408 throw new DateTimeException("Invalid value for era: " + era); 409 } 410 } else if (fieldValues.containsKey(ERA)) { 411 ERA.checkValidValue(fieldValues.get(ERA)); // always validated 412 } 413 414 // build date 415 if (fieldValues.containsKey(YEAR)) { 416 if (fieldValues.containsKey(MONTH_OF_YEAR)) { 417 if (fieldValues.containsKey(DAY_OF_MONTH)) { 418 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 419 if (resolverStyle == ResolverStyle.LENIENT) { 420 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 421 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_MONTH), 1); 422 return date(y, 1, 1).plusMonths(months).plusDays(days); 423 } else { 424 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR); 425 int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH); 426 if (resolverStyle == ResolverStyle.SMART && dom > 28) { 427 dom = Math.min(dom, date(y, moy, 1).lengthOfMonth()); 428 } 429 return date(y, moy, dom); 430 } 431 } 432 if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) { 433 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) { 434 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 435 if (resolverStyle == ResolverStyle.LENIENT) { 436 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 437 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); 438 long days = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1); 439 return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS); 440 } 441 int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR)); 442 int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH)); 443 int ad = ALIGNED_DAY_OF_WEEK_IN_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH)); 444 HijrahDate date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS); 445 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { 446 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 447 } 448 return date; 449 } 450 if (fieldValues.containsKey(DAY_OF_WEEK)) { 451 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 452 if (resolverStyle == ResolverStyle.LENIENT) { 453 long months = Jdk8Methods.safeSubtract(fieldValues.remove(MONTH_OF_YEAR), 1); 454 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); 455 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_WEEK), 1); 456 return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS); 457 } 458 int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR)); 459 int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH)); 460 int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK)); 461 HijrahDate date = date(y, moy, 1).plus(aw - 1, WEEKS).with(nextOrSame(DayOfWeek.of(dow))); 462 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { 463 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 464 } 465 return date; 466 } 467 } 468 } 469 if (fieldValues.containsKey(DAY_OF_YEAR)) { 470 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 471 if (resolverStyle == ResolverStyle.LENIENT) { 472 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_YEAR), 1); 473 return dateYearDay(y, 1).plusDays(days); 474 } 475 int doy = DAY_OF_YEAR.checkValidIntValue(fieldValues.remove(DAY_OF_YEAR)); 476 return dateYearDay(y, doy); 477 } 478 if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) { 479 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) { 480 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 481 if (resolverStyle == ResolverStyle.LENIENT) { 482 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); 483 long days = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1); 484 return date(y, 1, 1).plus(weeks, WEEKS).plus(days, DAYS); 485 } 486 int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR)); 487 int ad = ALIGNED_DAY_OF_WEEK_IN_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR)); 488 HijrahDate date = date(y, 1, 1).plusDays((aw - 1) * 7 + (ad - 1)); 489 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { 490 throw new DateTimeException("Strict mode rejected date parsed to a different year"); 491 } 492 return date; 493 } 494 if (fieldValues.containsKey(DAY_OF_WEEK)) { 495 int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR)); 496 if (resolverStyle == ResolverStyle.LENIENT) { 497 long weeks = Jdk8Methods.safeSubtract(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); 498 long days = Jdk8Methods.safeSubtract(fieldValues.remove(DAY_OF_WEEK), 1); 499 return date(y, 1, 1).plus(weeks, WEEKS).plus(days, DAYS); 500 } 501 int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR)); 502 int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK)); 503 HijrahDate date = date(y, 1, 1).plus(aw - 1, WEEKS).with(nextOrSame(DayOfWeek.of(dow))); 504 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { 505 throw new DateTimeException("Strict mode rejected date parsed to a different month"); 506 } 507 return date; 508 } 509 } 510 } 511 return null; 512 } 513 514 } 515