• 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 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