]> granicus.if.org Git - icu/commitdiff
ICU-8449 Add Um al-Qura Hijri Calendar Support
authorScott Russell <DTownSMR@gmail.com>
Fri, 16 Aug 2013 16:04:37 +0000 (16:04 +0000)
committerScott Russell <DTownSMR@gmail.com>
Fri, 16 Aug 2013 16:04:37 +0000 (16:04 +0000)
X-SVN-Rev: 34048

icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java
icu4j/main/classes/core/src/com/ibm/icu/util/IslamicCalendar.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/IslamicTest.java

index 6288e60f99cb5f8054e6eb1b7a1b5680f09451d5..891e763fbda8da909be42a813ce1bed3b25deb0e 100644 (file)
@@ -25,6 +25,7 @@ import com.ibm.icu.text.DateFormat;
 import com.ibm.icu.text.DateFormatSymbols;
 import com.ibm.icu.text.MessageFormat;
 import com.ibm.icu.text.SimpleDateFormat;
+import com.ibm.icu.util.IslamicCalendar.CalculationType;
 import com.ibm.icu.util.ULocale.Category;
 
 /**
@@ -1882,9 +1883,13 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
         case CALTYPE_ISLAMIC_CIVIL:
             cal = new IslamicCalendar(zone, locale);
             break;
+        case CALTYPE_ISLAMIC_UMALQURA :
+               cal=new IslamicCalendar (zone,locale);
+               ((IslamicCalendar)cal).setType(CalculationType.ISLAMIC_UMALQURA);
+               break;
         case CALTYPE_ISLAMIC:
             cal = new IslamicCalendar(zone, locale);
-            ((IslamicCalendar)cal).setCivil(false);
+            ((IslamicCalendar)cal).setType(CalculationType.ISLAMIC);
             break;
         case CALTYPE_HEBREW:
             cal = new HebrewCalendar(zone, locale);
@@ -1914,10 +1919,9 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
             cal.setFirstDayOfWeek(MONDAY);
             cal.setMinimalDaysInFirstWeek(4);
             break;
-        case CALTYPE_ISLAMIC_UMALQURA:
         case CALTYPE_ISLAMIC_TBLA:
         case CALTYPE_ISLAMIC_RGSA:
-            // Need to add handling for these, meanwhile fall through to default
+         // Need to add handling for these, meanwhile fall through to default
         default:
             // we must not get here, because unknown type is mapped to
             // Gregorian at the beginning of this method.
index 250c72c0149298c651ab87d5933e4a3ae17d5a4e..3ea4f032e9f31249ee6dd0165b84e231cd24bff8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2012, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2013, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -33,7 +33,7 @@ import com.ibm.icu.util.ULocale.Category;
  * we treat days and months as beginning at midnight,
  * roughly 6 hours after the corresponding sunset.
  * <p>
- * There are two main variants of the Islamic calendar in existence.  The first
+ * There are three main variants of the Islamic calendar in existence.  The first
  * is the <em>civil</em> calendar, which uses a fixed cycle of alternating 29-
  * and 30-day months, with a leap day added to the last month of 11 out of
  * every 30 years.  This calendar is easily calculated and thus predictable in
@@ -41,8 +41,9 @@ import com.ibm.icu.util.ULocale.Category;
  * This is the default behavior of a newly-created <code>IslamicCalendar</code>
  * object.
  * <p>
- * The Islamic <em>religious</em> calendar, however, is based on the <em>observation</em>
- * of the crescent moon.  It is thus affected by the position at which the
+ * The Islamic <em>religious</em> calendar and Saudi Arabia's <em>Umm al-Qura</em> 
+ * calendar, however, are based on the <em>observation</em> of the crescent moon.  
+ * It is thus affected by the position at which the
  * observations are made, seasonal variations in the time of sunset, the
  * eccentricities of the moon's orbit, and even the weather at the observation
  * site.  This makes it impossible to calculate in advance, and it causes the
@@ -53,20 +54,26 @@ import com.ibm.icu.util.ULocale.Category;
  * moon's illumination, and other factors, it is possible to determine the start
  * of a lunar month with a fairly high degree of certainty.  However, these
  * calculations are extremely complicated and thus slow, so most algorithms,
- * including the one used here, are only approximations of the true astronical
+ * including the one used here, are only approximations of the true astronomical
  * calculations.  At present, the approximations used in this class are fairly
  * simplistic; they will be improved in later versions of the code.
  * <p>
- * The {@link #setCivil setCivil} method determines
+ * Like the Islamic religious calendar, <em>Umm al-Qura</em> is also based 
+ * on the sighting method of the crescent moon but is standardized by Saudi Arabia.
+ * <p>  
+ * The {@link #setType setType} method determines
  * which approach is used to determine the start of a month.  By default, the
- * fixed-cycle civil calendar is used.  However, if <code>setCivil(false)</code>
+ * fixed-cycle <em>civil</em> calendar is used.  However, if <code>setType(ISLAMIC)</code>
  * is called, an approximation of the true lunar calendar will be used.
+ * Similarly, if <code>setType(ISLAMIC_UMALQURA)</code> is called, an approximation 
+ * of the Umm al-Qura lunar calendar will be used.
  * <p>
  * This class should not be subclassed.</p>
  * <p>
  * IslamicCalendar usually should be instantiated using 
  * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
- * with the tag <code>"@calendar=islamic"</code> or <code>"@calendar=islamic-civil"</code>.</p>
+ * with the tag <code>"@calendar=islamic"</code> or <code>"@calendar=islamic-civil"</code> 
+ * or <code>"@calendar=islamic-umalqura"</code>.</p>
  *
  * @see com.ibm.icu.util.GregorianCalendar
  * @see com.ibm.icu.util.Calendar
@@ -306,13 +313,21 @@ public class IslamicCalendar extends Calendar {
      *                  <code>false</code> to use the astronomical calendar.
      * @stable ICU 2.8
      */
