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