• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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