001: /*
002: *******************************************************************************
003: * Copyright (C) 1996-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007: package com.ibm.icu.util;
008:
009: import com.ibm.icu.util.TimeZone;
010: import com.ibm.icu.impl.CalendarCache;
011: import java.util.Date;
012: import java.util.Locale;
013:
014: /**
015: * <code>HebrewCalendar</code> is a subclass of <code>Calendar</code>
016: * that that implements the traditional Hebrew calendar.
017: * This is the civil calendar in Israel and the liturgical calendar
018: * of the Jewish faith worldwide.
019: * <p>
020: * The Hebrew calendar is lunisolar and thus has a number of interesting
021: * properties that distinguish it from the Gregorian. Months start
022: * on the day of (an arithmetic approximation of) each new moon. Since the
023: * solar year (approximately 365.24 days) is not an even multiple of
024: * the lunar month (approximately 29.53 days) an extra "leap month" is
025: * inserted in 7 out of every 19 years. To make matters even more
026: * interesting, the start of a year can be delayed by up to three days
027: * in order to prevent certain holidays from falling on the Sabbath and
028: * to prevent certain illegal year lengths. Finally, the lengths of certain
029: * months can vary depending on the number of days in the year.
030: * <p>
031: * The leap month is known as "Adar 1" and is inserted between the
032: * months of Shevat and Adar in leap years. Since the leap month does
033: * not come at the end of the year, calculations involving
034: * month numbers are particularly complex. Users of this class should
035: * make sure to use the {@link #roll roll} and {@link #add add} methods
036: * rather than attempting to perform date arithmetic by manipulating
037: * the fields directly.
038: * <p>
039: * <b>Note:</b> In the traditional Hebrew calendar, days start at sunset.
040: * However, in order to keep the time fields in this class
041: * synchronized with those of the other calendars and with local clock time,
042: * we treat days and months as beginning at midnight,
043: * roughly 6 hours after the corresponding sunset.
044: * <p>
045: * If you are interested in more information on the rules behind the Hebrew
046: * calendar, see one of the following references:
047: * <ul>
048: * <li>"<a href="http://www.amazon.com/exec/obidos/ASIN/0521564743">Calendrical Calculations</a>",
049: * by Nachum Dershowitz & Edward Reingold, Cambridge University Press, 1997, pages 85-91.
050: *
051: * <li>Hebrew Calendar Science and Myths,
052: * <a href="http://www.geocities.com/Athens/1584/">
053: * http://www.geocities.com/Athens/1584/</a>
054: *
055: * <li>The Calendar FAQ,
056: * <a href="http://www.faqs.org/faqs/calendars/faq/">
057: * http://www.faqs.org/faqs/calendars/faq/</a>
058: * </ul>
059: *
060: * <p>
061: * This class should not be subclassed.</p>
062: * <p>
063: * HebrewCalendar usually should be instantiated using
064: * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
065: * with the tag <code>"@calendar=hebrew"</code>.</p>
066: *
067: * @see com.ibm.icu.util.GregorianCalendar
068: * @see com.ibm.icu.util.Calendar
069: *
070: * @author Laura Werner
071: * @author Alan Liu
072: * @stable ICU 2.8
073: */
074: public class HebrewCalendar extends Calendar {
075: // jdk1.4.2 serialver
076: private static final long serialVersionUID = -1952524560588825816L;
077:
078: private static String copyright = "Copyright \u00a9 1997-1998 IBM Corp. All Rights Reserved.";
079:
080: //-------------------------------------------------------------------------
081: // Tons o' Constants...
082: //-------------------------------------------------------------------------
083:
084: /**
085: * Constant for Tishri, the 1st month of the Hebrew year.
086: * @stable ICU 2.8
087: */
088: public static final int TISHRI = 0;
089:
090: /**
091: * Constant for Heshvan, the 2nd month of the Hebrew year.
092: * @stable ICU 2.8
093: */
094: public static final int HESHVAN = 1;
095:
096: /**
097: * Constant for Kislev, the 3rd month of the Hebrew year.
098: * @stable ICU 2.8
099: */
100: public static final int KISLEV = 2;
101:
102: /**
103: * Constant for Tevet, the 4th month of the Hebrew year.
104: * @stable ICU 2.8
105: */
106: public static final int TEVET = 3;
107:
108: /**
109: * Constant for Shevat, the 5th month of the Hebrew year.
110: * @stable ICU 2.8
111: */
112: public static final int SHEVAT = 4;
113:
114: /**
115: * Constant for Adar I, the 6th month of the Hebrew year
116: * (present in leap years only). In non-leap years, the calendar
117: * jumps from Shevat (5th month) to Adar (7th month).
118: * @stable ICU 2.8
119: */
120: public static final int ADAR_1 = 5;
121:
122: /**
123: * Constant for the Adar, the 7th month of the Hebrew year.
124: * @stable ICU 2.8
125: */
126: public static final int ADAR = 6;
127:
128: /**
129: * Constant for Nisan, the 8th month of the Hebrew year.
130: * @stable ICU 2.8
131: */
132: public static final int NISAN = 7;
133:
134: /**
135: * Constant for Iyar, the 9th month of the Hebrew year.
136: * @stable ICU 2.8
137: */
138: public static final int IYAR = 8;
139:
140: /**
141: * Constant for Sivan, the 10th month of the Hebrew year.
142: * @stable ICU 2.8
143: */
144: public static final int SIVAN = 9;
145:
146: /**
147: * Constant for Tammuz, the 11th month of the Hebrew year.
148: * @stable ICU 2.8
149: */
150: public static final int TAMUZ = 10;
151:
152: /**
153: * Constant for Av, the 12th month of the Hebrew year.
154: * @stable ICU 2.8
155: */
156: public static final int AV = 11;
157:
158: /**
159: * Constant for Elul, the 13th month of the Hebrew year.
160: * @stable ICU 2.8
161: */
162: public static final int ELUL = 12;
163:
164: /**
165: * The absolute date, in milliseconds since 1/1/1970 AD, Gregorian,
166: * of the start of the Hebrew calendar. In order to keep this calendar's
167: * time of day in sync with that of the Gregorian calendar, we use
168: * midnight, rather than sunset the day before.
169: */
170: //private static final long EPOCH_MILLIS = -180799862400000L; // 1/1/1 HY
171: private static final int LIMITS[][] = {
172: // Minimum Greatest Least Maximum
173: // Minimum Maximum
174: { 0, 0, 0, 0 }, // ERA
175: { 1, 1, 5000000, 5000000 }, // YEAR
176: { 0, 0, 12, 12 }, // MONTH
177: { 1, 1, 51, 56 }, // WEEK_OF_YEAR
178: { 0, 0, 5, 6 }, // WEEK_OF_MONTH
179: { 1, 1, 29, 30 }, // DAY_OF_MONTH
180: { 1, 1, 353, 385 }, // DAY_OF_YEAR
181: {/* */}, // DAY_OF_WEEK
182: { -1, -1, 4, 6 }, // DAY_OF_WEEK_IN_MONTH
183: {/* */}, // AM_PM
184: {/* */}, // HOUR
185: {/* */}, // HOUR_OF_DAY
186: {/* */}, // MINUTE
187: {/* */}, // SECOND
188: {/* */}, // MILLISECOND
189: {/* */}, // ZONE_OFFSET
190: {/* */}, // DST_OFFSET
191: { -5000001, -5000001, 5000001, 5000001 }, // YEAR_WOY
192: {/* */}, // DOW_LOCAL
193: { -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
194: {/* */}, // JULIAN_DAY
195: {/* */}, // MILLISECONDS_IN_DAY
196: };
197:
198: /**
199: * The lengths of the Hebrew months. This is complicated, because there
200: * are three different types of years, or six if you count leap years.
201: * Due to the rules for postponing the start of the year to avoid having
202: * certain holidays fall on the sabbath, the year can end up being three
203: * different lengths, called "deficient", "normal", and "complete".
204: */
205: private static final int MONTH_LENGTH[][] = {
206: // Deficient Normal Complete
207: { 30, 30, 30 }, //Tishri
208: { 29, 29, 30 }, //Heshvan
209: { 29, 30, 30 }, //Kislev
210: { 29, 29, 29 }, //Tevet
211: { 30, 30, 30 }, //Shevat
212: { 30, 30, 30 }, //Adar I (leap years only)
213: { 29, 29, 29 }, //Adar
214: { 30, 30, 30 }, //Nisan
215: { 29, 29, 29 }, //Iyar
216: { 30, 30, 30 }, //Sivan
217: { 29, 29, 29 }, //Tammuz
218: { 30, 30, 30 }, //Av
219: { 29, 29, 29 }, //Elul
220: };
221:
222: /**
223: * The cumulative # of days to the end of each month in a non-leap year
224: * Although this can be calculated from the MONTH_LENGTH table,
225: * keeping it around separately makes some calculations a lot faster
226: */
227: private static final int MONTH_START[][] = {
228: // Deficient Normal Complete
229: { 0, 0, 0 }, // (placeholder)
230: { 30, 30, 30 }, // Tishri
231: { 59, 59, 60 }, // Heshvan
232: { 88, 89, 90 }, // Kislev
233: { 117, 118, 119 }, // Tevet
234: { 147, 148, 149 }, // Shevat
235: { 147, 148, 149 }, // (Adar I placeholder)
236: { 176, 177, 178 }, // Adar
237: { 206, 207, 208 }, // Nisan
238: { 235, 236, 237 }, // Iyar
239: { 265, 266, 267 }, // Sivan
240: { 294, 295, 296 }, // Tammuz
241: { 324, 325, 326 }, // Av
242: { 353, 354, 355 }, // Elul
243: };
244:
245: /**
246: * The cumulative # of days to the end of each month in a leap year
247: */
248: private static final int LEAP_MONTH_START[][] = {
249: // Deficient Normal Complete
250: { 0, 0, 0 }, // (placeholder)
251: { 30, 30, 30 }, // Tishri
252: { 59, 59, 60 }, // Heshvan
253: { 88, 89, 90 }, // Kislev
254: { 117, 118, 119 }, // Tevet
255: { 147, 148, 149 }, // Shevat
256: { 177, 178, 179 }, // Adar I
257: { 206, 207, 208 }, // Adar II
258: { 236, 237, 238 }, // Nisan
259: { 265, 266, 267 }, // Iyar
260: { 295, 296, 297 }, // Sivan
261: { 324, 325, 326 }, // Tammuz
262: { 354, 355, 356 }, // Av
263: { 383, 384, 385 }, // Elul
264: };
265:
266: //-------------------------------------------------------------------------
267: // Data Members...
268: //-------------------------------------------------------------------------
269:
270: private static CalendarCache cache = new CalendarCache();
271:
272: //-------------------------------------------------------------------------
273: // Constructors...
274: //-------------------------------------------------------------------------
275:
276: /**
277: * Constructs a default <code>HebrewCalendar</code> using the current time
278: * in the default time zone with the default locale.
279: * @stable ICU 2.8
280: */
281: public HebrewCalendar() {
282: this (TimeZone.getDefault(), ULocale.getDefault());
283: }
284:
285: /**
286: * Constructs a <code>HebrewCalendar</code> based on the current time
287: * in the given time zone with the default locale.
288: *
289: * @param zone The time zone for the new calendar.
290: * @stable ICU 2.8
291: */
292: public HebrewCalendar(TimeZone zone) {
293: this (zone, ULocale.getDefault());
294: }
295:
296: /**
297: * Constructs a <code>HebrewCalendar</code> based on the current time
298: * in the default time zone with the given locale.
299: *
300: * @param aLocale The locale for the new calendar.
301: * @stable ICU 2.8
302: */
303: public HebrewCalendar(Locale aLocale) {
304: this (TimeZone.getDefault(), aLocale);
305: }
306:
307: /**
308: * Constructs a <code>HebrewCalendar</code> based on the current time
309: * in the default time zone with the given locale.
310: *
311: * @param locale The locale for the new calendar.
312: * @draft ICU 3.2
313: * @provisional This API might change or be removed in a future release.
314: */
315: public HebrewCalendar(ULocale locale) {
316: this (TimeZone.getDefault(), locale);
317: }
318:
319: /**
320: * Constructs a <code>HebrewCalendar</code> based on the current time
321: * in the given time zone with the given locale.
322: *
323: * @param zone The time zone for the new calendar.
324: *
325: * @param aLocale The locale for the new calendar.
326: * @stable ICU 2.8
327: */
328: public HebrewCalendar(TimeZone zone, Locale aLocale) {
329: super (zone, aLocale);
330: setTimeInMillis(System.currentTimeMillis());
331: }
332:
333: /**
334: * Constructs a <code>HebrewCalendar</code> based on the current time
335: * in the given time zone with the given locale.
336: *
337: * @param zone The time zone for the new calendar.
338: *
339: * @param locale The locale for the new calendar.
340: * @draft ICU 3.2
341: * @provisional This API might change or be removed in a future release.
342: */
343: public HebrewCalendar(TimeZone zone, ULocale locale) {
344: super (zone, locale);
345: setTimeInMillis(System.currentTimeMillis());
346: }
347:
348: /**
349: * Constructs a <code>HebrewCalendar</code> with the given date set
350: * in the default time zone with the default locale.
351: *
352: * @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
353: *
354: * @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
355: * The value is 0-based. e.g., 0 for Tishri.
356: *
357: * @param date The value used to set the calendar's {@link #DATE DATE} time field.
358: * @stable ICU 2.8
359: */
360: public HebrewCalendar(int year, int month, int date) {
361: super (TimeZone.getDefault(), ULocale.getDefault());
362: this .set(YEAR, year);
363: this .set(MONTH, month);
364: this .set(DATE, date);
365: }
366:
367: /**
368: * Constructs a <code>HebrewCalendar</code> with the given date set
369: * in the default time zone with the default locale.
370: *
371: * @param date The date to which the new calendar is set.
372: * @stable ICU 2.8
373: */
374: public HebrewCalendar(Date date) {
375: super (TimeZone.getDefault(), ULocale.getDefault());
376: this .setTime(date);
377: }
378:
379: /**
380: * Constructs a <code>HebrewCalendar</code> with the given date
381: * and time set for the default time zone with the default locale.
382: *
383: * @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
384: *
385: * @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
386: * The value is 0-based. e.g., 0 for Tishri.
387: *
388: * @param date The value used to set the calendar's {@link #DATE DATE} time field.
389: *
390: * @param hour The value used to set the calendar's {@link #HOUR_OF_DAY HOUR_OF_DAY} time field.
391: *
392: * @param minute The value used to set the calendar's {@link #MINUTE MINUTE} time field.
393: *
394: * @param second The value used to set the calendar's {@link #SECOND SECOND} time field.
395: * @stable ICU 2.8
396: */
397: public HebrewCalendar(int year, int month, int date, int hour,
398: int minute, int second) {
399: super (TimeZone.getDefault(), ULocale.getDefault());
400: this .set(YEAR, year);
401: this .set(MONTH, month);
402: this .set(DATE, date);
403: this .set(HOUR_OF_DAY, hour);
404: this .set(MINUTE, minute);
405: this .set(SECOND, second);
406: }
407:
408: //-------------------------------------------------------------------------
409: // Rolling and adding functions overridden from Calendar
410: //
411: // These methods call through to the default implementation in IBMCalendar
412: // for most of the fields and only handle the unusual ones themselves.
413: //-------------------------------------------------------------------------
414:
415: /**
416: * Add a signed amount to a specified field, using this calendar's rules.
417: * For example, to add three days to the current date, you can call
418: * <code>add(Calendar.DATE, 3)</code>.
419: * <p>
420: * When adding to certain fields, the values of other fields may conflict and
421: * need to be changed. For example, when adding one to the {@link #MONTH MONTH} field
422: * for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
423: * must be adjusted so that the result is "29 Elul 5758" rather than the invalid
424: * "30 Elul 5758".
425: * <p>
426: * This method is able to add to
427: * all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
428: * and {@link #ZONE_OFFSET ZONE_OFFSET}.
429: * <p>
430: * <b>Note:</b> You should always use {@link #roll roll} and add rather
431: * than attempting to perform arithmetic operations directly on the fields
432: * of a <tt>HebrewCalendar</tt>. Since the {@link #MONTH MONTH} field behaves
433: * discontinuously in non-leap years, simple arithmetic can give invalid results.
434: * <p>
435: * @param field the time field.
436: * @param amount the amount to add to the field.
437: *
438: * @exception IllegalArgumentException if the field is invalid or refers
439: * to a field that cannot be handled by this method.
440: * @stable ICU 2.8
441: */
442: public void add(int field, int amount) {
443: switch (field) {
444: case MONTH: {
445: // We can't just do a set(MONTH, get(MONTH) + amount). The
446: // reason is ADAR_1. Suppose amount is +2 and we land in
447: // ADAR_1 -- then we have to bump to ADAR_2 aka ADAR. But
448: // if amount is -2 and we land in ADAR_1, then we have to
449: // bump the other way -- down to SHEVAT. - Alan 11/00
450: int month = get(MONTH);
451: int year = get(YEAR);
452: boolean acrossAdar1;
453: if (amount > 0) {
454: acrossAdar1 = (month < ADAR_1); // started before ADAR_1?
455: month += amount;
456: for (;;) {
457: if (acrossAdar1 && month >= ADAR_1
458: && !isLeapYear(year)) {
459: ++month;
460: }
461: if (month <= ELUL) {
462: break;
463: }
464: month -= ELUL + 1;
465: ++year;
466: acrossAdar1 = true;
467: }
468: } else {
469: acrossAdar1 = (month > ADAR_1); // started after ADAR_1?
470: month += amount;
471: for (;;) {
472: if (acrossAdar1 && month <= ADAR_1
473: && !isLeapYear(year)) {
474: --month;
475: }
476: if (month >= 0) {
477: break;
478: }
479: month += ELUL + 1;
480: --year;
481: acrossAdar1 = true;
482: }
483: }
484: set(MONTH, month);
485: set(YEAR, year);
486: pinField(DAY_OF_MONTH);
487: break;
488: }
489:
490: default:
491: super .add(field, amount);
492: break;
493: }
494: }
495:
496: /**
497: * Rolls (up/down) a specified amount time on the given field. For
498: * example, to roll the current date up by three days, you can call
499: * <code>roll(Calendar.DATE, 3)</code>. If the
500: * field is rolled past its maximum allowable value, it will "wrap" back
501: * to its minimum and continue rolling.
502: * For example, calling <code>roll(Calendar.DATE, 10)</code>
503: * on a Hebrew calendar set to "25 Av 5758" will result in the date "5 Av 5758".
504: * <p>
505: * When rolling certain fields, the values of other fields may conflict and
506: * need to be changed. For example, when rolling the {@link #MONTH MONTH} field
507: * upward by one for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
508: * must be adjusted so that the result is "29 Elul 5758" rather than the invalid
509: * "30 Elul".
510: * <p>
511: * This method is able to roll
512: * all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
513: * and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for
514: * additional fields in their overrides of <code>roll</code>.
515: * <p>
516: * <b>Note:</b> You should always use roll and {@link #add add} rather
517: * than attempting to perform arithmetic operations directly on the fields
518: * of a <tt>HebrewCalendar</tt>. Since the {@link #MONTH MONTH} field behaves
519: * discontinuously in non-leap years, simple arithmetic can give invalid results.
520: * <p>
521: * @param field the time field.
522: * @param amount the amount by which the field should be rolled.
523: *
524: * @exception IllegalArgumentException if the field is invalid or refers
525: * to a field that cannot be handled by this method.
526: * @stable ICU 2.8
527: */
528: public void roll(int field, int amount) {
529: switch (field) {
530: case MONTH: {
531: int month = get(MONTH);
532: int year = get(YEAR);
533:
534: boolean leapYear = isLeapYear(year);
535: int yearLength = monthsInYear(year);
536: int newMonth = month + (amount % yearLength);
537: //
538: // If it's not a leap year and we're rolling past the missing month
539: // of ADAR_1, we need to roll an extra month to make up for it.
540: //
541: if (!leapYear) {
542: if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) {
543: newMonth++;
544: } else if (amount < 0 && month > ADAR_1
545: && newMonth <= ADAR_1) {
546: newMonth--;
547: }
548: }
549: set(MONTH, (newMonth + 13) % 13);
550: pinField(DAY_OF_MONTH);
551: return;
552: }
553: default:
554: super .roll(field, amount);
555: }
556: }
557:
558: //-------------------------------------------------------------------------
559: // Support methods
560: //-------------------------------------------------------------------------
561:
562: // Hebrew date calculations are performed in terms of days, hours, and
563: // "parts" (or halakim), which are 1/1080 of an hour, or 3 1/3 seconds.
564: private static final long HOUR_PARTS = 1080;
565: private static final long DAY_PARTS = 24 * HOUR_PARTS;
566:
567: // An approximate value for the length of a lunar month.
568: // It is used to calculate the approximate year and month of a given
569: // absolute date.
570: static private final int MONTH_DAYS = 29;
571: static private final long MONTH_FRACT = 12 * HOUR_PARTS + 793;
572: static private final long MONTH_PARTS = MONTH_DAYS * DAY_PARTS
573: + MONTH_FRACT;
574:
575: // The time of the new moon (in parts) on 1 Tishri, year 1 (the epoch)
576: // counting from noon on the day before. BAHARAD is an abbreviation of
577: // Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204).
578: static private final long BAHARAD = 11 * HOUR_PARTS + 204;
579:
580: /**
581: * Finds the day # of the first day in the given Hebrew year.
582: * To do this, we want to calculate the time of the Tishri 1 new moon
583: * in that year.
584: * <p>
585: * The algorithm here is similar to ones described in a number of
586: * references, including:
587: * <ul>
588: * <li>"Calendrical Calculations", by Nachum Dershowitz & Edward Reingold,
589: * Cambridge University Press, 1997, pages 85-91.
590: *
591: * <li>Hebrew Calendar Science and Myths,
592: * <a href="http://www.geocities.com/Athens/1584/">
593: * http://www.geocities.com/Athens/1584/</a>
594: *
595: * <li>The Calendar FAQ,
596: * <a href="http://www.faqs.org/faqs/calendars/faq/">
597: * http://www.faqs.org/faqs/calendars/faq/</a>
598: * </ul>
599: */
600: private static long startOfYear(int year) {
601: long day = cache.get(year);
602:
603: if (day == CalendarCache.EMPTY) {
604: int months = (235 * year - 234) / 19; // # of months before year
605:
606: long frac = months * MONTH_FRACT + BAHARAD; // Fractional part of day #
607: day = months * 29 + (frac / DAY_PARTS); // Whole # part of calculation
608: frac = frac % DAY_PARTS; // Time of day
609:
610: int wd = (int) (day % 7); // Day of week (0 == Monday)
611:
612: if (wd == 2 || wd == 4 || wd == 6) {
613: // If the 1st is on Sun, Wed, or Fri, postpone to the next day
614: day += 1;
615: wd = (int) (day % 7);
616: }
617: if (wd == 1 && frac > 15 * HOUR_PARTS + 204
618: && !isLeapYear(year)) {
619: // If the new moon falls after 3:11:20am (15h204p from the previous noon)
620: // on a Tuesday and it is not a leap year, postpone by 2 days.
621: // This prevents 356-day years.
622: day += 2;
623: } else if (wd == 0 && frac > 21 * HOUR_PARTS + 589
624: && isLeapYear(year - 1)) {
625: // If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon)
626: // on a Monday and *last* year was a leap year, postpone by 1 day.
627: // Prevents 382-day years.
628: day += 1;
629: }
630: cache.put(year, day);
631: }
632: return day;
633: }
634:
635: /**
636: * Find the day of the week for a given day
637: *
638: * @param day The # of days since the start of the Hebrew calendar,
639: * 1-based (i.e. 1/1/1 AM is day 1).
640: */
641: ///CLOVER:OFF
642: private static int absoluteDayToDayOfWeek(long day) {
643: // We know that 1/1/1 AM is a Monday, which makes the math easy...
644: return (int) (day % 7) + 1;
645: }
646:
647: ///CLOVER:ON
648:
649: /**
650: * Returns the the type of a given year.
651: * 0 "Deficient" year with 353 or 383 days
652: * 1 "Normal" year with 354 or 384 days
653: * 2 "Complete" year with 355 or 385 days
654: */
655: private final int yearType(int year) {
656: int yearLength = handleGetYearLength(year);
657:
658: if (yearLength > 380) {
659: yearLength -= 30; // Subtract length of leap month.
660: }
661:
662: int type = 0;
663:
664: switch (yearLength) {
665: case 353:
666: type = 0;
667: break;
668: case 354:
669: type = 1;
670: break;
671: case 355:
672: type = 2;
673: break;
674: default:
675: throw new IllegalArgumentException("Illegal year length "
676: + yearLength + " in year " + year);
677:
678: }
679: return type;
680: }
681:
682: /**
683: * Determine whether a given Hebrew year is a leap year
684: *
685: * The rule here is that if (year % 19) == 0, 3, 6, 8, 11, 14, or 17.
686: * The formula below performs the same test, believe it or not.
687: */
688: private static final boolean isLeapYear(int year) {
689: //return (year * 12 + 17) % 19 >= 12;
690: int x = (year * 12 + 17) % 19;
691: return x >= ((x < 0) ? -7 : 12);
692: }
693:
694: private static int monthsInYear(int year) {
695: return isLeapYear(year) ? 13 : 12;
696: }
697:
698: //-------------------------------------------------------------------------
699: // Calendar framework
700: //-------------------------------------------------------------------------
701:
702: /**
703: * @stable ICU 2.8
704: */
705: protected int handleGetLimit(int field, int limitType) {
706: return LIMITS[field][limitType];
707: }
708:
709: /**
710: * Returns the length of the given month in the given year
711: * @stable ICU 2.8
712: */
713: protected int handleGetMonthLength(int extendedYear, int month) {
714:
715: switch (month) {
716: case HESHVAN:
717: case KISLEV:
718: // These two month lengths can vary
719: return MONTH_LENGTH[month][yearType(extendedYear)];
720:
721: default:
722: // The rest are a fixed length
723: return MONTH_LENGTH[month][0];
724: }
725: }
726:
727: /**
728: * Returns the number of days in the given Hebrew year
729: * @stable ICU 2.8
730: */
731: protected int handleGetYearLength(int eyear) {
732: return (int) (startOfYear(eyear + 1) - startOfYear(eyear));
733: }
734:
735: //-------------------------------------------------------------------------
736: // Functions for converting from milliseconds to field values
737: //-------------------------------------------------------------------------
738:
739: /**
740: * Subclasses may override this method to compute several fields
741: * specific to each calendar system. These are:
742: *
743: * <ul><li>ERA
744: * <li>YEAR
745: * <li>MONTH
746: * <li>DAY_OF_MONTH
747: * <li>DAY_OF_YEAR
748: * <li>EXTENDED_YEAR</ul>
749: *
750: * Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields,
751: * which will be set when this method is called. Subclasses can
752: * also call the getGregorianXxx() methods to obtain Gregorian
753: * calendar equivalents for the given Julian day.
754: *
755: * <p>In addition, subclasses should compute any subclass-specific
756: * fields, that is, fields from BASE_FIELD_COUNT to
757: * getFieldCount() - 1.
758: * @stable ICU 2.8
759: */
760: protected void handleComputeFields(int julianDay) {
761: long d = julianDay - 347997;
762: long m = (d * DAY_PARTS) / MONTH_PARTS; // Months (approx)
763: int year = (int) ((19 * m + 234) / 235) + 1; // Years (approx)
764: long ys = startOfYear(year); // 1st day of year
765: int dayOfYear = (int) (d - ys);
766:
767: // Because of the postponement rules, it's possible to guess wrong. Fix it.
768: while (dayOfYear < 1) {
769: year--;
770: ys = startOfYear(year);
771: dayOfYear = (int) (d - ys);
772: }
773:
774: // Now figure out which month we're in, and the date within that month
775: int yearType = yearType(year);
776: int monthStart[][] = isLeapYear(year) ? LEAP_MONTH_START
777: : MONTH_START;
778:
779: int month = 0;
780: while (dayOfYear > monthStart[month][yearType]) {
781: month++;
782: }
783: month--;
784: int dayOfMonth = dayOfYear - monthStart[month][yearType];
785:
786: internalSet(ERA, 0);
787: internalSet(YEAR, year);
788: internalSet(EXTENDED_YEAR, year);
789: internalSet(MONTH, month);
790: internalSet(DAY_OF_MONTH, dayOfMonth);
791: internalSet(DAY_OF_YEAR, dayOfYear);
792: }
793:
794: //-------------------------------------------------------------------------
795: // Functions for converting from field values to milliseconds
796: //-------------------------------------------------------------------------
797:
798: /**
799: * @stable ICU 2.8
800: */
801: protected int handleGetExtendedYear() {
802: int year;
803: if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) {
804: year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
805: } else {
806: year = internalGet(YEAR, 1); // Default to year 1
807: }
808: return year;
809: }
810:
811: /**
812: * Return JD of start of given month/year.
813: * @stable ICU 2.8
814: */
815: protected int handleComputeMonthStart(int eyear, int month,
816: boolean useMonth) {
817:
818: // Resolve out-of-range months. This is necessary in order to
819: // obtain the correct year. We correct to
820: // a 12- or 13-month year (add/subtract 12 or 13, depending
821: // on the year) but since we _always_ number from 0..12, and
822: // the leap year determines whether or not month 5 (Adar 1)
823: // is present, we allow 0..12 in any given year.
824: while (month < 0) {
825: month += monthsInYear(--eyear);
826: }
827: // Careful: allow 0..12 in all years
828: while (month > 12) {
829: month -= monthsInYear(eyear++);
830: }
831:
832: long day = startOfYear(eyear);
833:
834: if (month != 0) {
835: if (isLeapYear(eyear)) {
836: day += LEAP_MONTH_START[month][yearType(eyear)];
837: } else {
838: day += MONTH_START[month][yearType(eyear)];
839: }
840: }
841:
842: return (int) (day + 347997);
843: }
844:
845: /**
846: * Return the current Calendar type.
847: * @return type of calendar (gregorian, etc.)
848: * @internal ICU 3.0
849: * @deprecated This API is ICU internal only.
850: */
851: public String getType() {
852: return "hebrew";
853: }
854:
855: /*
856: private static CalendarFactory factory;
857: public static CalendarFactory factory() {
858: if (factory == null) {
859: factory = new CalendarFactory() {
860: public Calendar create(TimeZone tz, ULocale loc) {
861: return new HebrewCalendar(tz, loc);
862: }
863:
864: public String factoryName() {
865: return "Hebrew";
866: }
867: };
868: }
869: return factory;
870: }
871: */
872: }
|