jdk/src/share/classes/java/time/calendar/HijrahChrono.java
changeset 15658 55b829ca2334
parent 15657 c588664d547e
child 15659 e575dab44ff5
equal deleted inserted replaced
15657:c588664d547e 15658:55b829ca2334
     1 /*
       
     2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 /*
       
    27  * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
       
    28  *
       
    29  * All rights reserved.
       
    30  *
       
    31  * Redistribution and use in source and binary forms, with or without
       
    32  * modification, are permitted provided that the following conditions are met:
       
    33  *
       
    34  *  * Redistributions of source code must retain the above copyright notice,
       
    35  *    this list of conditions and the following disclaimer.
       
    36  *
       
    37  *  * Redistributions in binary form must reproduce the above copyright notice,
       
    38  *    this list of conditions and the following disclaimer in the documentation
       
    39  *    and/or other materials provided with the distribution.
       
    40  *
       
    41  *  * Neither the name of JSR-310 nor the names of its contributors
       
    42  *    may be used to endorse or promote products derived from this software
       
    43  *    without specific prior written permission.
       
    44  *
       
    45  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    46  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    47  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    48  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
       
    49  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    50  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    51  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    52  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       
    53  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       
    54  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       
    55  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    56  */
       
    57 
       
    58 package java.time.calendar;
       
    59 
       
    60 import static java.time.temporal.ChronoField.EPOCH_DAY;
       
    61 
       
    62 import java.io.IOException;
       
    63 import java.io.Serializable;
       
    64 import java.text.ParseException;
       
    65 import java.time.DateTimeException;
       
    66 import java.time.temporal.Chrono;
       
    67 import java.time.temporal.ChronoField;
       
    68 import java.time.temporal.ChronoLocalDate;
       
    69 import java.time.temporal.Era;
       
    70 import java.time.temporal.TemporalAccessor;
       
    71 import java.time.temporal.ValueRange;
       
    72 import java.util.Arrays;
       
    73 import java.util.HashMap;
       
    74 import java.util.List;
       
    75 import java.util.Locale;
       
    76 
       
    77 /**
       
    78  * The Hijrah calendar system.
       
    79  * <p>
       
    80  * This chronology defines the rules of the Hijrah calendar system.
       
    81  * <p>
       
    82  * The implementation follows the Freeman-Grenville algorithm (*1) and has following features.
       
    83  * <p><ul>
       
    84  * <li>A year has 12 months.</li>
       
    85  * <li>Over a cycle of 30 years there are 11 leap years.</li>
       
    86  * <li>There are 30 days in month number 1, 3, 5, 7, 9, and 11,
       
    87  * and 29 days in month number 2, 4, 6, 8, 10, and 12.</li>
       
    88  * <li>In a leap year month 12 has 30 days.</li>
       
    89  * <li>In a 30 year cycle, year 2, 5, 7, 10, 13, 16, 18, 21, 24,
       
    90  * 26, and 29 are leap years.</li>
       
    91  * <li>Total of 10631 days in a 30 years cycle.</li>
       
    92  * </ul><p>
       
    93  * <P>
       
    94  * The table shows the features described above.
       
    95  * <blockquote>
       
    96  * <table border="1">
       
    97  *   <caption>Hijrah Calendar Months</caption>
       
    98  *   <tbody>
       
    99  *     <tr>
       
   100  *       <th># of month</th>
       
   101  *       <th>Name of month</th>
       
   102  *       <th>Number of days</th>
       
   103  *     </tr>
       
   104  *     <tr>
       
   105  *       <td>1</td>
       
   106  *       <td>Muharram</td>
       
   107  *       <td>30</td>
       
   108  *     </tr>
       
   109  *     <tr>
       
   110  *       <td>2</td>
       
   111  *       <td>Safar</td>
       
   112  *       <td>29</td>
       
   113  *     </tr>
       
   114  *     <tr>
       
   115  *       <td>3</td>
       
   116  *       <td>Rabi'al-Awwal</td>
       
   117  *       <td>30</td>
       
   118  *     </tr>
       
   119  *     <tr>
       
   120  *       <td>4</td>
       
   121  *       <td>Rabi'ath-Thani</td>
       
   122  *       <td>29</td>
       
   123  *     </tr>
       
   124  *     <tr>
       
   125  *       <td>5</td>
       
   126  *       <td>Jumada l-Ula</td>
       
   127  *       <td>30</td>
       
   128  *     </tr>
       
   129  *     <tr>
       
   130  *       <td>6</td>
       
   131  *       <td>Jumada t-Tania</td>
       
   132  *       <td>29</td>
       
   133  *     </tr>
       
   134  *     <tr>
       
   135  *       <td>7</td>
       
   136  *       <td>Rajab</td>
       
   137  *       <td>30</td>
       
   138  *     </tr>
       
   139  *     <tr>
       
   140  *       <td>8</td>
       
   141  *       <td>Sha`ban</td>
       
   142  *       <td>29</td>
       
   143  *     </tr>
       
   144  *     <tr>
       
   145  *       <td>9</td>
       
   146  *       <td>Ramadan</td>
       
   147  *       <td>30</td>
       
   148  *     </tr>
       
   149  *     <tr>
       
   150  *       <td>10</td>
       
   151  *       <td>Shawwal</td>
       
   152  *       <td>29</td>
       
   153  *     </tr>
       
   154  *     <tr>
       
   155  *       <td>11</td>
       
   156  *       <td>Dhu 'l-Qa`da</td>
       
   157  *       <td>30</td>
       
   158  *     </tr>
       
   159  *     <tr>
       
   160  *       <td>12</td>
       
   161  *       <td>Dhu 'l-Hijja</td>
       
   162  *       <td>29, but 30 days in years 2, 5, 7, 10,<br>
       
   163  * 13, 16, 18, 21, 24, 26, and 29</td>
       
   164  *     </tr>
       
   165  *   </tbody>
       
   166  * </table>
       
   167  * </blockquote>
       
   168  * <p>
       
   169  * (*1) The algorithm is taken from the book,
       
   170  * The Muslim and Christian Calendars by G.S.P. Freeman-Grenville.
       
   171  * <p>
       
   172  *
       
   173  * <h3>Specification for implementors</h3>
       
   174  * This class is immutable and thread-safe.
       
   175  *
       
   176  * @since 1.8
       
   177  */
       
   178 public final class HijrahChrono extends Chrono<HijrahChrono> implements Serializable {
       
   179 
       
   180     /**
       
   181      * The Hijrah Calendar id.
       
   182      */
       
   183     private final String typeId;
       
   184 
       
   185     /**
       
   186      * The Hijrah calendarType.
       
   187      */
       
   188     private final String calendarType;
       
   189 
       
   190     /**
       
   191      * The singleton instance for the era before the current one - Before Hijrah -
       
   192      * which has the value 0.
       
   193      */
       
   194     public static final Era<HijrahChrono> ERA_BEFORE_AH = HijrahEra.BEFORE_AH;
       
   195     /**
       
   196      * The singleton instance for the current era - Hijrah - which has the value 1.
       
   197      */
       
   198     public static final Era<HijrahChrono> ERA_AH = HijrahEra.AH;
       
   199     /**
       
   200      * Serialization version.
       
   201      */
       
   202     private static final long serialVersionUID = 3127340209035924785L;
       
   203     /**
       
   204      * The minimum valid year-of-era.
       
   205      */
       
   206     public static final int MIN_YEAR_OF_ERA = 1;
       
   207     /**
       
   208      * The maximum valid year-of-era.
       
   209      * This is currently set to 9999 but may be changed to increase the valid range
       
   210      * in a future version of the specification.
       
   211      */
       
   212     public static final int MAX_YEAR_OF_ERA = 9999;
       
   213 
       
   214     /**
       
   215      * Number of Gregorian day of July 19, year 622 (Gregorian), which is epoch day
       
   216      * of Hijrah calendar.
       
   217      */
       
   218     private static final int HIJRAH_JAN_1_1_GREGORIAN_DAY = -492148;
       
   219     /**
       
   220      * 0-based, for number of day-of-year in the beginning of month in normal
       
   221      * year.
       
   222      */
       
   223     private static final int NUM_DAYS[] =
       
   224         {0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325};
       
   225     /**
       
   226      * 0-based, for number of day-of-year in the beginning of month in leap year.
       
   227      */
       
   228     private static final int LEAP_NUM_DAYS[] =
       
   229         {0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325};
       
   230     /**
       
   231      * 0-based, for day-of-month in normal year.
       
   232      */
       
   233     private static final int MONTH_LENGTH[] =
       
   234         {30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29};
       
   235     /**
       
   236      * 0-based, for day-of-month in leap year.
       
   237      */
       
   238     private static final int LEAP_MONTH_LENGTH[] =
       
   239         {30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 30};
       
   240 
       
   241     /**
       
   242      * <pre>
       
   243      *                            Greatest       Least
       
   244      * Field name        Minimum   Minimum     Maximum     Maximum
       
   245      * ----------        -------   -------     -------     -------
       
   246      * ERA                     0         0           1           1
       
   247      * YEAR_OF_ERA             1         1        9999        9999
       
   248      * MONTH_OF_YEAR           1         1          12          12
       
   249      * DAY_OF_MONTH            1         1          29          30
       
   250      * DAY_OF_YEAR             1         1         354         355
       
   251      * </pre>
       
   252      *
       
   253      * Minimum values.
       
   254      */
       
   255     private static final int MIN_VALUES[] =
       
   256         {
       
   257         0,
       
   258         MIN_YEAR_OF_ERA,
       
   259         0,
       
   260         1,
       
   261         0,
       
   262         1,
       
   263         1
       
   264         };
       
   265 
       
   266     /**
       
   267      * Least maximum values.
       
   268      */
       
   269     private static final int LEAST_MAX_VALUES[] =
       
   270         {
       
   271         1,
       
   272         MAX_YEAR_OF_ERA,
       
   273         11,
       
   274         51,
       
   275         5,
       
   276         29,
       
   277         354
       
   278         };
       
   279 
       
   280     /**
       
   281      * Maximum values.
       
   282      */
       
   283     private static final int MAX_VALUES[] =
       
   284         {
       
   285         1,
       
   286         MAX_YEAR_OF_ERA,
       
   287         11,
       
   288         52,
       
   289         6,
       
   290         30,
       
   291         355
       
   292         };
       
   293 
       
   294    /**
       
   295      * Position of day-of-month. This value is used to get the min/max value
       
   296      * from an array.
       
   297      */
       
   298     private static final int POSITION_DAY_OF_MONTH = 5;
       
   299     /**
       
   300      * Position of day-of-year. This value is used to get the min/max value from
       
   301      * an array.
       
   302      */
       
   303     private static final int POSITION_DAY_OF_YEAR = 6;
       
   304     /**
       
   305      * Zero-based start date of cycle year.
       
   306      */
       
   307     private static final int CYCLEYEAR_START_DATE[] =
       
   308         {
       
   309         0,
       
   310         354,
       
   311         709,
       
   312         1063,
       
   313         1417,
       
   314         1772,
       
   315         2126,
       
   316         2481,
       
   317         2835,
       
   318         3189,
       
   319         3544,
       
   320         3898,
       
   321         4252,
       
   322         4607,
       
   323         4961,
       
   324         5315,
       
   325         5670,
       
   326         6024,
       
   327         6379,
       
   328         6733,
       
   329         7087,
       
   330         7442,
       
   331         7796,
       
   332         8150,
       
   333         8505,
       
   334         8859,
       
   335         9214,
       
   336         9568,
       
   337         9922,
       
   338         10277
       
   339         };
       
   340 
       
   341     /**
       
   342      * Holding the adjusted month days in year. The key is a year (Integer) and
       
   343      * the value is the all the month days in year (int[]).
       
   344      */
       
   345     private final HashMap<Integer, int[]> ADJUSTED_MONTH_DAYS = new HashMap<>();
       
   346     /**
       
   347      * Holding the adjusted month length in year. The key is a year (Integer)
       
   348      * and the value is the all the month length in year (int[]).
       
   349      */
       
   350     private final HashMap<Integer, int[]> ADJUSTED_MONTH_LENGTHS = new HashMap<>();
       
   351     /**
       
   352      * Holding the adjusted days in the 30 year cycle. The key is a cycle number
       
   353      * (Integer) and the value is the all the starting days of the year in the
       
   354      * cycle (int[]).
       
   355      */
       
   356     private final HashMap<Integer, int[]> ADJUSTED_CYCLE_YEARS = new HashMap<>();
       
   357     /**
       
   358      * Holding the adjusted cycle in the 1 - 30000 year. The key is the cycle
       
   359      * number (Integer) and the value is the starting days in the cycle in the
       
   360      * term.
       
   361      */
       
   362     private final long[] ADJUSTED_CYCLES;
       
   363     /**
       
   364      * Holding the adjusted min values.
       
   365      */
       
   366     private final int[] ADJUSTED_MIN_VALUES;
       
   367     /**
       
   368      * Holding the adjusted max least max values.
       
   369      */
       
   370     private final int[] ADJUSTED_LEAST_MAX_VALUES;
       
   371     /**
       
   372      * Holding adjusted max values.
       
   373      */
       
   374     private final int[] ADJUSTED_MAX_VALUES;
       
   375     /**
       
   376      * Holding the non-adjusted month days in year for non leap year.
       
   377      */
       
   378     private static final int[] DEFAULT_MONTH_DAYS;
       
   379     /**
       
   380      * Holding the non-adjusted month days in year for leap year.
       
   381      */
       
   382     private static final int[] DEFAULT_LEAP_MONTH_DAYS;
       
   383     /**
       
   384      * Holding the non-adjusted month length for non leap year.
       
   385      */
       
   386     private static final int[] DEFAULT_MONTH_LENGTHS;
       
   387     /**
       
   388      * Holding the non-adjusted month length for leap year.
       
   389      */
       
   390     private static final int[] DEFAULT_LEAP_MONTH_LENGTHS;
       
   391     /**
       
   392      * Holding the non-adjusted 30 year cycle starting day.
       
   393      */
       
   394     private static final int[] DEFAULT_CYCLE_YEARS;
       
   395     /**
       
   396      * number of 30-year cycles to hold the deviation data.
       
   397      */
       
   398     private static final int MAX_ADJUSTED_CYCLE = 334; // to support year 9999
       
   399 
       
   400 
       
   401     /**
       
   402      * Narrow names for eras.
       
   403      */
       
   404     private static final HashMap<String, String[]> ERA_NARROW_NAMES = new HashMap<>();
       
   405     /**
       
   406      * Short names for eras.
       
   407      */
       
   408     private static final HashMap<String, String[]> ERA_SHORT_NAMES = new HashMap<>();
       
   409     /**
       
   410      * Full names for eras.
       
   411      */
       
   412     private static final HashMap<String, String[]> ERA_FULL_NAMES = new HashMap<>();
       
   413     /**
       
   414      * Fallback language for the era names.
       
   415      */
       
   416     private static final String FALLBACK_LANGUAGE = "en";
       
   417 
       
   418     /**
       
   419      * Singleton instance of the Hijrah chronology.
       
   420      * Must be initialized after the rest of the static initialization.
       
   421      */
       
   422     public static final HijrahChrono INSTANCE;
       
   423 
       
   424     /**
       
   425      * Name data.
       
   426      */
       
   427     static {
       
   428         ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"BH", "HE"});
       
   429         ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"B.H.", "H.E."});
       
   430         ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Before Hijrah", "Hijrah Era"});
       
   431 
       
   432         DEFAULT_MONTH_DAYS = Arrays.copyOf(NUM_DAYS, NUM_DAYS.length);
       
   433 
       
   434         DEFAULT_LEAP_MONTH_DAYS = Arrays.copyOf(LEAP_NUM_DAYS, LEAP_NUM_DAYS.length);
       
   435 
       
   436         DEFAULT_MONTH_LENGTHS = Arrays.copyOf(MONTH_LENGTH, MONTH_LENGTH.length);
       
   437 
       
   438         DEFAULT_LEAP_MONTH_LENGTHS = Arrays.copyOf(LEAP_MONTH_LENGTH, LEAP_MONTH_LENGTH.length);
       
   439 
       
   440         DEFAULT_CYCLE_YEARS = Arrays.copyOf(CYCLEYEAR_START_DATE, CYCLEYEAR_START_DATE.length);
       
   441 
       
   442         INSTANCE = new HijrahChrono();
       
   443 
       
   444         String extraCalendars = java.security.AccessController.doPrivileged(
       
   445             new sun.security.action.GetPropertyAction("java.time.calendar.HijrahCalendars"));
       
   446         if (extraCalendars != null) {
       
   447             try {
       
   448                 // Split on whitespace
       
   449                 String[] splits = extraCalendars.split("\\s");
       
   450                 for (String cal : splits) {
       
   451                     if (!cal.isEmpty()) {
       
   452                         // Split on the delimiter between typeId "-" calendarType
       
   453                         String[] type = cal.split("-");
       
   454                         Chrono<?> cal2 = new HijrahChrono(type[0], type.length > 1 ? type[1] : type[0]);
       
   455                     }
       
   456                 }
       
   457             } catch (Exception ex) {
       
   458                 // Log the error
       
   459                 // ex.printStackTrace();
       
   460             }
       
   461         }
       
   462     }
       
   463 
       
   464     /**
       
   465      * Restricted constructor.
       
   466      */
       
   467     private HijrahChrono() {
       
   468         this("Hijrah", "islamicc");
       
   469     }
       
   470     /**
       
   471      * Constructor for name and type HijrahChrono.
       
   472      * @param id the id of the calendar
       
   473      * @param calendarType the calendar type
       
   474      */
       
   475     private HijrahChrono(String id, String calendarType) {
       
   476         this.typeId = id;
       
   477         this.calendarType = calendarType;
       
   478 
       
   479         ADJUSTED_CYCLES = new long[MAX_ADJUSTED_CYCLE];
       
   480         for (int i = 0; i < ADJUSTED_CYCLES.length; i++) {
       
   481             ADJUSTED_CYCLES[i] = (10631L * i);
       
   482         }
       
   483         // Initialize min values, least max values and max values.
       
   484         ADJUSTED_MIN_VALUES = Arrays.copyOf(MIN_VALUES, MIN_VALUES.length);
       
   485         ADJUSTED_LEAST_MAX_VALUES = Arrays.copyOf(LEAST_MAX_VALUES, LEAST_MAX_VALUES.length);
       
   486         ADJUSTED_MAX_VALUES = Arrays.copyOf(MAX_VALUES,MAX_VALUES.length);
       
   487 
       
   488         try {
       
   489             // Implicitly reads deviation data for this HijrahChronology.
       
   490             boolean any = HijrahDeviationReader.readDeviation(typeId, calendarType, this::addDeviationAsHijrah);
       
   491         } catch (IOException | ParseException e) {
       
   492             // do nothing. Log deviation config errors.
       
   493             //e.printStackTrace();
       
   494         }
       
   495     }
       
   496 
       
   497     /**
       
   498      * Resolve singleton.
       
   499      *
       
   500      * @return the singleton instance, not null
       
   501      */
       
   502     private Object readResolve() {
       
   503         return INSTANCE;
       
   504     }
       
   505 
       
   506     //-----------------------------------------------------------------------
       
   507     /**
       
   508      * Gets the ID of the chronology - 'Hijrah'.
       
   509      * <p>
       
   510      * The ID uniquely identifies the {@code Chrono}.
       
   511      * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
       
   512      *
       
   513      * @return the chronology ID - 'Hijrah'
       
   514      * @see #getCalendarType()
       
   515      */
       
   516     @Override
       
   517     public String getId() {
       
   518         return typeId;
       
   519     }
       
   520 
       
   521     /**
       
   522      * Gets the calendar type of the underlying calendar system - 'islamicc'.
       
   523      * <p>
       
   524      * The calendar type is an identifier defined by the
       
   525      * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
       
   526      * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
       
   527      * It can also be used as part of a locale, accessible via
       
   528      * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
       
   529      *
       
   530      * @return the calendar system type - 'islamicc'
       
   531      * @see #getId()
       
   532      */
       
   533     @Override
       
   534     public String getCalendarType() {
       
   535         return calendarType;
       
   536     }
       
   537 
       
   538     //-----------------------------------------------------------------------
       
   539     @Override
       
   540     public ChronoLocalDate<HijrahChrono> date(int prolepticYear, int month, int dayOfMonth) {
       
   541         return HijrahDate.of(this, prolepticYear, month, dayOfMonth);
       
   542     }
       
   543 
       
   544     @Override
       
   545     public ChronoLocalDate<HijrahChrono> dateYearDay(int prolepticYear, int dayOfYear) {
       
   546         return HijrahDate.of(this, prolepticYear, 1, 1).plusDays(dayOfYear - 1);  // TODO better
       
   547     }
       
   548 
       
   549     @Override
       
   550     public ChronoLocalDate<HijrahChrono> date(TemporalAccessor temporal) {
       
   551         if (temporal instanceof HijrahDate) {
       
   552             return (HijrahDate) temporal;
       
   553         }
       
   554         return HijrahDate.ofEpochDay(this, temporal.getLong(EPOCH_DAY));
       
   555     }
       
   556 
       
   557     //-----------------------------------------------------------------------
       
   558     @Override
       
   559     public boolean isLeapYear(long prolepticYear) {
       
   560         return isLeapYear0(prolepticYear);
       
   561     }
       
   562     /**
       
   563      * Returns if the year is a leap year.
       
   564      * @param prolepticYear he year to compute from
       
   565      * @return {@code true} if the year is a leap year, otherwise {@code false}
       
   566      */
       
   567     private static boolean isLeapYear0(long prolepticYear) {
       
   568         return (14 + 11 * (prolepticYear > 0 ? prolepticYear : -prolepticYear)) % 30 < 11;
       
   569     }
       
   570 
       
   571     @Override
       
   572     public int prolepticYear(Era<HijrahChrono> era, int yearOfEra) {
       
   573         if (era instanceof HijrahEra == false) {
       
   574             throw new DateTimeException("Era must be HijrahEra");
       
   575         }
       
   576         return (era == HijrahEra.AH ? yearOfEra : 1 - yearOfEra);
       
   577     }
       
   578 
       
   579     @Override
       
   580     public Era<HijrahChrono> eraOf(int eraValue) {
       
   581         switch (eraValue) {
       
   582             case 0:
       
   583                 return HijrahEra.BEFORE_AH;
       
   584             case 1:
       
   585                 return HijrahEra.AH;
       
   586             default:
       
   587                 throw new DateTimeException("invalid Hijrah era");
       
   588         }
       
   589     }
       
   590 
       
   591     @Override
       
   592     public List<Era<HijrahChrono>> eras() {
       
   593         return Arrays.<Era<HijrahChrono>>asList(HijrahEra.values());
       
   594     }
       
   595 
       
   596     //-----------------------------------------------------------------------
       
   597     @Override
       
   598     public ValueRange range(ChronoField field) {
       
   599         return field.range();
       
   600     }
       
   601 
       
   602     /**
       
   603      * Check the validity of a yearOfEra.
       
   604      * @param yearOfEra the year to check
       
   605      */
       
   606     void checkValidYearOfEra(int yearOfEra) {
       
   607          if (yearOfEra < MIN_YEAR_OF_ERA  ||
       
   608                  yearOfEra > MAX_YEAR_OF_ERA) {
       
   609              throw new DateTimeException("Invalid year of Hijrah Era");
       
   610          }
       
   611     }
       
   612 
       
   613     void checkValidDayOfYear(int dayOfYear) {
       
   614          if (dayOfYear < 1  ||
       
   615                  dayOfYear > getMaximumDayOfYear()) {
       
   616              throw new DateTimeException("Invalid day of year of Hijrah date");
       
   617          }
       
   618     }
       
   619 
       
   620     void checkValidMonth(int month) {
       
   621          if (month < 1 || month > 12) {
       
   622              throw new DateTimeException("Invalid month of Hijrah date");
       
   623          }
       
   624     }
       
   625 
       
   626     void checkValidDayOfMonth(int dayOfMonth) {
       
   627          if (dayOfMonth < 1  ||
       
   628                  dayOfMonth > getMaximumDayOfMonth()) {
       
   629              throw new DateTimeException("Invalid day of month of Hijrah date, day "
       
   630                      + dayOfMonth + " greater than " + getMaximumDayOfMonth() + " or less than 1");
       
   631          }
       
   632     }
       
   633 
       
   634     //-----------------------------------------------------------------------
       
   635     /**
       
   636      * Returns the int array containing the following field from the julian day.
       
   637      *
       
   638      * int[0] = ERA
       
   639      * int[1] = YEAR
       
   640      * int[2] = MONTH
       
   641      * int[3] = DATE
       
   642      * int[4] = DAY_OF_YEAR
       
   643      * int[5] = DAY_OF_WEEK
       
   644      *
       
   645      * @param gregorianDays  a julian day.
       
   646      */
       
   647     int[] getHijrahDateInfo(long gregorianDays) {
       
   648         int era, year, month, date, dayOfWeek, dayOfYear;
       
   649 
       
   650         int cycleNumber, yearInCycle, dayOfCycle;
       
   651 
       
   652         long epochDay = gregorianDays - HIJRAH_JAN_1_1_GREGORIAN_DAY;
       
   653 
       
   654         if (epochDay >= 0) {
       
   655             cycleNumber = getCycleNumber(epochDay); // 0 - 99.
       
   656             dayOfCycle = getDayOfCycle(epochDay, cycleNumber); // 0 - 10631.
       
   657             yearInCycle = getYearInCycle(cycleNumber, dayOfCycle); // 0 - 29.
       
   658             dayOfYear = getDayOfYear(cycleNumber, dayOfCycle, yearInCycle);
       
   659             // 0 - 354/355
       
   660             year = cycleNumber * 30 + yearInCycle + 1; // 1-based year.
       
   661             month = getMonthOfYear(dayOfYear, year); // 0-based month-of-year
       
   662             date = getDayOfMonth(dayOfYear, month, year); // 0-based date
       
   663             ++date; // Convert from 0-based to 1-based
       
   664             era = HijrahEra.AH.getValue();
       
   665         } else {
       
   666             cycleNumber = (int) epochDay / 10631; // 0 or negative number.
       
   667             dayOfCycle = (int) epochDay % 10631; // -10630 - 0.
       
   668             if (dayOfCycle == 0) {
       
   669                 dayOfCycle = -10631;
       
   670                 cycleNumber++;
       
   671             }
       
   672             yearInCycle = getYearInCycle(cycleNumber, dayOfCycle); // 0 - 29.
       
   673             dayOfYear = getDayOfYear(cycleNumber, dayOfCycle, yearInCycle);
       
   674             year = cycleNumber * 30 - yearInCycle; // negative number.
       
   675             year = 1 - year;
       
   676             dayOfYear = (isLeapYear(year) ? (dayOfYear + 355)
       
   677                     : (dayOfYear + 354));
       
   678             month = getMonthOfYear(dayOfYear, year);
       
   679             date = getDayOfMonth(dayOfYear, month, year);
       
   680             ++date; // Convert from 0-based to 1-based
       
   681             era = HijrahEra.BEFORE_AH.getValue();
       
   682         }
       
   683         // Hijrah day zero is a Friday
       
   684         dayOfWeek = (int) ((epochDay + 5) % 7);
       
   685         dayOfWeek += (dayOfWeek <= 0) ? 7 : 0;
       
   686 
       
   687         int dateInfo[] = new int[6];
       
   688         dateInfo[0] = era;
       
   689         dateInfo[1] = year;
       
   690         dateInfo[2] = month + 1; // change to 1-based.
       
   691         dateInfo[3] = date;
       
   692         dateInfo[4] = dayOfYear + 1; // change to 1-based.
       
   693         dateInfo[5] = dayOfWeek;
       
   694         return dateInfo;
       
   695     }
       
   696 
       
   697     /**
       
   698      * Return Gregorian epoch day from Hijrah year, month, and day.
       
   699      *
       
   700      * @param prolepticYear  the year to represent, caller calculated
       
   701      * @param monthOfYear  the month-of-year to represent, caller calculated
       
   702      * @param dayOfMonth  the day-of-month to represent, caller calculated
       
   703      * @return a julian day
       
   704      */
       
   705     long getGregorianEpochDay(int prolepticYear, int monthOfYear, int dayOfMonth) {
       
   706         long day = yearToGregorianEpochDay(prolepticYear);
       
   707         day += getMonthDays(monthOfYear - 1, prolepticYear);
       
   708         day += dayOfMonth;
       
   709         return day;
       
   710     }
       
   711 
       
   712     /**
       
   713      * Returns the Gregorian epoch day from the proleptic year
       
   714      * @param prolepticYear the proleptic year
       
   715      * @return the Epoch day
       
   716      */
       
   717     private long yearToGregorianEpochDay(int prolepticYear) {
       
   718 
       
   719         int cycleNumber = (prolepticYear - 1) / 30; // 0-based.
       
   720         int yearInCycle = (prolepticYear - 1) % 30; // 0-based.
       
   721 
       
   722         int dayInCycle = getAdjustedCycle(cycleNumber)[Math.abs(yearInCycle)]
       
   723                 ;
       
   724 
       
   725         if (yearInCycle < 0) {
       
   726             dayInCycle = -dayInCycle;
       
   727         }
       
   728 
       
   729         Long cycleDays;
       
   730 
       
   731         try {
       
   732             cycleDays = ADJUSTED_CYCLES[cycleNumber];
       
   733         } catch (ArrayIndexOutOfBoundsException e) {
       
   734             cycleDays = null;
       
   735         }
       
   736 
       
   737         if (cycleDays == null) {
       
   738             cycleDays = new Long(cycleNumber * 10631);
       
   739         }
       
   740 
       
   741         return (cycleDays.longValue() + dayInCycle + HIJRAH_JAN_1_1_GREGORIAN_DAY - 1);
       
   742     }
       
   743 
       
   744     /**
       
   745      * Returns the 30 year cycle number from the epoch day.
       
   746      *
       
   747      * @param epochDay  an epoch day
       
   748      * @return a cycle number
       
   749      */
       
   750     private int getCycleNumber(long epochDay) {
       
   751         long[] days = ADJUSTED_CYCLES;
       
   752         int cycleNumber;
       
   753         try {
       
   754             for (int i = 0; i < days.length; i++) {
       
   755                 if (epochDay < days[i]) {
       
   756                     return i - 1;
       
   757                 }
       
   758             }
       
   759             cycleNumber = (int) epochDay / 10631;
       
   760         } catch (ArrayIndexOutOfBoundsException e) {
       
   761             cycleNumber = (int) epochDay / 10631;
       
   762         }
       
   763         return cycleNumber;
       
   764     }
       
   765 
       
   766     /**
       
   767      * Returns day of cycle from the epoch day and cycle number.
       
   768      *
       
   769      * @param epochDay  an epoch day
       
   770      * @param cycleNumber  a cycle number
       
   771      * @return a day of cycle
       
   772      */
       
   773     private int getDayOfCycle(long epochDay, int cycleNumber) {
       
   774         Long day;
       
   775 
       
   776         try {
       
   777             day = ADJUSTED_CYCLES[cycleNumber];
       
   778         } catch (ArrayIndexOutOfBoundsException e) {
       
   779             day = null;
       
   780         }
       
   781         if (day == null) {
       
   782             day = new Long(cycleNumber * 10631);
       
   783         }
       
   784         return (int) (epochDay - day.longValue());
       
   785     }
       
   786 
       
   787     /**
       
   788      * Returns the year in cycle from the cycle number and day of cycle.
       
   789      *
       
   790      * @param cycleNumber  a cycle number
       
   791      * @param dayOfCycle  day of cycle
       
   792      * @return a year in cycle
       
   793      */
       
   794     private int getYearInCycle(int cycleNumber, long dayOfCycle) {
       
   795         int[] cycles = getAdjustedCycle(cycleNumber);
       
   796         if (dayOfCycle == 0) {
       
   797             return 0;
       
   798         }
       
   799 
       
   800         if (dayOfCycle > 0) {
       
   801             for (int i = 0; i < cycles.length; i++) {
       
   802                 if (dayOfCycle < cycles[i]) {
       
   803                     return i - 1;
       
   804                 }
       
   805             }
       
   806             return 29;
       
   807         } else {
       
   808             dayOfCycle = -dayOfCycle;
       
   809             for (int i = 0; i < cycles.length; i++) {
       
   810                 if (dayOfCycle <= cycles[i]) {
       
   811                     return i - 1;
       
   812                 }
       
   813             }
       
   814             return 29;
       
   815         }
       
   816     }
       
   817 
       
   818     /**
       
   819      * Returns adjusted 30 year cycle starting day as Integer array from the
       
   820      * cycle number specified.
       
   821      *
       
   822      * @param cycleNumber  a cycle number
       
   823      * @return an Integer array
       
   824      */
       
   825     int[] getAdjustedCycle(int cycleNumber) {
       
   826         int[] cycles;
       
   827         try {
       
   828             cycles = ADJUSTED_CYCLE_YEARS.get(cycleNumber);
       
   829         } catch (ArrayIndexOutOfBoundsException e) {
       
   830             cycles = null;
       
   831         }
       
   832         if (cycles == null) {
       
   833             cycles = DEFAULT_CYCLE_YEARS;
       
   834         }
       
   835         return cycles;
       
   836     }
       
   837 
       
   838     /**
       
   839      * Returns adjusted month days as Integer array form the year specified.
       
   840      *
       
   841      * @param year  a year
       
   842      * @return an Integer array
       
   843      */
       
   844     int[] getAdjustedMonthDays(int year) {
       
   845         int[] newMonths;
       
   846         try {
       
   847             newMonths = ADJUSTED_MONTH_DAYS.get(year);
       
   848         } catch (ArrayIndexOutOfBoundsException e) {
       
   849             newMonths = null;
       
   850         }
       
   851         if (newMonths == null) {
       
   852             if (isLeapYear0(year)) {
       
   853                 newMonths = DEFAULT_LEAP_MONTH_DAYS;
       
   854             } else {
       
   855                 newMonths = DEFAULT_MONTH_DAYS;
       
   856             }
       
   857         }
       
   858         return newMonths;
       
   859     }
       
   860 
       
   861     /**
       
   862      * Returns adjusted month length as Integer array form the year specified.
       
   863      *
       
   864      * @param year  a year
       
   865      * @return an Integer array
       
   866      */
       
   867     int[] getAdjustedMonthLength(int year) {
       
   868         int[] newMonths;
       
   869         try {
       
   870             newMonths = ADJUSTED_MONTH_LENGTHS.get(year);
       
   871         } catch (ArrayIndexOutOfBoundsException e) {
       
   872             newMonths = null;
       
   873         }
       
   874         if (newMonths == null) {
       
   875             if (isLeapYear0(year)) {
       
   876                 newMonths = DEFAULT_LEAP_MONTH_LENGTHS;
       
   877             } else {
       
   878                 newMonths = DEFAULT_MONTH_LENGTHS;
       
   879             }
       
   880         }
       
   881         return newMonths;
       
   882     }
       
   883 
       
   884     /**
       
   885      * Returns day-of-year.
       
   886      *
       
   887      * @param cycleNumber  a cycle number
       
   888      * @param dayOfCycle  day of cycle
       
   889      * @param yearInCycle  year in cycle
       
   890      * @return day-of-year
       
   891      */
       
   892     private int getDayOfYear(int cycleNumber, int dayOfCycle, int yearInCycle) {
       
   893         int[] cycles = getAdjustedCycle(cycleNumber);
       
   894 
       
   895         if (dayOfCycle > 0) {
       
   896             return dayOfCycle - cycles[yearInCycle];
       
   897         } else {
       
   898             return cycles[yearInCycle] + dayOfCycle;
       
   899         }
       
   900     }
       
   901 
       
   902     /**
       
   903      * Returns month-of-year. 0-based.
       
   904      *
       
   905      * @param dayOfYear  day-of-year
       
   906      * @param year  a year
       
   907      * @return month-of-year
       
   908      */
       
   909     private int getMonthOfYear(int dayOfYear, int year) {
       
   910 
       
   911         int[] newMonths = getAdjustedMonthDays(year);
       
   912 
       
   913         if (dayOfYear >= 0) {
       
   914             for (int i = 0; i < newMonths.length; i++) {
       
   915                 if (dayOfYear < newMonths[i]) {
       
   916                     return i - 1;
       
   917                 }
       
   918             }
       
   919             return 11;
       
   920         } else {
       
   921             dayOfYear = (isLeapYear0(year) ? (dayOfYear + 355)
       
   922                     : (dayOfYear + 354));
       
   923             for (int i = 0; i < newMonths.length; i++) {
       
   924                 if (dayOfYear < newMonths[i]) {
       
   925                     return i - 1;
       
   926                 }
       
   927             }
       
   928             return 11;
       
   929         }
       
   930     }
       
   931 
       
   932     /**
       
   933      * Returns day-of-month.
       
   934      *
       
   935      * @param dayOfYear  day of  year
       
   936      * @param month  month
       
   937      * @param year  year
       
   938      * @return day-of-month
       
   939      */
       
   940     private int getDayOfMonth(int dayOfYear, int month, int year) {
       
   941 
       
   942         int[] newMonths = getAdjustedMonthDays(year);
       
   943 
       
   944         if (dayOfYear >= 0) {
       
   945             if (month > 0) {
       
   946                 return dayOfYear - newMonths[month];
       
   947             } else {
       
   948                 return dayOfYear;
       
   949             }
       
   950         } else {
       
   951             dayOfYear = (isLeapYear0(year) ? (dayOfYear + 355)
       
   952                     : (dayOfYear + 354));
       
   953             if (month > 0) {
       
   954                 return dayOfYear - newMonths[month];
       
   955             } else {
       
   956                 return dayOfYear;
       
   957             }
       
   958         }
       
   959     }
       
   960 
       
   961 
       
   962     /**
       
   963      * Returns month days from the beginning of year.
       
   964      *
       
   965      * @param month  month (0-based)
       
   966      * @parma year  year
       
   967      * @return month days from the beginning of year
       
   968      */
       
   969     private int getMonthDays(int month, int year) {
       
   970         int[] newMonths = getAdjustedMonthDays(year);
       
   971         return newMonths[month];
       
   972     }
       
   973 
       
   974     /**
       
   975      * Returns month length.
       
   976      *
       
   977      * @param month  month (0-based)
       
   978      * @param year  year
       
   979      * @return month length
       
   980      */
       
   981     private int getMonthLength(int month, int year) {
       
   982       int[] newMonths = getAdjustedMonthLength(year);
       
   983       return newMonths[month];
       
   984     }
       
   985 
       
   986     /**
       
   987      * Returns year length.
       
   988      *
       
   989      * @param year  year
       
   990      * @return year length
       
   991      */
       
   992     int getYearLength(int year) {
       
   993 
       
   994         int cycleNumber = (year - 1) / 30;
       
   995         int[] cycleYears;
       
   996         try {
       
   997             cycleYears = ADJUSTED_CYCLE_YEARS.get(cycleNumber);
       
   998         } catch (ArrayIndexOutOfBoundsException e) {
       
   999             cycleYears = null;
       
  1000         }
       
  1001         if (cycleYears != null) {
       
  1002             int yearInCycle = (year - 1) % 30;
       
  1003             if (yearInCycle == 29) {
       
  1004                 return (int)(ADJUSTED_CYCLES[cycleNumber + 1]
       
  1005                         - ADJUSTED_CYCLES[cycleNumber]
       
  1006                         - cycleYears[yearInCycle]);
       
  1007             }
       
  1008             return cycleYears[yearInCycle + 1]
       
  1009                     - cycleYears[yearInCycle];
       
  1010         } else {
       
  1011             return isLeapYear0(year) ? 355 : 354;
       
  1012         }
       
  1013     }
       
  1014 
       
  1015 
       
  1016     /**
       
  1017      * Returns maximum day-of-month.
       
  1018      *
       
  1019      * @return maximum day-of-month
       
  1020      */
       
  1021     int getMaximumDayOfMonth() {
       
  1022         return ADJUSTED_MAX_VALUES[POSITION_DAY_OF_MONTH];
       
  1023     }
       
  1024 
       
  1025     /**
       
  1026      * Returns smallest maximum day-of-month.
       
  1027      *
       
  1028      * @return smallest maximum day-of-month
       
  1029      */
       
  1030     int getSmallestMaximumDayOfMonth() {
       
  1031         return ADJUSTED_LEAST_MAX_VALUES[POSITION_DAY_OF_MONTH];
       
  1032     }
       
  1033 
       
  1034     /**
       
  1035      * Returns maximum day-of-year.
       
  1036      *
       
  1037      * @return maximum day-of-year
       
  1038      */
       
  1039     int getMaximumDayOfYear() {
       
  1040         return ADJUSTED_MAX_VALUES[POSITION_DAY_OF_YEAR];
       
  1041     }
       
  1042 
       
  1043     /**
       
  1044      * Returns smallest maximum day-of-year.
       
  1045      *
       
  1046      * @return smallest maximum day-of-year
       
  1047      */
       
  1048     int getSmallestMaximumDayOfYear() {
       
  1049         return ADJUSTED_LEAST_MAX_VALUES[POSITION_DAY_OF_YEAR];
       
  1050     }
       
  1051 
       
  1052     // ----- Deviation handling -----//
       
  1053 
       
  1054     /**
       
  1055      * Adds deviation definition. The year and month specified should be the
       
  1056      * calculated Hijrah year and month. The month is 1 based. e.g. 9 for
       
  1057      * Ramadan (9th month) Addition of anything minus deviation days is
       
  1058      * calculated negatively in the case the user wants to subtract days from
       
  1059      * the calendar. For example, adding -1 days will subtract one day from the
       
  1060      * current date.
       
  1061      *
       
  1062      * @param startYear  start year, 1 origin
       
  1063      * @param startMonth  start month, 1 origin
       
  1064      * @param endYear  end year, 1 origin
       
  1065      * @param endMonth  end month, 1 origin
       
  1066      * @param offset  offset -2, -1, +1, +2
       
  1067      */
       
  1068     private void addDeviationAsHijrah(Deviation entry) {
       
  1069         int startYear = entry.startYear;
       
  1070         int startMonth = entry.startMonth - 1 ;
       
  1071         int endYear = entry.endYear;
       
  1072         int endMonth = entry.endMonth - 1;
       
  1073         int offset = entry.offset;
       
  1074 
       
  1075         if (startYear < 1) {
       
  1076             throw new IllegalArgumentException("startYear < 1");
       
  1077         }
       
  1078         if (endYear < 1) {
       
  1079             throw new IllegalArgumentException("endYear < 1");
       
  1080         }
       
  1081         if (startMonth < 0 || startMonth > 11) {
       
  1082             throw new IllegalArgumentException(
       
  1083                     "startMonth < 0 || startMonth > 11");
       
  1084         }
       
  1085         if (endMonth < 0 || endMonth > 11) {
       
  1086             throw new IllegalArgumentException("endMonth < 0 || endMonth > 11");
       
  1087         }
       
  1088         if (endYear > 9999) {
       
  1089             throw new IllegalArgumentException("endYear > 9999");
       
  1090         }
       
  1091         if (endYear < startYear) {
       
  1092             throw new IllegalArgumentException("startYear > endYear");
       
  1093         }
       
  1094         if (endYear == startYear && endMonth < startMonth) {
       
  1095             throw new IllegalArgumentException(
       
  1096                     "startYear == endYear && endMonth < startMonth");
       
  1097         }
       
  1098 
       
  1099         // Adjusting start year.
       
  1100         boolean isStartYLeap = isLeapYear0(startYear);
       
  1101 
       
  1102         // Adjusting the number of month.
       
  1103         int[] orgStartMonthNums = ADJUSTED_MONTH_DAYS.get(startYear);
       
  1104         if (orgStartMonthNums == null) {
       
  1105             if (isStartYLeap) {
       
  1106                 orgStartMonthNums = Arrays.copyOf(LEAP_NUM_DAYS, LEAP_NUM_DAYS.length);
       
  1107             } else {
       
  1108                 orgStartMonthNums = Arrays.copyOf(NUM_DAYS, NUM_DAYS.length);
       
  1109             }
       
  1110         }
       
  1111 
       
  1112         int[] newStartMonthNums = new int[orgStartMonthNums.length];
       
  1113 
       
  1114         for (int month = 0; month < 12; month++) {
       
  1115             if (month > startMonth) {
       
  1116                 newStartMonthNums[month] = (orgStartMonthNums[month] - offset);
       
  1117             } else {
       
  1118                 newStartMonthNums[month] = (orgStartMonthNums[month]);
       
  1119             }
       
  1120         }
       
  1121 
       
  1122         ADJUSTED_MONTH_DAYS.put(startYear, newStartMonthNums);
       
  1123 
       
  1124         // Adjusting the days of month.
       
  1125 
       
  1126         int[] orgStartMonthLengths = ADJUSTED_MONTH_LENGTHS.get(startYear);
       
  1127         if (orgStartMonthLengths == null) {
       
  1128             if (isStartYLeap) {
       
  1129                 orgStartMonthLengths = Arrays.copyOf(LEAP_MONTH_LENGTH, LEAP_MONTH_LENGTH.length);
       
  1130             } else {
       
  1131                 orgStartMonthLengths = Arrays.copyOf(MONTH_LENGTH, MONTH_LENGTH.length);
       
  1132             }
       
  1133         }
       
  1134 
       
  1135         int[] newStartMonthLengths = new int[orgStartMonthLengths.length];
       
  1136 
       
  1137         for (int month = 0; month < 12; month++) {
       
  1138             if (month == startMonth) {
       
  1139                 newStartMonthLengths[month] = orgStartMonthLengths[month] - offset;
       
  1140             } else {
       
  1141                 newStartMonthLengths[month] = orgStartMonthLengths[month];
       
  1142             }
       
  1143         }
       
  1144 
       
  1145         ADJUSTED_MONTH_LENGTHS.put(startYear, newStartMonthLengths);
       
  1146 
       
  1147         if (startYear != endYear) {
       
  1148             // System.out.println("over year");
       
  1149             // Adjusting starting 30 year cycle.
       
  1150             int sCycleNumber = (startYear - 1) / 30;
       
  1151             int sYearInCycle = (startYear - 1) % 30; // 0-based.
       
  1152             int[] startCycles = ADJUSTED_CYCLE_YEARS.get(sCycleNumber);
       
  1153             if (startCycles == null) {
       
  1154                 startCycles = Arrays.copyOf(CYCLEYEAR_START_DATE, CYCLEYEAR_START_DATE.length);
       
  1155             }
       
  1156 
       
  1157             for (int j = sYearInCycle + 1; j < CYCLEYEAR_START_DATE.length; j++) {
       
  1158                 startCycles[j] = startCycles[j] - offset;
       
  1159             }
       
  1160 
       
  1161             // System.out.println(sCycleNumber + ":" + sYearInCycle);
       
  1162             ADJUSTED_CYCLE_YEARS.put(sCycleNumber, startCycles);
       
  1163 
       
  1164             int sYearInMaxY = (startYear - 1) / 30;
       
  1165             int sEndInMaxY = (endYear - 1) / 30;
       
  1166 
       
  1167             if (sYearInMaxY != sEndInMaxY) {
       
  1168                 // System.out.println("over 30");
       
  1169                 // Adjusting starting 30 * MAX_ADJUSTED_CYCLE year cycle.
       
  1170                 // System.out.println(sYearInMaxY);
       
  1171 
       
  1172                 for (int j = sYearInMaxY + 1; j < ADJUSTED_CYCLES.length; j++) {
       
  1173                     ADJUSTED_CYCLES[j] = ADJUSTED_CYCLES[j] - offset;
       
  1174                 }
       
  1175 
       
  1176                 // Adjusting ending 30 * MAX_ADJUSTED_CYCLE year cycles.
       
  1177                 for (int j = sEndInMaxY + 1; j < ADJUSTED_CYCLES.length; j++) {
       
  1178                     ADJUSTED_CYCLES[j] = ADJUSTED_CYCLES[j] + offset;
       
  1179                 }
       
  1180             }
       
  1181 
       
  1182             // Adjusting ending 30 year cycle.
       
  1183             int eCycleNumber = (endYear - 1) / 30;
       
  1184             int sEndInCycle = (endYear - 1) % 30; // 0-based.
       
  1185             int[] endCycles = ADJUSTED_CYCLE_YEARS.get(eCycleNumber);
       
  1186             if (endCycles == null) {
       
  1187                 endCycles = Arrays.copyOf(CYCLEYEAR_START_DATE, CYCLEYEAR_START_DATE.length);
       
  1188             }
       
  1189             for (int j = sEndInCycle + 1; j < CYCLEYEAR_START_DATE.length; j++) {
       
  1190                 endCycles[j] = endCycles[j] + offset;
       
  1191             }
       
  1192             ADJUSTED_CYCLE_YEARS.put(eCycleNumber, endCycles);
       
  1193         }
       
  1194 
       
  1195         // Adjusting ending year.
       
  1196         boolean isEndYLeap = isLeapYear0(endYear);
       
  1197 
       
  1198         int[] orgEndMonthDays = ADJUSTED_MONTH_DAYS.get(endYear);
       
  1199 
       
  1200         if (orgEndMonthDays == null) {
       
  1201             if (isEndYLeap) {
       
  1202                 orgEndMonthDays = Arrays.copyOf(LEAP_NUM_DAYS, LEAP_NUM_DAYS.length);
       
  1203             } else {
       
  1204                 orgEndMonthDays = Arrays.copyOf(NUM_DAYS, NUM_DAYS.length);
       
  1205             }
       
  1206         }
       
  1207 
       
  1208         int[] newEndMonthDays = new int[orgEndMonthDays.length];
       
  1209 
       
  1210         for (int month = 0; month < 12; month++) {
       
  1211             if (month > endMonth) {
       
  1212                 newEndMonthDays[month] = orgEndMonthDays[month] + offset;
       
  1213             } else {
       
  1214                 newEndMonthDays[month] = orgEndMonthDays[month];
       
  1215             }
       
  1216         }
       
  1217 
       
  1218         ADJUSTED_MONTH_DAYS.put(endYear, newEndMonthDays);
       
  1219 
       
  1220         // Adjusting the days of month.
       
  1221         int[] orgEndMonthLengths = ADJUSTED_MONTH_LENGTHS.get(endYear);
       
  1222 
       
  1223         if (orgEndMonthLengths == null) {
       
  1224             if (isEndYLeap) {
       
  1225                 orgEndMonthLengths = Arrays.copyOf(LEAP_MONTH_LENGTH, LEAP_MONTH_LENGTH.length);
       
  1226             } else {
       
  1227                 orgEndMonthLengths = Arrays.copyOf(MONTH_LENGTH, MONTH_LENGTH.length);
       
  1228             }
       
  1229         }
       
  1230 
       
  1231         int[] newEndMonthLengths = new int[orgEndMonthLengths.length];
       
  1232 
       
  1233         for (int month = 0; month < 12; month++) {
       
  1234             if (month == endMonth) {
       
  1235                 newEndMonthLengths[month] = orgEndMonthLengths[month] + offset;
       
  1236             } else {
       
  1237                 newEndMonthLengths[month] = orgEndMonthLengths[month];
       
  1238             }
       
  1239         }
       
  1240 
       
  1241         ADJUSTED_MONTH_LENGTHS.put(endYear, newEndMonthLengths);
       
  1242 
       
  1243         int[] startMonthLengths = ADJUSTED_MONTH_LENGTHS.get(startYear);
       
  1244         int[] endMonthLengths = ADJUSTED_MONTH_LENGTHS.get(endYear);
       
  1245         int[] startMonthDays = ADJUSTED_MONTH_DAYS.get(startYear);
       
  1246         int[] endMonthDays = ADJUSTED_MONTH_DAYS.get(endYear);
       
  1247 
       
  1248         int startMonthLength = startMonthLengths[startMonth];
       
  1249         int endMonthLength = endMonthLengths[endMonth];
       
  1250         int startMonthDay = startMonthDays[11] + startMonthLengths[11];
       
  1251         int endMonthDay = endMonthDays[11] + endMonthLengths[11];
       
  1252 
       
  1253         int maxMonthLength = ADJUSTED_MAX_VALUES[POSITION_DAY_OF_MONTH];
       
  1254         int leastMaxMonthLength = ADJUSTED_LEAST_MAX_VALUES[POSITION_DAY_OF_MONTH];
       
  1255 
       
  1256         if (maxMonthLength < startMonthLength) {
       
  1257             maxMonthLength = startMonthLength;
       
  1258         }
       
  1259         if (maxMonthLength < endMonthLength) {
       
  1260             maxMonthLength = endMonthLength;
       
  1261         }
       
  1262         ADJUSTED_MAX_VALUES[POSITION_DAY_OF_MONTH] = maxMonthLength;
       
  1263 
       
  1264         if (leastMaxMonthLength > startMonthLength) {
       
  1265             leastMaxMonthLength = startMonthLength;
       
  1266         }
       
  1267         if (leastMaxMonthLength > endMonthLength) {
       
  1268             leastMaxMonthLength = endMonthLength;
       
  1269         }
       
  1270         ADJUSTED_LEAST_MAX_VALUES[POSITION_DAY_OF_MONTH] = leastMaxMonthLength;
       
  1271 
       
  1272         int maxMonthDay = ADJUSTED_MAX_VALUES[POSITION_DAY_OF_YEAR];
       
  1273         int leastMaxMonthDay = ADJUSTED_LEAST_MAX_VALUES[POSITION_DAY_OF_YEAR];
       
  1274 
       
  1275         if (maxMonthDay < startMonthDay) {
       
  1276             maxMonthDay = startMonthDay;
       
  1277         }
       
  1278         if (maxMonthDay < endMonthDay) {
       
  1279             maxMonthDay = endMonthDay;
       
  1280         }
       
  1281 
       
  1282         ADJUSTED_MAX_VALUES[POSITION_DAY_OF_YEAR] = maxMonthDay;
       
  1283 
       
  1284         if (leastMaxMonthDay > startMonthDay) {
       
  1285             leastMaxMonthDay = startMonthDay;
       
  1286         }
       
  1287         if (leastMaxMonthDay > endMonthDay) {
       
  1288             leastMaxMonthDay = endMonthDay;
       
  1289         }
       
  1290         ADJUSTED_LEAST_MAX_VALUES[POSITION_DAY_OF_YEAR] = leastMaxMonthDay;
       
  1291     }
       
  1292 
       
  1293     /**
       
  1294      * Package private Entry for suppling deviations from the Reader.
       
  1295      * Each entry consists of a range using the Hijrah calendar,
       
  1296      * start year, month, end year, end month, and an offset.
       
  1297      * The offset is used to modify the length of the month +2, +1, -1, -2.
       
  1298      */
       
  1299     static final class Deviation {
       
  1300 
       
  1301         Deviation(int startYear, int startMonth, int endYear, int endMonth, int offset) {
       
  1302             this.startYear = startYear;
       
  1303             this.startMonth = startMonth;
       
  1304             this.endYear = endYear;
       
  1305             this.endMonth = endMonth;
       
  1306             this.offset = offset;
       
  1307         }
       
  1308 
       
  1309         final int startYear;
       
  1310         final int startMonth;
       
  1311         final int endYear;
       
  1312         final int endMonth;
       
  1313         final int offset;
       
  1314 
       
  1315         int getStartYear() {
       
  1316             return startYear;
       
  1317         }
       
  1318 
       
  1319         int getStartMonth() {
       
  1320             return startMonth;
       
  1321         }
       
  1322 
       
  1323         int getEndYear() {
       
  1324             return endYear;
       
  1325         }
       
  1326 
       
  1327         int getEndMonth() {
       
  1328             return endMonth;
       
  1329         }
       
  1330 
       
  1331         int getOffset() {
       
  1332             return offset;
       
  1333         }
       
  1334 
       
  1335         @Override
       
  1336         public String toString() {
       
  1337             return String.format("[year: %4d, month: %2d, offset: %+d]", startYear, startMonth, offset);
       
  1338         }
       
  1339     }
       
  1340 
       
  1341 }