1/*
2 *******************************************************************************
3 * Copyright (C) 1996-2011, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7
8package com.ibm.icu.util;
9
10import java.util.Date;
11import java.util.Locale;
12
13import com.ibm.icu.util.ULocale.Category;
14
15/**
16 * <code>IndianCalendar</code> is a subclass of <code>GregorianCalendar</code>
17 * that numbers years since the birth of the Buddha.  This is the civil calendar
18 * which is accepted by government of India as Indian National Calendar.
19 * The two calendars most widely used in India today are the Vikrama calendar
20 * followed in North India and the Shalivahana or Saka calendar which is followed
21 * in South India and Maharashtra.
22
23 * A variant of the Shalivahana Calendar was reformed and standardized as the
24 * Indian National calendar in 1957.
25 * <p>
26 * Some details of Indian National Calendar (to be implemented) :
27 * The Months
28 * Month          Length      Start date (Gregorian)
29 * =================================================
30 * 1 Chaitra      30/31          March 22*
31 * 2 Vaisakha     31             April 21
32 * 3 Jyaistha     31             May 22
33 * 4 Asadha       31             June 22
34 * 5 Sravana      31             July 23
35 * 6 Bhadra       31             August 23
36 * 7 Asvina       30             September 23
37 * 8 Kartika      30             October 23
38 * 9 Agrahayana   30             November 22
39 * 10 Pausa       30             December 22
40 * 11 Magha       30             January 21
41 * 12 Phalguna    30             February 20
42
43 * In leap years, Chaitra has 31 days and starts on March 21 instead.
44 * The leap years of Gregorian calendar and Indian National Calendar are in synchornization.
45 * So When its a leap year in Gregorian calendar then Chaitra has 31 days.
46 *
47 * The Years
48 * Years are counted in the Saka Era, which starts its year 0 in 78AD (by gregorian calendar).
49 * So for eg. 9th June 2006 by Gregorian Calendar, is same as 19th of Jyaistha in 1928 of Saka
50 * era by Indian National Calendar.
51 * <p>
52 * The Indian Calendar has only one allowable era: <code>Saka Era</code>.  If the
53 * calendar is not in lenient mode (see <code>setLenient</code>), dates before
54 * 1/1/1 Saka Era are rejected with an <code>IllegalArgumentException</code>.
55 * <p>
56 * This class should not be subclassed.</p>
57 * <p>
58 * IndianCalendar usually should be instantiated using
59 * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
60 * with the tag <code>"@calendar=Indian"</code>.</p>
61 *
62 * @see com.ibm.icu.util.Calendar
63 * @see com.ibm.icu.util.GregorianCalendar
64 *
65 * @stable ICU 3.8
66 */
67public class IndianCalendar extends Calendar {
68    // jdk1.4.2 serialver
69    private static final long serialVersionUID = 3617859668165014834L;
70
71    /**
72     * Constant for Chaitra, the 1st month of the Indian year.
73     * @stable ICU 3.8
74     */
75    public static final int CHAITRA = 0;
76
77    /**
78     * Constant for Vaisakha, the 2nd month of the Indian year.
79     * @stable ICU 3.8
80     */
81    public static final int VAISAKHA = 1;
82
83    /**
84     * Constant for Jyaistha, the 3rd month of the Indian year.
85     * @stable ICU 3.8
86     */
87    public static final int JYAISTHA = 2;
88
89    /**
90     * Constant for Asadha, the 4th month of the Indian year.
91     * @stable ICU 3.8
92     */
93    public static final int ASADHA = 3;
94
95    /**
96     * Constant for Sravana, the 5th month of the Indian year.
97     * @stable ICU 3.8
98     */
99    public static final int SRAVANA = 4 ;
100
101    /**
102     * Constant for Bhadra, the 6th month of the Indian year.
103     * @stable ICU 3.8
104     */
105    public static final int BHADRA = 5 ;
106
107    /**
108     * Constant for Asvina, the 7th month of the Indian year.
109     * @stable ICU 3.8
110     */
111    public static final int ASVINA = 6 ;
112
113    /**
114     * Constant for Kartika, the 8th month of the Indian year.
115     * @stable ICU 3.8
116     */
117    public static final int KARTIKA = 7 ;
118
119    /**
120     * Constant for Agrahayana, the 9th month of the Indian year.
121     * @stable ICU 3.8
122     */
123    public static final int AGRAHAYANA = 8 ;
124
125    /**
126     * Constant for Pausa, the 10th month of the Indian year.
127     * @stable ICU 3.8
128     */
129    public static final int PAUSA = 9 ;
130
131    /**
132     * Constant for Magha, the 11th month of the Indian year.
133     * @stable ICU 3.8
134     */
135    public static final int MAGHA = 10;
136
137    /**
138     * Constant for Phalguna, the 12th month of the Indian year.
139     * @stable ICU 3.8
140     */
141    public static final int PHALGUNA = 11;
142
143    //-------------------------------------------------------------------------
144    // Constructors...
145    //-------------------------------------------------------------------------
146
147    /**
148     * Constant for the Indian Era.  This is the only allowable <code>ERA</code>
149     * value for the Indian calendar.
150     *
151     * @see com.ibm.icu.util.Calendar#ERA
152     * @stable ICU 3.8
153     */
154    public static final int IE = 0;
155
156    /**
157     * Constructs a <code>IndianCalendar</code> using the current time
158     * in the default time zone with the default <code>FORMAT</code> locale.
159     * @see Category#FORMAT
160     * @stable ICU 3.8
161     */
162    public IndianCalendar() {
163       this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
164    }
165
166    /**
167     * Constructs a <code>IndianCalendar</code> based on the current time
168     * in the given time zone with the default <code>FORMAT</code> locale.
169     *
170     * @param zone the given time zone.
171     * @see Category#FORMAT
172     * @stable ICU 3.8
173     */
174    public IndianCalendar(TimeZone zone) {
175       this(zone, ULocale.getDefault(Category.FORMAT));
176    }
177
178    /**
179     * Constructs a <code>IndianCalendar</code> based on the current time
180     * in the default time zone with the given locale.
181     *
182     * @param aLocale the given locale.
183     * @stable ICU 3.8
184     */
185    public IndianCalendar(Locale aLocale) {
186        this(TimeZone.getDefault(), aLocale);
187    }
188
189    /**
190     * Constructs a <code>IndianCalendar</code> based on the current time
191     * in the default time zone with the given locale.
192     *
193     * @param locale the given ulocale.
194     * @stable ICU 3.8
195     */
196    public IndianCalendar(ULocale locale) {
197       this(TimeZone.getDefault(), locale);
198    }
199
200    /**
201     * Constructs a <code>IndianCalendar</code> based on the current time
202     * in the given time zone with the given locale.
203     *
204     * @param zone the given time zone.
205     *
206     * @param aLocale the given locale.
207     * @stable ICU 3.8
208     */
209    public IndianCalendar(TimeZone zone, Locale aLocale) {
210        super(zone, aLocale);
211        setTimeInMillis(System.currentTimeMillis());
212    }
213
214    /**
215     * Constructs a <code>IndianCalendar</code> based on the current time
216     * in the given time zone with the given locale.
217     *
218     * @param zone the given time zone.
219     *
220     * @param locale the given ulocale.
221     * @stable ICU 3.8
222     */
223    public IndianCalendar(TimeZone zone, ULocale locale) {
224        super(zone, locale);
225        setTimeInMillis(System.currentTimeMillis());
226    }
227
228    /**
229     * Constructs a <code>IndianCalendar</code> with the given date set
230     * in the default time zone with the default <code>FORMAT</code> locale.
231     *
232     * @param date      The date to which the new calendar is set.
233     * @see Category#FORMAT
234     * @stable ICU 3.8
235     */
236    public IndianCalendar(Date date) {
237        super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
238        this.setTime(date);
239    }
240
241    /**
242     * Constructs a <code>IndianCalendar</code> with the given date set
243     * in the default time zone with the default <code>FORMAT</code> locale.
244     *
245     * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
246     *
247     * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field.
248     *                  The value is 0-based. e.g., 0 for January.
249     *
250     * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
251     * @see Category#FORMAT
252     * @stable ICU 3.8
253     */
254    public IndianCalendar(int year, int month, int date) {
255       super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
256       this.set(Calendar.YEAR, year);
257       this.set(Calendar.MONTH, month);
258       this.set(Calendar.DATE, date);
259
260    }
261
262    /**
263     * Constructs a IndianCalendar with the given date
264     * and time set for the default time zone with the default <code>FORMAT</code> locale.
265     *
266     * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
267     *
268     * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field.
269     *                  The value is 0-based. e.g., 0 for January.
270     *
271     * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
272     *
273     * @param hour      The value used to set the calendar's {@link #HOUR_OF_DAY HOUR_OF_DAY} time field.
274     *
275     * @param minute    The value used to set the calendar's {@link #MINUTE MINUTE} time field.
276     *
277     * @param second    The value used to set the calendar's {@link #SECOND SECOND} time field.
278     * @see Category#FORMAT
279     * @stable ICU 3.8
280     */
281    public IndianCalendar(int year, int month, int date, int hour,
282                             int minute, int second)
283    {
284       super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
285       this.set(Calendar.YEAR, year);
286       this.set(Calendar.MONTH, month);
287       this.set(Calendar.DATE, date);
288       this.set(Calendar.HOUR_OF_DAY, hour);
289       this.set(Calendar.MINUTE, minute);
290       this.set(Calendar.SECOND, second);
291    }
292
293
294    //-------------------------------------------------------------------------
295    // The only practical difference from a Gregorian calendar is that years
296    // are numbered since the Saka Era.  A couple of overrides will
297    // take care of that....
298    //-------------------------------------------------------------------------
299
300    // Starts in 78 AD,
301    private static final int INDIAN_ERA_START = 78;
302
303    // The Indian year starts 80 days later than the Gregorian year.
304    private static final int INDIAN_YEAR_START = 80;
305
306    /**
307     * {@inheritDoc}
308     * @stable ICU 3.8
309     */
310    protected int handleGetExtendedYear() {
311        int year;
312
313        if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) {
314            year = internalGet(EXTENDED_YEAR, 1);
315        } else {
316            // Ignore the era, as there is only one
317            year = internalGet(YEAR, 1);
318        }
319
320        return year;
321    }
322
323    /**
324     * {@inheritDoc}
325     * @stable ICU 3.8
326     */
327    protected int handleGetYearLength(int extendedYear) {
328       return super.handleGetYearLength(extendedYear);
329    }
330
331    /**
332     * {@inheritDoc}
333     * @stable ICU 3.8
334     */
335    protected int handleGetMonthLength(int extendedYear, int month) {
336        if (month < 0 || month > 11) {
337            int[] remainder = new int[1];
338            extendedYear += floorDivide(month, 12, remainder);
339            month = remainder[0];
340        }
341
342        if(isGregorianLeap(extendedYear + INDIAN_ERA_START) && month == 0) {
343            return 31;
344        }
345
346        if(month >= 1 && month <=5) {
347            return 31;
348        }
349
350        return 30;
351    }
352
353    /**
354     * {@inheritDoc}
355     * @stable ICU 3.8
356     */
357    protected void handleComputeFields(int julianDay){
358        double jdAtStartOfGregYear;
359        int leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
360        int[] gregorianDay;          // Stores gregorian date corresponding to Julian day;
361
362        gregorianDay = jdToGregorian(julianDay);                    // Gregorian date for Julian day
363        IndianYear = gregorianDay[0] - INDIAN_ERA_START;            // Year in Saka era
364        jdAtStartOfGregYear = gregorianToJD(gregorianDay[0], 1, 1); // JD at start of Gregorian year
365        yday = (int)(julianDay - jdAtStartOfGregYear);              // Day number in Gregorian year (starting from 0)
366
367        if (yday < INDIAN_YEAR_START) {
368            //  Day is at the end of the preceding Saka year
369            IndianYear -= 1;
370            leapMonth = isGregorianLeap(gregorianDay[0] - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year
371            yday += leapMonth + (31 * 5) + (30 * 3) + 10;
372        } else {
373            leapMonth = isGregorianLeap(gregorianDay[0]) ? 31 : 30; // Days in leapMonth this year
374            yday -= INDIAN_YEAR_START;
375        }
376
377        if (yday < leapMonth) {
378            IndianMonth = 0;
379            IndianDayOfMonth = yday + 1;
380        } else {
381              mday = yday - leapMonth;
382              if (mday < (31 * 5)) {
383                 IndianMonth = mday / 31 + 1;
384                 IndianDayOfMonth = (mday % 31) + 1;
385              } else {
386                 mday -= 31 * 5;
387                 IndianMonth = mday / 30 + 6;
388                 IndianDayOfMonth = (mday % 30) + 1;
389              }
390        }
391
392        internalSet(ERA, 0);
393        internalSet(EXTENDED_YEAR, IndianYear);
394        internalSet(YEAR, IndianYear);
395        internalSet(MONTH, IndianMonth);
396        internalSet(DAY_OF_MONTH, IndianDayOfMonth );
397        internalSet(DAY_OF_YEAR, yday + 1); // yday is 0-based
398     }
399
400    private static final int LIMITS[][] = {
401        // Minimum  Greatest     Least    Maximum
402        //           Minimum   Maximum
403        {        0,        0,        0,        0}, // ERA
404        { -5000000, -5000000,  5000000,  5000000}, // YEAR
405        {        0,        0,       11,       11}, // MONTH
406        {        1,        1,       52,       53}, // WEEK_OF_YEAR
407        {/*                                   */}, // WEEK_OF_MONTH
408        {        1,        1,       30,       31}, // DAY_OF_MONTH
409        {        1,        1,      365,      366}, // DAY_OF_YEAR
410        {/*                                   */}, // DAY_OF_WEEK
411        {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
412        {/*                                   */}, // AM_PM
413        {/*                                   */}, // HOUR
414        {/*                                   */}, // HOUR_OF_DAY
415        {/*                                   */}, // MINUTE
416        {/*                                   */}, // SECOND
417        {/*                                   */}, // MILLISECOND
418        {/*                                   */}, // ZONE_OFFSET
419        {/*                                   */}, // DST_OFFSET
420        { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
421        {/*                                   */}, // DOW_LOCAL
422        { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
423        {/*                                   */}, // JULIAN_DAY
424        {/*                                   */}, // MILLISECONDS_IN_DAY
425    };
426
427
428    /**
429     * {@inheritDoc}
430     * @stable ICU 3.8
431     */
432    protected int handleGetLimit(int field, int limitType) {
433       return LIMITS[field][limitType];
434    }
435
436    /**
437     * {@inheritDoc}
438     * @stable ICU 3.8
439     */
440    protected int handleComputeMonthStart(int year, int month, boolean useMonth) {
441
442       //month is 0 based; converting it to 1-based
443       int imonth;
444
445       // If the month is out of range, adjust it into range, and adjust the extended year accordingly
446       if (month < 0 || month > 11) {
447           year += month / 12;
448           month %= 12;
449       }
450
451       if(month == 12) {
452           imonth = 1;
453       } else {
454           imonth = month +1;
455       }
456
457       double jd = IndianToJD(year ,imonth, 1);
458
459       return (int)jd;
460    }
461
462
463
464    /*
465     * This routine converts an Indian date to the corresponding Julian date"
466     * @param year   The year in Saka Era according to Indian calendar.
467     * @param month  The month according to Indian calendar (between 1 to 12)
468     * @param date   The date in month
469     */
470    private static double IndianToJD(int year, int month, int date) {
471       int leapMonth, gyear, m;
472       double start, jd;
473
474       gyear = year + INDIAN_ERA_START;
475
476
477       if(isGregorianLeap(gyear)) {
478          leapMonth = 31;
479          start = gregorianToJD(gyear, 3, 21);
480       } else {
481          leapMonth = 30;
482          start = gregorianToJD(gyear, 3, 22);
483       }
484
485       if (month == 1) {
486          jd = start + (date - 1);
487       } else {
488          jd = start + leapMonth;
489          m = month - 2;
490          m = Math.min(m, 5);
491          jd += m * 31;
492          if (month >= 8) {
493             m = month - 7;
494             jd += m * 30;
495          }
496          jd += date - 1;
497       }
498
499       return jd;
500    }
501
502    /*
503     * The following function is not needed for basic calendar functioning.
504     * This routine converts a gregorian date to the corresponding Julian date"
505     * @param year   The year in standard Gregorian calendar (AD/BC) .
506     * @param month  The month according to Gregorian calendar (between 0 to 11)
507     * @param date   The date in month
508     */
509    private static double gregorianToJD(int year, int month, int date) {
510       double JULIAN_EPOCH = 1721425.5;
511       int y = year - 1;
512       int result = (365 * y)
513                  + (y / 4)
514                  - (y / 100)
515                  + (y / 400)
516                  + (((367 * month) - 362) / 12)
517                  + ((month <= 2) ? 0 : (isGregorianLeap(year) ? -1 : -2))
518                  + date;
519       return result - 1 + JULIAN_EPOCH;
520    }
521
522    /*
523     * The following function is not needed for basic calendar functioning.
524     * This routine converts a julian day (jd) to the corresponding date in Gregorian calendar"
525     * @param jd The Julian date in Julian Calendar which is to be converted to Indian date"
526     */
527    private static int[] jdToGregorian(double jd) {
528       double JULIAN_EPOCH = 1721425.5;
529       double wjd, depoch, quadricent, dqc, cent, dcent, quad, dquad, yindex, yearday, leapadj;
530       int year, month, day;
531
532       wjd = Math.floor(jd - 0.5) + 0.5;
533       depoch = wjd - JULIAN_EPOCH;
534       quadricent = Math.floor(depoch / 146097);
535       dqc = depoch % 146097;
536       cent = Math.floor(dqc / 36524);
537       dcent = dqc % 36524;
538       quad = Math.floor(dcent / 1461);
539       dquad = dcent % 1461;
540       yindex = Math.floor(dquad / 365);
541       year = (int)((quadricent * 400) + (cent * 100) + (quad * 4) + yindex);
542
543       if (!((cent == 4) || (yindex == 4))) {
544          year++;
545       }
546
547       yearday = wjd - gregorianToJD(year, 1, 1);
548       leapadj = ((wjd < gregorianToJD(year, 3, 1)) ? 0
549             :
550             (isGregorianLeap(year) ? 1 : 2)
551             );
552
553       month = (int)Math.floor((((yearday + leapadj) * 12) + 373) / 367);
554       day = (int)(wjd - gregorianToJD(year, month, 1)) + 1;
555
556       int[] julianDate = new int[3];
557
558       julianDate[0] = year;
559       julianDate[1] = month;
560       julianDate[2] = day;
561
562       return julianDate;
563    }
564
565    /*
566     * The following function is not needed for basic calendar functioning.
567     * This routine checks if the Gregorian year is a leap year"
568     * @param year      The year in Gregorian Calendar
569     */
570    private static boolean isGregorianLeap(int year)
571    {
572       return ((year % 4) == 0) &&
573          (!(((year % 100) == 0) && ((year % 400) != 0)));
574    }
575
576
577    /**
578     * {@inheritDoc}
579     * @stable ICU 3.8
580     */
581    public String getType() {
582        return "indian";
583    }
584}
585