2 *******************************************************************************
\r
3 * Copyright (C) 1996-2008, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
8 package com.ibm.icu.util;
\r
10 import com.ibm.icu.util.TimeZone;
\r
11 import java.util.Date;
\r
12 import java.util.Locale;
\r
15 * <code>IndianCalendar</code> is a subclass of <code>GregorianCalendar</code>
\r
16 * that numbers years since the birth of the Buddha. This is the civil calendar
\r
17 * which is accepted by government of India as Indian National Calendar.
\r
18 * The two calendars most widely used in India today are the Vikrama calendar
\r
19 * followed in North India and the Shalivahana or Saka calendar which is followed
\r
20 * in South India and Maharashtra.
\r
22 * A variant of the Shalivahana Calendar was reformed and standardized as the
\r
23 * Indian National calendar in 1957.
\r
25 * Some details of Indian National Calendar (to be implemented) :
\r
27 * Month Length Start date (Gregorian)
\r
28 * =================================================
\r
29 * 1 Chaitra 30/31 March 22*
\r
30 * 2 Vaisakha 31 April 21
\r
31 * 3 Jyaistha 31 May 22
\r
32 * 4 Asadha 31 June 22
\r
33 * 5 Sravana 31 July 23
\r
34 * 6 Bhadra 31 August 23
\r
35 * 7 Asvina 30 September 23
\r
36 * 8 Kartika 30 October 23
\r
37 * 9 Agrahayana 30 November 22
\r
38 * 10 Pausa 30 December 22
\r
39 * 11 Magha 30 January 21
\r
40 * 12 Phalguna 30 February 20
\r
42 * In leap years, Chaitra has 31 days and starts on March 21 instead.
\r
43 * The leap years of Gregorian calendar and Indian National Calendar are in synchornization.
\r
44 * So When its a leap year in Gregorian calendar then Chaitra has 31 days.
\r
47 * Years are counted in the Saka Era, which starts its year 0 in 78AD (by gregorian calendar).
\r
48 * So for eg. 9th June 2006 by Gregorian Calendar, is same as 19th of Jyaistha in 1928 of Saka
\r
49 * era by Indian National Calendar.
\r
51 * The Indian Calendar has only one allowable era: <code>Saka Era</code>. If the
\r
52 * calendar is not in lenient mode (see <code>setLenient</code>), dates before
\r
53 * 1/1/1 Saka Era are rejected with an <code>IllegalArgumentException</code>.
\r
55 * This class should not be subclassed.</p>
\r
57 * IndianCalendar usually should be instantiated using
\r
58 * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
\r
59 * with the tag <code>"@calendar=Indian"</code>.</p>
\r
61 * @see com.ibm.icu.util.Calendar
\r
62 * @see com.ibm.icu.util.GregorianCalendar
\r
66 public class IndianCalendar extends Calendar {
\r
67 // jdk1.4.2 serialver
\r
68 private static final long serialVersionUID = 3617859668165014834L;
\r
71 * Constant for Chaitra, the 1st month of the Indian year.
\r
74 public static final int CHAITRA = 0;
\r
77 * Constant for Vaisakha, the 2nd month of the Indian year.
\r
80 public static final int VAISAKHA = 1;
\r
83 * Constant for Jyaistha, the 3rd month of the Indian year.
\r
86 public static final int JYAISTHA = 2;
\r
89 * Constant for Asadha, the 4th month of the Indian year.
\r
92 public static final int ASADHA = 3;
\r
95 * Constant for Sravana, the 5th month of the Indian year.
\r
98 public static final int SRAVANA = 4 ;
\r
101 * Constant for Bhadra, the 6th month of the Indian year.
\r
104 public static final int BHADRA = 5 ;
\r
107 * Constant for Asvina, the 7th month of the Indian year.
\r
110 public static final int ASVINA = 6 ;
\r
113 * Constant for Kartika, the 8th month of the Indian year.
\r
116 public static final int KARTIKA = 7 ;
\r
119 * Constant for Agrahayana, the 9th month of the Indian year.
\r
122 public static final int AGRAHAYANA = 8 ;
\r
125 * Constant for Pausa, the 10th month of the Indian year.
\r
128 public static final int PAUSA = 9 ;
\r
131 * Constant for Magha, the 11th month of the Indian year.
\r
134 public static final int MAGHA = 10;
\r
137 * Constant for Phalguna, the 12th month of the Indian year.
\r
140 public static final int PHALGUNA = 11;
\r
142 //-------------------------------------------------------------------------
\r
144 //-------------------------------------------------------------------------
\r
147 * Constant for the Indian Era. This is the only allowable <code>ERA</code>
\r
148 * value for the Indian calendar.
\r
150 * @see com.ibm.icu.util.Calendar#ERA
\r
153 public static final int IE = 0;
\r
156 * Constructs a <code>IndianCalendar</code> using the current time
\r
157 * in the default time zone with the default locale.
\r
160 public IndianCalendar() {
\r
161 this(TimeZone.getDefault(), ULocale.getDefault());
\r
165 * Constructs a <code>IndianCalendar</code> based on the current time
\r
166 * in the given time zone with the default locale.
\r
168 * @param zone the given time zone.
\r
171 public IndianCalendar(TimeZone zone) {
\r
172 this(zone, ULocale.getDefault());
\r
176 * Constructs a <code>IndianCalendar</code> based on the current time
\r
177 * in the default time zone with the given locale.
\r
179 * @param aLocale the given locale.
\r
182 public IndianCalendar(Locale aLocale) {
\r
183 this(TimeZone.getDefault(), aLocale);
\r
187 * Constructs a <code>IndianCalendar</code> based on the current time
\r
188 * in the default time zone with the given locale.
\r
190 * @param locale the given ulocale.
\r
193 public IndianCalendar(ULocale locale) {
\r
194 this(TimeZone.getDefault(), locale);
\r
198 * Constructs a <code>IndianCalendar</code> based on the current time
\r
199 * in the given time zone with the given locale.
\r
201 * @param zone the given time zone.
\r
203 * @param aLocale the given locale.
\r
206 public IndianCalendar(TimeZone zone, Locale aLocale) {
\r
207 super(zone, aLocale);
\r
208 setTimeInMillis(System.currentTimeMillis());
\r
212 * Constructs a <code>IndianCalendar</code> based on the current time
\r
213 * in the given time zone with the given locale.
\r
215 * @param zone the given time zone.
\r
217 * @param locale the given ulocale.
\r
220 public IndianCalendar(TimeZone zone, ULocale locale) {
\r
221 super(zone, locale);
\r
222 setTimeInMillis(System.currentTimeMillis());
\r
226 * Constructs a <code>IndianCalendar</code> with the given date set
\r
227 * in the default time zone with the default locale.
\r
229 * @param date The date to which the new calendar is set.
\r
232 public IndianCalendar(Date date) {
\r
233 super(TimeZone.getDefault(), ULocale.getDefault());
\r
234 this.setTime(date);
\r
238 * Constructs a <code>IndianCalendar</code> with the given date set
\r
239 * in the default time zone with the default locale.
\r
241 * @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
\r
243 * @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
\r
244 * The value is 0-based. e.g., 0 for January.
\r
246 * @param date The value used to set the calendar's {@link #DATE DATE} time field.
\r
249 public IndianCalendar(int year, int month, int date) {
\r
250 super(TimeZone.getDefault(), ULocale.getDefault());
\r
251 this.set(Calendar.YEAR, year);
\r
252 this.set(Calendar.MONTH, month);
\r
253 this.set(Calendar.DATE, date);
\r
258 * Constructs a IndianCalendar with the given date
\r
259 * and time set for the default time zone with the default locale.
\r
261 * @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
\r
263 * @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
\r
264 * The value is 0-based. e.g., 0 for January.
\r
266 * @param date The value used to set the calendar's {@link #DATE DATE} time field.
\r
268 * @param hour The value used to set the calendar's {@link #HOUR_OF_DAY HOUR_OF_DAY} time field.
\r
270 * @param minute The value used to set the calendar's {@link #MINUTE MINUTE} time field.
\r
272 * @param second The value used to set the calendar's {@link #SECOND SECOND} time field.
\r
275 public IndianCalendar(int year, int month, int date, int hour,
\r
276 int minute, int second)
\r
278 super(TimeZone.getDefault(), ULocale.getDefault());
\r
279 this.set(Calendar.YEAR, year);
\r
280 this.set(Calendar.MONTH, month);
\r
281 this.set(Calendar.DATE, date);
\r
282 this.set(Calendar.HOUR_OF_DAY, hour);
\r
283 this.set(Calendar.MINUTE, minute);
\r
284 this.set(Calendar.SECOND, second);
\r
288 //-------------------------------------------------------------------------
\r
289 // The only practical difference from a Gregorian calendar is that years
\r
290 // are numbered since the Saka Era. A couple of overrides will
\r
291 // take care of that....
\r
292 //-------------------------------------------------------------------------
\r
294 // Starts in 78 AD,
\r
295 private static final int INDIAN_ERA_START = 78;
\r
297 // The Indian year starts 80 days later than the Gregorian year.
\r
298 private static final int INDIAN_YEAR_START = 80;
\r
304 protected int handleGetExtendedYear() {
\r
307 if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) {
\r
308 year = internalGet(EXTENDED_YEAR, 1);
\r
310 // Ignore the era, as there is only one
\r
311 year = internalGet(YEAR, 1);
\r
321 protected int handleGetYearLength(int extendedYear) {
\r
322 return super.handleGetYearLength(extendedYear);
\r
329 protected int handleGetMonthLength(int extendedYear, int month) {
\r
330 if (month < 0 || month > 11) {
\r
331 int[] remainder = new int[1];
\r
332 extendedYear += floorDivide(month, 12, remainder);
\r
333 month = remainder[0];
\r
336 if(isGregorianLeap(extendedYear + INDIAN_ERA_START) && month == 0) {
\r
340 if(month >= 1 && month <=5) {
\r
351 protected void handleComputeFields(int julianDay){
\r
352 double jdAtStartOfGregYear;
\r
353 int leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
\r
354 int[] gregorianDay; // Stores gregorian date corresponding to Julian day;
\r
356 gregorianDay = jdToGregorian(julianDay); // Gregorian date for Julian day
\r
357 IndianYear = gregorianDay[0] - INDIAN_ERA_START; // Year in Saka era
\r
358 jdAtStartOfGregYear = gregorianToJD(gregorianDay[0], 1, 1); // JD at start of Gregorian year
\r
359 yday = (int)(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0)
\r
361 if (yday < INDIAN_YEAR_START) {
\r
362 // Day is at the end of the preceding Saka year
\r
364 leapMonth = isGregorianLeap(gregorianDay[0] - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year
\r
365 yday += leapMonth + (31 * 5) + (30 * 3) + 10;
\r
367 leapMonth = isGregorianLeap(gregorianDay[0]) ? 31 : 30; // Days in leapMonth this year
\r
368 yday -= INDIAN_YEAR_START;
\r
371 if (yday < leapMonth) {
\r
373 IndianDayOfMonth = yday + 1;
\r
375 mday = yday - leapMonth;
\r
376 if (mday < (31 * 5)) {
\r
377 IndianMonth = (int)Math.floor(mday / 31) + 1;
\r
378 IndianDayOfMonth = (mday % 31) + 1;
\r
381 IndianMonth = (int)Math.floor(mday / 30) + 6;
\r
382 IndianDayOfMonth = (mday % 30) + 1;
\r
386 internalSet(ERA, 0);
\r
387 internalSet(EXTENDED_YEAR, IndianYear);
\r
388 internalSet(YEAR, IndianYear);
\r
389 internalSet(MONTH, IndianMonth);
\r
390 internalSet(DAY_OF_MONTH, IndianDayOfMonth );
\r
391 internalSet(DAY_OF_YEAR, yday + 1); // yday is 0-based
\r
394 private static final int LIMITS[][] = {
\r
395 // Minimum Greatest Least Maximum
\r
397 { 0, 0, 0, 0}, // ERA
\r
398 { -5000000, -5000000, 5000000, 5000000}, // YEAR
\r
399 { 0, 0, 11, 11}, // MONTH
\r
400 { 1, 1, 52, 53}, // WEEK_OF_YEAR
\r
401 {/* */}, // WEEK_OF_MONTH
\r
402 { 1, 1, 30, 31}, // DAY_OF_MONTH
\r
403 { 1, 1, 365, 366}, // DAY_OF_YEAR
\r
404 {/* */}, // DAY_OF_WEEK
\r
405 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
\r
408 {/* */}, // HOUR_OF_DAY
\r
411 {/* */}, // MILLISECOND
\r
412 {/* */}, // ZONE_OFFSET
\r
413 {/* */}, // DST_OFFSET
\r
414 { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY
\r
415 {/* */}, // DOW_LOCAL
\r
416 { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR
\r
417 {/* */}, // JULIAN_DAY
\r
418 {/* */}, // MILLISECONDS_IN_DAY
\r
426 protected int handleGetLimit(int field, int limitType) {
\r
427 return LIMITS[field][limitType];
\r
434 protected int handleComputeMonthStart(int year, int month, boolean useMonth) {
\r
436 //month is 0 based; converting it to 1-based
\r
442 imonth = month +1;
\r
445 double jd = IndianToJD(year ,imonth, 1);
\r
453 * This routine converts an Indian date to the corresponding Julian date"
\r
454 * @param year The year in Saka Era according to Indian calendar.
\r
455 * @param month The month according to Indian calendar (between 1 to 12)
\r
456 * @param date The date in month
\r
458 private static double IndianToJD(int year, int month, int date) {
\r
459 int leapMonth, gyear, m;
\r
462 gyear = year + INDIAN_ERA_START;
\r
465 if(isGregorianLeap(gyear)) {
\r
467 start = gregorianToJD(gyear, 3, 21);
\r
470 start = gregorianToJD(gyear, 3, 22);
\r
474 jd = start + (date - 1);
\r
476 jd = start + leapMonth;
\r
478 m = Math.min(m, 5);
\r
491 * The following function is not needed for basic calendar functioning.
\r
492 * This routine converts a gregorian date to the corresponding Julian date"
\r
493 * @param year The year in standard Gregorian calendar (AD/BC) .
\r
494 * @param month The month according to Gregorian calendar (between 0 to 11)
\r
495 * @param date The date in month
\r
497 private static double gregorianToJD(int year, int month, int date) {
\r
498 double JULIAN_EPOCH = 1721425.5;
\r
499 double jd = (JULIAN_EPOCH - 1) +
\r
500 (365 * (year - 1)) +
\r
501 Math.floor((year - 1) / 4) +
\r
502 (-Math.floor((year - 1) / 100)) +
\r
503 Math.floor((year - 1) / 400) +
\r
504 Math.floor((((367 * month) - 362) / 12) +
\r
505 ((month <= 2) ? 0 :
\r
506 (isGregorianLeap(year) ? -1 : -2)
\r
514 * The following function is not needed for basic calendar functioning.
\r
515 * This routine converts a julian day (jd) to the corresponding date in Gregorian calendar"
\r
516 * @param jd The Julian date in Julian Calendar which is to be converted to Indian date"
\r
518 private static int[] jdToGregorian(double jd) {
\r
519 double JULIAN_EPOCH = 1721425.5;
\r
520 double wjd, depoch, quadricent, dqc, cent, dcent, quad, dquad, yindex, yearday, leapadj;
\r
521 int year, month, day;
\r
523 wjd = Math.floor(jd - 0.5) + 0.5;
\r
524 depoch = wjd - JULIAN_EPOCH;
\r
525 quadricent = Math.floor(depoch / 146097);
\r
526 dqc = depoch % 146097;
\r
527 cent = Math.floor(dqc / 36524);
\r
528 dcent = dqc % 36524;
\r
529 quad = Math.floor(dcent / 1461);
\r
530 dquad = dcent % 1461;
\r
531 yindex = Math.floor(dquad / 365);
\r
532 year = (int)((quadricent * 400) + (cent * 100) + (quad * 4) + yindex);
\r
534 if (!((cent == 4) || (yindex == 4))) {
\r
538 yearday = wjd - gregorianToJD(year, 1, 1);
\r
539 leapadj = ((wjd < gregorianToJD(year, 3, 1)) ? 0
\r
541 (isGregorianLeap(year) ? 1 : 2)
\r
544 month = (int)Math.floor((((yearday + leapadj) * 12) + 373) / 367);
\r
545 day = (int)(wjd - gregorianToJD(year, month, 1)) + 1;
\r
547 int[] julianDate = new int[3];
\r
549 julianDate[0] = year;
\r
550 julianDate[1] = month;
\r
551 julianDate[2] = day;
\r
557 * The following function is not needed for basic calendar functioning.
\r
558 * This routine checks if the Gregorian year is a leap year"
\r
559 * @param year The year in Gregorian Calendar
\r
561 private static boolean isGregorianLeap(int year)
\r
563 return ((year % 4) == 0) &&
\r
564 (!(((year % 100) == 0) && ((year % 400) != 0)));
\r
572 public String getType() {
\r