+    @Deprecated
     public void setCivil(boolean beCivil)
     {
-        if (civil != beCivil) {
+        if (beCivil && cType != CalculationType.ISLAMIC_CIVIL) {
             // The fields of the calendar will become invalid, because the calendar
             // rules are different
             long m = getTimeInMillis();
-            civil = beCivil;
+            cType = CalculationType.ISLAMIC_CIVIL;
+            clear();
+            setTimeInMillis(m);
+        }else if(!beCivil && cType != CalculationType.ISLAMIC){
+                // The fields of the calendar will become invalid, because the calendar
+            // rules are different
+            long m = getTimeInMillis();
+            cType = CalculationType.ISLAMIC;
             clear();
             setTimeInMillis(m);
         }
@@ -323,9 +338,14 @@ public class IslamicCalendar extends Calendar {
      * calendar, or <code>false</code> if using the religious, astronomical
      * calendar.
      * @stable ICU 2.8
+     * 
      */
+    @Deprecated
     public boolean isCivil() {
-        return civil;
+       if(cType == CalculationType.ISLAMIC_CIVIL){
+               return true;
+       }
+        return false;
     }
     
     //-------------------------------------------------------------------------
@@ -361,7 +381,83 @@ public class IslamicCalendar extends Calendar {
         {/*                                   */}, // JULIAN_DAY
         {/*                                   */}, // MILLISECONDS_IN_DAY
     };
-
+    
+    /*
+     * bit map array where a bit turned on represents a month with 30 days. 
+     */
+    private static final int[] UMALQURA_MONTHLENGTH ={
+        //* 1318 -1322 */ "0101 0111 0100", "1001 0111 0110", "0100 1011 0111", "0010 0101 0111", "0101 0010 1011",
+                               0x0574,           0x0975,           0x06A7,           0x0257,           0x052B,            
+        //* 1323 -1327 */ "0110 1001 0101", "0110 1100 1010", "1010 1101 0101", "0101 0101 1011", "0010 0101 1101",                 
+                               0x0695,           0x06CA,           0x0AD5,           0x055B,           0x025B,                  
+        //* 1328 -1332 */ "1001 0010 1101", "1100 1001 0101", "1101 0100 1010", "1110 1010 0101", "0110 1101 0010",                 
+                               0x092D,           0x0C95,           0x0D4A,           0x0E5B,           0x025B,                  
+        //* 1333 -1337 */ "1010 1101 0101", "0101 0101 1010", "1010 1010 1011", "0100 0100 1011", "0110 1010 0101",                 
+                               0x0AD5,           0x055A,           0x0AAB,           0x044B,           0x06A5,                  
+        //* 1338 -1342 */ "0111 0101 0010", "1011 1010 1001", "0011 0111 0100", "1010 1011 0110", "0101 0101 0110",                  
+                               0x0752,           0x0BA9,           0x0374,           0x0AB6,           0x0556,                  
+        //* 1343 -1347 */ "1010 1010 1010", "1101 0101 0010", "1101 1010 1001", "0101 1101 0100", "1010 1110 1010", 
+                               0x0AAA,           0x0D52,           0x0DA9,           0x05D4,           0x0AEA,                  
+        //* 1348 -1352 */ "0100 1101 1101", "0010 0110 1110", "1001 0010 1110", "1010 1010 0110", "1101 0101 0100", 
+                               0x04DD,           0x026E,           0x092E,           0x0AA6,           0x0D54,                  
+        //* 1353 -1357 */ "0101 1010 1010", "0101 1011 0101", "0010 1011 0100", "1001 0011 0111", "0100 1001 1011", 
+                               0x05AA,           0x05B5,           0x02B4,           0x0937,           0x049B,                  
+        //* 1358 -1362 */ "1010 0100 1011", "1011 0010 0101", "1011 0101 0100", "1011 0110 1010", "0101 0110 1101", 
+                               0x0A4B,           0x0B25,           0x0B54,           0x0B6A,           0x056D,                  
+        //* 1363 -1367 */ "0100 1010 1101", "1010 0101 0101", "1101 0010 0101", "1110 1001 0010", "1110 1100 1001", 
+                               0x04AD,           0x0A55,           0x0D25,           0x0E92,           0x0EC9,                  
+        //* 1368 -1372 */ "0110 1101 0100", "1010 1110 1010", "0101 0110 1011", "0100 1010 1011", "0110 1000 0101", 
+                               0x06D4,           0x0ADA,           0x056B,           0x04AB,           0x0685,                  
+        //* 1373 -1377 */ "1011 0100 1001", "1011 1010 0100", "1011 1011 0010", "0101 1011 0101", "0010 1011 1010", 
+                               0x0B49,           0x0BA4,           0x0BB2,           0x05B5,           0x02BA,                  
+        //* 1378 -1382 */ "1001 0101 1011", "0100 1010 1011", "0101 0101 0101", "0110 1011 0010", "0110 1101 1001", 
+                               0x095B,           0x04AB,           0x0555,           0x06B2,           0x06D9,                  
+        //* 1383 -1387 */ "0010 1110 1100", "1001 0110 1110", "0100 1010 1110", "1010 0101 0110", "1101 0010 1010", 
+                               0x02EC,           0x096E,           0x04AE,           0x0A56,           0x0D2A,                  
+        //* 1388 -1392 */ "1101 0101 0101", "0101 1010 1010", "1010 1011 0101", "0100 1011 1011", "0000 0101 1011", 
+                               0x0D55,           0x05AA,           0x0AB5,           0x04BB,           0x005B,                  
+        //* 1393 -1397 */ "1001 0010 1011", "1010 1001 0101", "0011 0100 1010", "1011 1010 0101", "0101 1010 1010", 
+                               0x092B,           0x0A95,           0x034A,           0x0BA5,           0x05AA,                  
+        //* 1398 -1402 */ "1010 1011 0101", "0101 0101 0110", "1010 1001 0110", "1101 0100 1010", "1110 1010 0101", 
+                               0x0AB5,           0x0556,           0x0A96,           0x0B4A,           0x0EA5,                  
+        //* 1403 -1407 */ "0111 0101 0010", "0110 1110 1001", "0011 0110 1010", "1010 1010 1101", "0101 0101 0101", 
+                               0x0752,           0x06E9,           0x036A,           0x0AAD,           0x0555,                  
+        //* 1408 -1412 */ "1010 1010 0101", "1011 0101 0010", "1011 1010 1001", "0101 1011 0100", "1001 1011 1010", 
+                               0x0AA5,           0x0B52,           0x0BA9,           0x05B4,           0x09BA,                  
+        //* 1413 -1417 */ "0100 1101 1011", "0010 0101 1101", "0101 0010 1101", "1010 1010 0101", "1010 1101 0100", 
+                               0x04DB,           0x025D,           0x052D,           0x0AA5,           0x0AD4,              
+        //* 1418 -1422 */ "1010 1110 1010", "0101 0110 1101", "0100 1011 1101", "0010 0011 1101", "1001 0001 1101", 
+                               0x0AEA,           0x056D,           0x04BD,           0x023D,           0x091D,                  
+        //* 1423 -1427 */ "1010 1001 0101", "1011 0100 1010", "1011 0101 1010", "0101 0110 1101", "0010 1011 0110", 
+                               0x0A95,           0x0B4A,           0x0B5A,           0x056D,           0x02B6,                  
+        //* 1428 -1432 */ "1001 0011 1011", "0100 1001 1011", "0110 0101 0101", "0110 1010 1001", "0111 0101 0100", 
+                               0x093B,           0x049B,           0x0655,           0x06A9,           0x0754,                  
+        //* 1433 -1437 */ "1011 0110 1010", "0101 0110 1100", "1010 1010 1101", "0101 0101 0101", "1011 0010 1001", 
+                               0x0B6A,           0x056C,           0x0AAD,           0x0555,           0x0B29,                  
+        //* 1438 -1442 */ "1011 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", "0101 0101 1010", 
+                               0x0B92,           0x0BA9,           0x05D4,           0x0ADA,           0x055A,                  
+        //* 1443 -1447 */ "1010 1010 1011", "0101 1001 0101", "0111 0100 1001", "0111 0110 0100", "1011 1010 1010", 
+                               0x0AAB,           0x0595,           0x0749,           0x0764,           0x0BAA,                  
+        //* 1448 -1452 */ "0101 1011 0101", "0010 1011 0110", "1010 0101 0110", "1110 0100 1101", "1011 0010 0101",
+                               0x05B5,           0x02B6,           0x0A56,           0x0E4D,           0x0B25,                  
+        //* 1453 -1457 */ "1011 0101 0010", "1011 0110 1010", "0101 1010 1101", "0010 1010 1110", "1001 0010 1111",
+                               0x0B52,           0x0B6A,           0x05AD,           0x02AE,           0x092F,                  
+        //* 1458 -1462 */ "0100 1001 0111", "0110 0100 1011", "0110 1010 0101", "0110 1010 1100", "1010 1101 0110",
+                               0x0497,           0x064B,           0x06A5,           0x06AC,           0x0AD6,                  
+        //* 1463 -1467 */ "0101 0101 1101", "0100 1001 1101", "1010 0100 1101", "1101 0001 0110", "1101 1001 0101",
+                               0x055D,           0x049D,           0x0A4D,           0x0D16,           0x0D95,                  
+        //* 1468 -1472 */ "0101 1010 1010", "0101 1011 0101", "0010 1001 1010", "1001 0101 1011", "0100 1010 1100",
+                               0x05AA,           0x05B5,           0x029A,           0x095B,           0x04AC,                  
+        //* 1473 -1477 */ "0101 1001 0101", "0110 1100 1010", "0110 1110 0100", "1010 1110 1010", "0100 1111 0101",
+                               0x0595,           0x06CA,           0x06E4,           0x0AEA,           0x04F5,                  
+        //* 1478 -1480 */ "0010 1011 0110", "1001 0101 0110", "1010 1010 1010"   
+                               0x02B6,           0x0956,           0x0AAA                  
+    };
+    
+    private static final int UMALQURA_YEAR_START = 1318;
+    private static final int UMALQURA_YEAR_END = 1480;
+    
+  
     /**
      * @stable ICU 2.8
      */
@@ -405,11 +501,20 @@ public class IslamicCalendar extends Calendar {
      * from the Hijri epoch, origin 0.
      */
     private long yearStart(int year) {
-        if (civil) {
-            return (year-1)*354 + (long)Math.floor((3+11*year)/30.0);
-        } else {
-            return trueMonthStart(12*(year-1));
-        }
+       long ys = 0;
+        if (cType == CalculationType.ISLAMIC_CIVIL
+                       || (cType == CalculationType.ISLAMIC_UMALQURA && year < UMALQURA_YEAR_START )) {
+            ys = (year-1)*354 + (long)Math.floor((3+11*year)/30.0);
+        }else if(cType == CalculationType.ISLAMIC){
+            ys = trueMonthStart(12*(year-1));
+        }else if(cType == CalculationType.ISLAMIC_UMALQURA){
+               ys = yearStart(UMALQURA_YEAR_START -1);  
+               ys += handleGetYearLength(UMALQURA_YEAR_START -1);
+               for(int i=UMALQURA_YEAR_START; i< year; i++){  
+                       ys+= handleGetYearLength(i);
+               }
+        }      
+       return ys;
     }
     
     /**
@@ -424,12 +529,20 @@ public class IslamicCalendar extends Calendar {
         // in the case of an add operation
         int realYear = year + month / 12;
         int realMonth = month % 12;
-        if (civil) {
-            return (long)Math.ceil(29.5*realMonth)
+        long ms = 0;
+        if (cType == CalculationType.ISLAMIC_CIVIL
+                       || (cType == CalculationType.ISLAMIC_UMALQURA && year < UMALQURA_YEAR_START )) {
+            ms = (long)Math.ceil(29.5*realMonth)
                     + (realYear-1)*354 + (long)Math.floor((3+11*realYear)/30.0);
-        } else {
-            return trueMonthStart(12*(realYear-1) + realMonth);
+        }else if(cType == CalculationType.ISLAMIC){
+            ms = trueMonthStart(12*(realYear-1) + realMonth);
+        }else if(cType == CalculationType.ISLAMIC_UMALQURA){
+               ms = yearStart(year);
+               for(int i=0; i< month; i++){
+                       ms+= handleGetMonthLength(year, i);
+               }
         }
+        return ms;
     }
     
     /**
@@ -515,8 +628,11 @@ public class IslamicCalendar extends Calendar {
      * astronomical calculations for the time of the new moon.
      *
      * @serial
+     * @deprecated
      */
-    private boolean civil = true;
+   private boolean civil = true;
+    
+    private CalculationType cType = CalculationType.ISLAMIC_CIVIL;
 
     //----------------------------------------------------------------------
     // Calendar framework
@@ -533,14 +649,22 @@ public class IslamicCalendar extends Calendar {
 
         int length = 0;
         
-        if (civil) {
+        if (cType == CalculationType.ISLAMIC_CIVIL 
+                       || (cType == CalculationType.ISLAMIC_UMALQURA && (extendedYear < UMALQURA_YEAR_START  || extendedYear > UMALQURA_YEAR_END) )) {
             length = 29 + (month+1) % 2;
             if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) {
                 length++;
             }
-        } else {
+        } else if (cType == CalculationType.ISLAMIC){
             month = 12*(extendedYear-1) + month;
             length = (int)( trueMonthStart(month+1) - trueMonthStart(month) );
+        }else if (cType == CalculationType.ISLAMIC_UMALQURA){            
+            int idx = (extendedYear - UMALQURA_YEAR_START);     // calculate year offset into bit map array
+            int mask = (int) (0x01 << (11 - month));            // set mask for bit corresponding to month            
+            if((UMALQURA_MONTHLENGTH[idx] & mask) == 0 )    
+                       return 29;
+               else
+                       return 30;
         }
         return length;
     }
@@ -550,12 +674,18 @@ public class IslamicCalendar extends Calendar {
      * @stable ICU 2.8
      */
     protected int handleGetYearLength(int extendedYear) {
-        if (civil) {
-            return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
-        } else {
+       int length =0; 
+        if (cType == CalculationType.ISLAMIC_CIVIL
+                       || (cType == CalculationType.ISLAMIC_UMALQURA && (extendedYear < UMALQURA_YEAR_START  || extendedYear > UMALQURA_YEAR_END) )) {
+            length =  354 + (civilLeapYear(extendedYear) ? 1 : 0);
+        } else if (cType == CalculationType.ISLAMIC){
             int month = 12*(extendedYear-1);
-            return (int)(trueMonthStart(month + 12) - trueMonthStart(month));
+            length =  (int)(trueMonthStart(month + 12) - trueMonthStart(month));
+        }else if (cType == CalculationType.ISLAMIC_UMALQURA){
+               for(int i=0; i<12; i++)
+                       length += handleGetMonthLength(extendedYear, i);
         }
+        return length;
     }
     
     //-------------------------------------------------------------------------
@@ -604,16 +734,16 @@ public class IslamicCalendar extends Calendar {
      * @stable ICU 2.8
      */
     protected void handleComputeFields(int julianDay) {
-        int year, month, dayOfMonth, dayOfYear;
-        long monthStart;
+       int year =0, month=0, dayOfMonth=0, dayOfYear=0;
+       long monthStart;
         long days = julianDay - 1948440;
 
-        if (civil) {
+        if (cType == CalculationType.ISLAMIC_CIVIL) {
             // Use the civil calendar approximation, which is just arithmetic
             year  = (int)Math.floor( (30 * days + 10646) / 10631.0 );
             month = (int)Math.ceil((days - 29 - yearStart(year)) / 29.5 );
             month = Math.min(month, 11);
-        } else {
+        } else if (cType == CalculationType.ISLAMIC){
             // Guess at the number of elapsed full months since the epoch
             int months = (int)Math.floor(days / CalendarAstronomer.SYNODIC_MONTH);
 
@@ -633,12 +763,44 @@ public class IslamicCalendar extends Calendar {
 
             year = months / 12 + 1;
             month = months % 12;
+        } else if (cType == CalculationType.ISLAMIC_UMALQURA){         
+               long umalquraStartdays = yearStart(UMALQURA_YEAR_START)  ; 
+               if( days < umalquraStartdays){
+                       //Use Civil calculation
+                       year  = (int)Math.floor( (30 * days + 10646) / 10631.0 );
+                month = (int)Math.ceil((days - 29 - yearStart(year)) / 29.5 );
+                month = Math.min(month, 11);
+               }else{
+                       int y =UMALQURA_YEAR_START-1, m =0;
+                       long d = 1;
+                       while(d > 0){ 
+                               y++; 
+                               d = days - yearStart(y) +1;
+                               if(d == handleGetYearLength(y)){
+                                       m=11;
+                                       break;
+                               }else if(d < handleGetYearLength(y) ){
+                                       int monthLen = handleGetMonthLength(y, m); 
+                                       m=0;
+                                       while(d > monthLen){
+                                               d -= monthLen;
+                                               m++;
+                                               monthLen = handleGetMonthLength(y, m);                                                  
+                                       }
+                                       break;
+                               }                                                       
+                       }                       
+                       year = y;
+                               month = m;
+               }
         }
 
+       
         dayOfMonth = (int)(days - monthStart(year, month)) + 1;
 
         // Now figure out the day of the year.
         dayOfYear = (int)(days - monthStart(year, 0) + 1);
+        
 
         internalSet(ERA, 0);
         internalSet(YEAR, year);
@@ -647,16 +809,28 @@ public class IslamicCalendar extends Calendar {
         internalSet(DAY_OF_MONTH, dayOfMonth);
         internalSet(DAY_OF_YEAR, dayOfYear);       
     }    
+    
+    public enum CalculationType {ISLAMIC, ISLAMIC_CIVIL, ISLAMIC_UMALQURA};
+    
+    /**
+     * sets the calculation type for this calendar.
+     * @see #cType
+     */
+    public void setType(CalculationType type){
+       cType = type;           
+    }
 
     /**
      * {@inheritDoc}
      * @stable ICU 3.8
      */
     public String getType() {
-        if(civil) {
+        if(cType == CalculationType.ISLAMIC_CIVIL) {
             return "islamic-civil";
-        } else {
+        } else if (cType == CalculationType.ISLAMIC){
             return "islamic";
+        }else {
+               return "islamic-umalqura";
         }
     }
 
index 2e5c0f5d86b14e0c8df17df42c5f20a412505355..6b88b51398787448d0f8e1cb813bfbcb49192b13 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 1996-2012, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2013, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -11,8 +11,10 @@ import java.util.Locale;
 
 import com.ibm.icu.impl.LocaleUtility;
 import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.IslamicCalendar;
+import com.ibm.icu.util.IslamicCalendar.CalculationType;
 import com.ibm.icu.util.TimeZone;
 import com.ibm.icu.util.ULocale;
 
@@ -58,7 +60,9 @@ public class IslamicTest extends CalendarTest {
         };
        
         IslamicCalendar cal = newCivil();
-
+        doRollAdd(ROLL, cal, tests);
+        
+        cal = newIslamicUmalqura();
         doRollAdd(ROLL, cal, tests);
     }
 
@@ -149,7 +153,7 @@ public class IslamicTest extends CalendarTest {
         // rounding errors after year AH3954 - about 2500 years out.
 
         IslamicCalendar islamic2 = new IslamicCalendar();
-        islamic2.setCivil(false);
+        islamic2.setType(CalculationType.ISLAMIC);
         int testTime = getInclusion() <= 5 ? 20000 : 800000;
         doLimitsTest(islamic2, null, cal.getTime(), testTime);
         doTheoreticalLimitsTest(islamic2, true);
@@ -226,6 +230,14 @@ public class IslamicTest extends CalendarTest {
         errln("islamic calendar is civil");
         }
 
+        // since setCivil/isCivil are now deprecated, make sure same test works for setType
+        // operations on non-civil calendar
+        cal = new IslamicCalendar(800, IslamicCalendar.RAMADAN, 1, 1, 1, 1);
+        cal.setType(CalculationType.ISLAMIC);
+        if (cal.isCivil()) {
+        errln("islamic calendar is civil");
+        }
+
         Date now = new Date();
         cal.setTime(now);
 
@@ -273,12 +285,18 @@ public class IslamicTest extends CalendarTest {
 
     private static IslamicCalendar newCivil() {
         IslamicCalendar civilCalendar = new IslamicCalendar();
-        civilCalendar.setCivil(true);
+        civilCalendar.setType(CalculationType.ISLAMIC_CIVIL);
         return civilCalendar;
     }
     private static IslamicCalendar newIslamic() {
         IslamicCalendar civilCalendar = new IslamicCalendar();
-        civilCalendar.setCivil(false);
+        civilCalendar.setType(CalculationType.ISLAMIC);
+        return civilCalendar;
+    }
+    
+    private static IslamicCalendar newIslamicUmalqura() {
+        IslamicCalendar civilCalendar = new IslamicCalendar();
+        civilCalendar.setType(CalculationType.ISLAMIC_UMALQURA);
         return civilCalendar;
     }
 
@@ -292,5 +310,122 @@ public class IslamicTest extends CalendarTest {
     public void Test8822() {
         verifyType(newIslamic(),"islamic");
         verifyType(newCivil(),"islamic-civil");
+        verifyType(newIslamicUmalqura(), "islamic-umalqura");
+    } 
+    
+    private void setAndTestCalendar(IslamicCalendar cal, int initMonth, int initDay, int initYear) {
+        cal.clear();
+        cal.setLenient(false);
+        cal.set(initYear, initMonth, initDay);
+        int day = cal.get(Calendar.DAY_OF_MONTH);
+        int month = cal.get(Calendar.MONTH);
+        int year = cal.get(Calendar.YEAR);
+        if(initDay != day || initMonth != month || initYear != year)
+        {
+            errln("year init values:\tmonth "+initMonth+"\tday "+initDay+"\tyear "+initYear);
+            errln("values post set():\tmonth "+month+"\tday "+day+"\tyear "+year);
+        }
+    }
+
+    private void setAndTestWholeYear(IslamicCalendar cal, int startYear) {
+        for(int startMonth = 0; startMonth < 12; startMonth++) {
+            for(int startDay = 1; startDay < 31; startDay++ ) {                
+                try {
+                    setAndTestCalendar(cal, startMonth, startDay, startYear);
+                } catch(IllegalArgumentException iae) {
+                    if(startDay != 30) {
+                        errln("unexpected exception that wasn't for trying to set a date to '30'. errmsg - " + iae.getLocalizedMessage());
+                    }                    
+                }                
+            }
+        }
     }
+    
+    
+    public void Test8449() {
+        int firstYear = 1318;
+        //*  use either 1 or 2 leading slashes to toggle
+        int lastYear = 1368;    // just enough to be pretty sure
+        /*/
+        int lastYear = 1480;    // the whole shootin' match
+        //*/
+        
+        IslamicCalendar tstCal = newIslamicUmalqura();
+        tstCal.clear();
+        tstCal.setLenient(false);
+        
+        int day=0, month=0, year=0, initDay = 27, initMonth = IslamicCalendar.RAJAB, initYear = 1434;
+
+        try {
+            for( int startYear = firstYear; startYear <= lastYear; startYear++) {
+                setAndTestWholeYear(tstCal, startYear);
+            }
+        } catch(Throwable t) {
+            errln("unexpected exception thrown - message=" +t.getLocalizedMessage());
+        }
+
+        try {
+            initMonth = IslamicCalendar.RABI_2;
+            initDay = 5;
+            int loopCnt = 25;
+            tstCal.clear();
+            setAndTestCalendar( tstCal, initMonth, initDay, initYear);
+            for(int x=1; x<=loopCnt; x++) {
+                day = tstCal.get(Calendar.DAY_OF_MONTH);
+                month = tstCal.get(Calendar.MONTH);
+                year = tstCal.get(Calendar.YEAR);
+                tstCal.roll(Calendar.DAY_OF_MONTH, true);
+            }
+            if(day != (initDay + loopCnt - 1) || month != IslamicCalendar.RABI_2 || year != 1434)
+                errln("invalid values for RABI_2 date after roll of " + loopCnt);
+        } catch(IllegalArgumentException iae) {
+            errln("unexpected exception received!!!");
+        }
+        
+        try {
+            tstCal.clear();
+            initMonth = 2;
+            initDay = 30;
+            setAndTestCalendar( tstCal, initMonth, initDay, initYear);
+            errln("expected exception NOT thrown");
+        } catch(IllegalArgumentException iae) {
+            // expected this
+        }
+        
+        try {
+            tstCal.clear();
+            initMonth = 3;
+            initDay = 30;
+            setAndTestCalendar( tstCal, initMonth, initDay, initYear);
+        } catch(IllegalArgumentException iae) {
+            errln("unexpected exception received!!!");
+        }
+        
+        try {
+            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");            
+            Date date = formatter.parse("1975-05-06");
+            ULocale islamicLoc = new ULocale("ar_SA@calendar=islamic-umalqura"); 
+            IslamicCalendar is_cal = new IslamicCalendar();
+            is_cal.setType(CalculationType.ISLAMIC_UMALQURA);
+            is_cal.setTime(date);
+            SimpleDateFormat formatterIslamic = (SimpleDateFormat) is_cal.getDateTimeFormat(0,0,islamicLoc);
+            formatterIslamic.applyPattern("yyyy-MMMM-dd");
+            String str = formatterIslamic.format(is_cal.getTime());
+
+            // 1395 - Rabi - 29
+            int is_day = is_cal.get(Calendar.DAY_OF_MONTH);
+            int is_month = is_cal.get(Calendar.MONTH);
+            int is_year = is_cal.get(Calendar.YEAR);
+            if(is_day != 29 || is_month != IslamicCalendar.RABI_2 || is_year != 1395)
+                errln("unexpected conversion date: "+is_day+" "+is_month+" "+is_year);
+
+            String expectedFormatResult = "\u0661\u0663\u0669\u0665-\u0631\u0628\u064A\u0639 \u0627\u0644\u0622\u062E\u0631-\u0662\u0669";
+            if(!str.equals(expectedFormatResult))
+                errln("unexpected formatted result: "+str);
+            
+        }catch(Exception e){
+            errln(e.getLocalizedMessage());
+        }
+    }
+    
 }