/* GENERATED SOURCE. DO NOT MODIFY. */
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 1996-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
package android.icu.util;
import java.util.Date;
import java.util.Locale;
import android.icu.impl.CalendarCache;
import android.icu.util.ULocale.Category;
/**
* HebrewCalendar
is a subclass of Calendar
* that that implements the traditional Hebrew calendar.
* This is the civil calendar in Israel and the liturgical calendar
* of the Jewish faith worldwide.
*
* The Hebrew calendar is lunisolar and thus has a number of interesting * properties that distinguish it from the Gregorian. Months start * on the day of (an arithmetic approximation of) each new moon. Since the * solar year (approximately 365.24 days) is not an even multiple of * the lunar month (approximately 29.53 days) an extra "leap month" is * inserted in 7 out of every 19 years. To make matters even more * interesting, the start of a year can be delayed by up to three days * in order to prevent certain holidays from falling on the Sabbath and * to prevent certain illegal year lengths. Finally, the lengths of certain * months can vary depending on the number of days in the year. *
* The leap month is known as "Adar 1" and is inserted between the * months of Shevat and Adar in leap years. Since the leap month does * not come at the end of the year, calculations involving * month numbers are particularly complex. Users of this class should * make sure to use the {@link #roll roll} and {@link #add add} methods * rather than attempting to perform date arithmetic by manipulating * the fields directly. *
* Note: In the traditional Hebrew calendar, days start at sunset. * However, in order to keep the time fields in this class * synchronized with those of the other calendars and with local clock time, * we treat days and months as beginning at midnight, * roughly 6 hours after the corresponding sunset. *
* If you are interested in more information on the rules behind the Hebrew * calendar, see one of the following references: *
* This class should not be subclassed.
*
* HebrewCalendar usually should be instantiated using
* {@link android.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale
* with the tag "@calendar=hebrew"
.
HebrewCalendar
using the current time
* in the default time zone with the default FORMAT
locale.
* @see Category#FORMAT
*/
public HebrewCalendar() {
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
}
/**
* Constructs a HebrewCalendar
based on the current time
* in the given time zone with the default FORMAT
locale.
*
* @param zone The time zone for the new calendar.
* @see Category#FORMAT
*/
public HebrewCalendar(TimeZone zone) {
this(zone, ULocale.getDefault(Category.FORMAT));
}
/**
* Constructs a HebrewCalendar
based on the current time
* in the default time zone with the given locale.
*
* @param aLocale The locale for the new calendar.
*/
public HebrewCalendar(Locale aLocale) {
this(TimeZone.forLocaleOrDefault(aLocale), aLocale);
}
/**
* Constructs a HebrewCalendar
based on the current time
* in the default time zone with the given locale.
*
* @param locale The locale for the new calendar.
*/
public HebrewCalendar(ULocale locale) {
this(TimeZone.forULocaleOrDefault(locale), locale);
}
/**
* Constructs a HebrewCalendar
based on the current time
* in the given time zone with the given locale.
*
* @param zone The time zone for the new calendar.
*
* @param aLocale The locale for the new calendar.
*/
public HebrewCalendar(TimeZone zone, Locale aLocale) {
super(zone, aLocale);
setTimeInMillis(System.currentTimeMillis());
}
/**
* Constructs a HebrewCalendar
based on the current time
* in the given time zone with the given locale.
*
* @param zone The time zone for the new calendar.
*
* @param locale The locale for the new calendar.
*/
public HebrewCalendar(TimeZone zone, ULocale locale) {
super(zone, locale);
setTimeInMillis(System.currentTimeMillis());
}
/**
* Constructs a HebrewCalendar
with the given date set
* in the default time zone with the default FORMAT
locale.
*
* @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
*
* @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
* The value is 0-based. e.g., 0 for Tishri.
*
* @param date The value used to set the calendar's {@link #DATE DATE} time field.
* @see Category#FORMAT
*/
public HebrewCalendar(int year, int month, int date) {
super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
this.set(YEAR, year);
this.set(MONTH, month);
this.set(DATE, date);
}
/**
* Constructs a HebrewCalendar
with the given date set
* in the default time zone with the default FORMAT
locale.
*
* @param date The date to which the new calendar is set.
* @see Category#FORMAT
*/
public HebrewCalendar(Date date) {
super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
this.setTime(date);
}
/**
* Constructs a HebrewCalendar
with the given date
* and time set for the default time zone with the default FORMAT
locale.
*
* @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
*
* @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
* The value is 0-based. e.g., 0 for Tishri.
*
* @param date The value used to set the calendar's {@link #DATE DATE} time field.
*
* @param hour The value used to set the calendar's {@link #HOUR_OF_DAY HOUR_OF_DAY} time field.
*
* @param minute The value used to set the calendar's {@link #MINUTE MINUTE} time field.
*
* @param second The value used to set the calendar's {@link #SECOND SECOND} time field.
* @see Category#FORMAT
*/
public HebrewCalendar(int year, int month, int date, int hour,
int minute, int second)
{
super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
this.set(YEAR, year);
this.set(MONTH, month);
this.set(DATE, date);
this.set(HOUR_OF_DAY, hour);
this.set(MINUTE, minute);
this.set(SECOND, second);
}
//-------------------------------------------------------------------------
// Rolling and adding functions overridden from Calendar
//
// These methods call through to the default implementation in IBMCalendar
// for most of the fields and only handle the unusual ones themselves.
//-------------------------------------------------------------------------
/**
* Add a signed amount to a specified field, using this calendar's rules.
* For example, to add three days to the current date, you can call
* add(Calendar.DATE, 3)
.
* * When adding to certain fields, the values of other fields may conflict and * need to be changed. For example, when adding one to the {@link #MONTH MONTH} field * for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field * must be adjusted so that the result is "29 Elul 5758" rather than the invalid * "30 Elul 5758". *
* This method is able to add to * all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, * and {@link #ZONE_OFFSET ZONE_OFFSET}. *
* Note: You should always use {@link #roll roll} and add rather * than attempting to perform arithmetic operations directly on the fields * of a HebrewCalendar. Since the {@link #MONTH MONTH} field behaves * discontinuously in non-leap years, simple arithmetic can give invalid results. *
* @param field the time field.
* @param amount the amount to add to the field.
*
* @exception IllegalArgumentException if the field is invalid or refers
* to a field that cannot be handled by this method.
*/
@Override
public void add(int field, int amount)
{
switch (field) {
case MONTH:
case ORDINAL_MONTH:
{
// We can't just do a set(MONTH, get(MONTH) + amount). The
// reason is ADAR_1. Suppose amount is +2 and we land in
// ADAR_1 -- then we have to bump to ADAR_2 aka ADAR. But
// if amount is -2 and we land in ADAR_1, then we have to
// bump the other way -- down to SHEVAT. - Alan 11/00
int month = get(MONTH);
int year = get(YEAR);
boolean acrossAdar1;
if (amount > 0) {
acrossAdar1 = (month < ADAR_1); // started before ADAR_1?
month += amount;
for (;;) {
if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) {
++month;
}
if (month <= ELUL) {
break;
}
month -= ELUL+1;
++year;
acrossAdar1 = true;
}
} else {
acrossAdar1 = (month > ADAR_1); // started after ADAR_1?
month += amount;
for (;;) {
if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) {
--month;
}
if (month >= 0) {
break;
}
month += ELUL+1;
--year;
acrossAdar1 = true;
}
}
set(MONTH, month);
set(YEAR, year);
pinField(DAY_OF_MONTH);
break;
}
default:
super.add(field, amount);
break;
}
}
/**
* Rolls (up/down) a specified amount time on the given field. For
* example, to roll the current date up by three days, you can call
* roll(Calendar.DATE, 3)
. If the
* field is rolled past its maximum allowable value, it will "wrap" back
* to its minimum and continue rolling.
* For example, calling roll(Calendar.DATE, 10)
* on a Hebrew calendar set to "25 Av 5758" will result in the date "5 Av 5758".
*
* When rolling certain fields, the values of other fields may conflict and * need to be changed. For example, when rolling the {@link #MONTH MONTH} field * upward by one for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field * must be adjusted so that the result is "29 Elul 5758" rather than the invalid * "30 Elul". *
* This method is able to roll
* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for
* additional fields in their overrides of roll
.
*
* Note: You should always use roll and {@link #add add} rather * than attempting to perform arithmetic operations directly on the fields * of a HebrewCalendar. Since the {@link #MONTH MONTH} field behaves * discontinuously in non-leap years, simple arithmetic can give invalid results. *
* @param field the time field. * @param amount the amount by which the field should be rolled. * * @exception IllegalArgumentException if the field is invalid or refers * to a field that cannot be handled by this method. */ @Override public void roll(int field, int amount) { switch (field) { case MONTH: case ORDINAL_MONTH: { int month = get(MONTH); int year = get(YEAR); boolean leapYear = isLeapYear(year); int yearLength = monthsInYear(year); int newMonth = month + (amount % yearLength); // // If it's not a leap year and we're rolling past the missing month // of ADAR_1, we need to roll an extra month to make up for it. // if (!leapYear) { if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) { newMonth++; } else if (amount < 0 && month > ADAR_1 && newMonth <= ADAR_1) { newMonth--; } } set(MONTH, (newMonth + 13) % 13); pinField(DAY_OF_MONTH); return; } default: super.roll(field, amount); } } //------------------------------------------------------------------------- // Support methods //------------------------------------------------------------------------- // Hebrew date calculations are performed in terms of days, hours, and // "parts" (or halakim), which are 1/1080 of an hour, or 3 1/3 seconds. private static final long HOUR_PARTS = 1080; private static final long DAY_PARTS = 24*HOUR_PARTS; // An approximate value for the length of a lunar month. // It is used to calculate the approximate year and month of a given // absolute date. static private final int MONTH_DAYS = 29; static private final long MONTH_FRACT = 12*HOUR_PARTS + 793; static private final long MONTH_PARTS = MONTH_DAYS*DAY_PARTS + MONTH_FRACT; // The time of the new moon (in parts) on 1 Tishri, year 1 (the epoch) // counting from noon on the day before. BAHARAD is an abbreviation of // Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204). static private final long BAHARAD = 11*HOUR_PARTS + 204; /** * Finds the day # of the first day in the given Hebrew year. * To do this, we want to calculate the time of the Tishri 1 new moon * in that year. *
* The algorithm here is similar to ones described in a number of * references, including: *
* Overrides {@link Calendar#validateField(int)} to provide * special handling for month validation for Hebrew calendar. * @deprecated This API is ICU internal only. * @hide original deprecated declaration * @hide draft / provisional / internal are hidden on Android */ @Override @Deprecated protected void validateField(int field) { if ((field == MONTH || field == ORDINAL_MONTH) && !isLeapYear(handleGetExtendedYear()) && internalGetMonth() == ADAR_1) { throw new IllegalArgumentException("MONTH cannot be ADAR_1(5) except leap years"); } super.validateField(field); } //------------------------------------------------------------------------- // Functions for converting from milliseconds to field values //------------------------------------------------------------------------- /** * Subclasses may override this method to compute several fields * specific to each calendar system. These are: * *
In addition, subclasses should compute any subclass-specific * fields, that is, fields from BASE_FIELD_COUNT to * getFieldCount() - 1. */ @Override protected void handleComputeFields(int julianDay) { long d = julianDay - 347997; long m = floorDivide((d * DAY_PARTS), MONTH_PARTS); // Months (approx) int year = (int)(floorDivide((YEARS_IN_CYCLE * m + (MONTHS_IN_CYCLE-1)), MONTHS_IN_CYCLE) + 1); // Years (approx) long ys = startOfYear(year); // 1st day of year int dayOfYear = (int)(d - ys); // Because of the postponement rules, it's possible to guess wrong. Fix it. while (dayOfYear < 1) { year--; ys = startOfYear(year); dayOfYear = (int)(d - ys); } // Now figure out which month we're in, and the date within that month int yearType = yearType(year); boolean isLeap = isLeapYear(year); int monthStart[][] = isLeap ? LEAP_MONTH_START : MONTH_START; int month = 0; while (dayOfYear > monthStart[month][yearType]) { month++; } month--; int dayOfMonth = dayOfYear - monthStart[month][yearType]; internalSet(ERA, 0); internalSet(YEAR, year); internalSet(EXTENDED_YEAR, year); int ordinal_month = month; if (!isLeap && ordinal_month > ADAR_1) { ordinal_month--; } internalSet(ORDINAL_MONTH, ordinal_month); internalSet(MONTH, month); internalSet(DAY_OF_MONTH, dayOfMonth); internalSet(DAY_OF_YEAR, dayOfYear); } //------------------------------------------------------------------------- // Functions for converting from field values to milliseconds //------------------------------------------------------------------------- /** */ @Override protected int handleGetExtendedYear() { int year; if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) { year = internalGet(EXTENDED_YEAR, 1); // Default to year 1 } else { year = internalGet(YEAR, 1); // Default to year 1 } return year; } /** * Return JD of start of given month/year. */ @Override protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) { // Resolve out-of-range months. This is necessary in order to // obtain the correct year. We correct to // a 12- or 13-month year (add/subtract 12 or 13, depending // on the year) but since we _always_ number from 0..12, and // the leap year determines whether or not month 5 (Adar 1) // is present, we allow 0..12 in any given year. if (month <= -MONTHS_IN_CYCLE || MONTHS_IN_CYCLE <= month) { eyear += (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE; month = month % MONTHS_IN_CYCLE; } while (month < 0) { month += monthsInYear(--eyear); } // Careful: allow 0..12 in all years while (month > 12) { month -= monthsInYear(eyear++); } long day = startOfYear(eyear); if (month != 0) { if (isLeapYear(eyear)) { day += LEAP_MONTH_START[month][yearType(eyear)]; } else { day += MONTH_START[month][yearType(eyear)]; } } return (int) (day + 347997); } /** * {@inheritDoc} */ @Override public String getType() { return "hebrew"; } //------------------------------------------------------------------------- // Temporal Calendar API. //------------------------------------------------------------------------- /** * {@inheritDoc} * @hide draft / provisional / internal are hidden on Android */ public boolean inTemporalLeapYear() { return isLeapYear(get(EXTENDED_YEAR)); } private static String [] gTemporalMonthCodesForHebrew = { "M01", "M02", "M03", "M04", "M05", "M05L", "M06", "M07", "M08", "M09", "M10", "M11", "M12" }; /** * Gets The Temporal monthCode value corresponding to the month for the date. * The value is a string identifier that starts with the literal grapheme * "M" followed by two graphemes representing the zero-padded month number * of the current month in a normal (non-leap) year and suffixed by an * optional literal grapheme "L" if this is a leap month in a lunisolar * calendar. For the Hebrew calendar, the values are "M01" .. "M12" for * non-leap year, and "M01" .. "M05", "M05L", "M06" .. "M12" for leap year. * * @return One of 13 possible strings in {"M01".. "M05", "M05L", "M06" .. "M12"}. * @hide draft / provisional / internal are hidden on Android */ public String getTemporalMonthCode() { return gTemporalMonthCodesForHebrew[get(MONTH)]; } /** * Sets The Temporal monthCode which is a string identifier that starts * with the literal grapheme "M" followed by two graphemes representing * the zero-padded month number of the current month in a normal * (non-leap) year and suffixed by an optional literal grapheme "L" if this * is a leap month in a lunisolar calendar. For Hebrew calendar, the values * are "M01" .. "M12" for non-leap years, and "M01" .. "M05", "M05L", "M06" * .. "M12" for leap year. * @param temporalMonth The value to be set for temporal monthCode. * @hide draft / provisional / internal are hidden on Android */ public void setTemporalMonthCode( String temporalMonth ) { if (temporalMonth.length() == 3 || temporalMonth.length() == 4) { for (int m = 0; m < gTemporalMonthCodesForHebrew.length; m++) { if (temporalMonth.equals(gTemporalMonthCodesForHebrew[m])) { set(MONTH, m); return; } } } throw new IllegalArgumentException("Incorrect temporal Month code: " + temporalMonth); } //------------------------------------------------------------------------- // End of Temporal Calendar API //------------------------------------------------------------------------- /** * {@inheritDoc} * @deprecated This API is ICU internal only. * @hide draft / provisional / internal are hidden on Android */ @Deprecated protected int internalGetMonth() { if (resolveFields(MONTH_PRECEDENCE) == ORDINAL_MONTH) { int ordinalMonth = internalGet(ORDINAL_MONTH); int year = handleGetExtendedYear(); return ordinalMonth + (((!isLeapYear(year)) && (ordinalMonth > ADAR_1)) ? 1: 0); } return super.internalGetMonth(); } /* private static CalendarFactory factory; public static CalendarFactory factory() { if (factory == null) { factory = new CalendarFactory() { public Calendar create(TimeZone tz, ULocale loc) { return new HebrewCalendar(tz, loc); } public String factoryName() { return "Hebrew"; } }; } return factory; } */ }