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 }