usecrate::calendar_arithmetic::{ArithmeticDate, DateFieldsResolver, ToExtendedYear}; usecrate::error::{
DateError, DateFromFieldsError, EcmaReferenceYearError, MonthCodeError, UnknownEraError,
}; usecrate::options::{DateAddOptions, DateDifferenceOptions}; usecrate::options::{DateFromFieldsOptions, Overflow}; usecrate::types::{DateFields, MonthInfo, ValidMonthCode}; usecrate::RangeError; usecrate::{types, Calendar, Date}; use ::tinystr::tinystr; use calendrical_calculations::hebrew_keviyah::{Keviyah, YearInfo}; use calendrical_calculations::rata_die::RataDie;
/// The [Hebrew Calendar](https://en.wikipedia.org/wiki/Hebrew_calendar) /// /// The Hebrew calendar is a lunisolar calendar used as the Jewish liturgical calendar /// as well as an official calendar in Israel. /// /// This implementation uses civil month numbering, where Tishrei is the first month of the year. /// /// The precise algorithm used to calculate the Hebrew Calendar has [changed over time], with /// the modern one being in place since about 4536 AM (776 CE). This implementation extends /// proleptically for dates before that. /// /// [changed over time]: https://hakirah.org/vol20AjdlerAppendices.pdf /// /// This corresponds to the `"hebrew"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier). /// /// # Era codes /// /// This calendar uses a single era code `am`, Anno Mundi. Dates before this era use negative years. /// /// # Months and days /// /// The 12 months are called Tishrei (`M01`, 30 days), Ḥešvan (`M02`, 29/30 days), /// Kīslev (`M03`, 30/29 days), Ṭevet (`M04`, 29 days), Šəvaṭ (`M05`, 30 days), ʾĂdār (`M06`, 29 days), /// Nīsān (`M07`, 30 days), ʾĪyyar (`M08`, 29 days), Sivan (`M09`, 30 days), Tammūz (`M10`, 29 days), /// ʾAv (`M11`, 30 days), ʾElūl (`M12`, 29 days). /// /// Due to Rosh Hashanah postponement rules, Ḥešvan and Kislev vary in length. /// /// In leap years (years 3, 6, 8, 11, 17, 19 in a 19-year cycle), the leap month Adar I (`M05L`, 30 days) /// is inserted before Adar, and Adar is called Adar II (the `formatting_code` returned by [`MonthInfo`] /// will be `M06L` to mark this, while the `standard_code` remains `M06`). /// /// Standard years thus have 353-355 days, and leap years 383-385. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)] #[allow(clippy::exhaustive_structs)] // unit struct pubstruct Hebrew;
/// The inner date type used for representing [`Date`]s of [`Hebrew`]. See [`Date`] and [`Hebrew`] for more details. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pubstruct HebrewDateInner(ArithmeticDate<Hebrew>);
impl Hebrew { /// Construct a new [`Hebrew`] pubfn new() -> Self {
Hebrew
}
}
impl HebrewYearInfo { /// Convenience method to compute for a given year. Don't use this if you actually need /// a YearInfo that you want to call .new_year() on. #[inline] fn compute(value: i32) -> Self { Self {
keviyah: YearInfo::compute_for(value).keviyah,
value,
}
}
}
impl DateFieldsResolver for Hebrew { type YearInfo = HebrewYearInfo; fn days_in_provided_month(info: HebrewYearInfo, ordinal_month: u8) -> u8 {
info.keviyah.month_len(ordinal_month)
}
fn reference_year_from_month_day(
&self,
month_code: types::ValidMonthCode,
day: u8,
) -> Result<Self::YearInfo, EcmaReferenceYearError> { // December 31, 1972 occurs on 4th month, 26th day, 5733 AM let hebrew_year = match month_code.to_tuple() {
(1, false) => 5733,
(2, false) => match day { // There is no day 30 in 5733 (there is in 5732)
..=29 => 5733, // Note (here and below): this must be > 29, not just == 30, // since we have not yet applied a potential Overflow::Constrain.
_ => 5732,
},
(3, false) => match day { // There is no day 30 in 5733 (there is in 5732)
..=29 => 5733,
_ => 5732,
},
(4, false) => match day {
..=26 => 5733,
_ => 5732,
},
(5..=12, false) => 5732, // Neither 5731 nor 5732 is a leap year
(5, true) => 5730,
_ => { return Err(EcmaReferenceYearError::MonthCodeNotInCalendar);
}
};
Ok(HebrewYearInfo::compute(hebrew_year))
}
fn ordinal_month_from_code(
&self,
year: &Self::YearInfo,
month_code: types::ValidMonthCode,
options: DateFromFieldsOptions,
) -> Result<u8, MonthCodeError> { let is_leap_year = year.keviyah.is_leap(); let ordinal_month = match month_code.to_tuple() {
(n @ 1..=12, false) => n + (n >= 6 && is_leap_year) as u8,
(5, true) => { if is_leap_year { 6
} elseif matches!(options.overflow, Some(Overflow::Constrain)) { // M05L maps to M06 in a common year 6
} else { return Err(MonthCodeError::NotInYear);
}
}
_ => return Err(MonthCodeError::NotInCalendar),
};
Ok(ordinal_month)
}
implcrate::cal::scaffold::UnstableSealed for Hebrew {} impl Calendar for Hebrew { type DateInner = HebrewDateInner; type Year = types::EraYear; type DifferenceError = core::convert::Infallible;
fn from_rata_die(&self, rd: RataDie) -> Self::DateInner { let (year_info, year) = YearInfo::year_containing_rd(rd); let keviyah = year_info.keviyah;
// Obtaining a 1-indexed day-in-year value let day_in_year = u16::try_from(rd - year_info.new_year() + 1).unwrap_or(u16::MAX); let (month, day) = keviyah.month_day_for(day_in_year);
fn to_rata_die(&self, date: &Self::DateInner) -> RataDie { let ny = date.0.year.keviyah.year_info(date.0.year.value).new_year(); let days_preceding = date.0.year.keviyah.days_preceding(date.0.month);
// Need to subtract 1 since the new year is itself in this year
ny + i64::from(days_preceding) + i64::from(date.0.day) - 1
}
impl Date<Hebrew> { /// This method uses an ordinal month, which is probably not what you want. /// /// Use [`Date::try_new_from_codes`] #[deprecated(since = "2.1.0", note = "use `Date::try_new_from_codes`")] pubfn try_new_hebrew(
year: i32,
ordinal_month: u8,
day: u8,
) -> Result<Date<Hebrew>, RangeError> { let year = HebrewYearInfo::compute(year);
#[test] fn test_conversions() { for ((iso_y, iso_m, iso_d), (y, m, d)) in ISO_HEBREW_DATE_PAIRS.into_iter() { let iso_date = Date::try_new_iso(iso_y, iso_m, iso_d).unwrap(); let hebrew_date = Date::try_new_from_codes(Some("am"), y, m.to_month_code(), d, Hebrew)
.expect("Date should parse");
let iso_to_hebrew = iso_date.to_calendar(Hebrew);
let hebrew_to_iso = hebrew_date.to_iso();
assert_eq!(
hebrew_to_iso, iso_date, "Failed comparing to-ISO value for {hebrew_date:?} => {iso_date:?}"
);
assert_eq!(
iso_to_hebrew, hebrew_date, "Failed comparing to-hebrew value for {iso_date:?} => {hebrew_date:?}"
);
#[allow(deprecated)] // should still test let ordinal_hebrew_date = Date::try_new_hebrew(y, ordinal_month, d)
.expect("Construction of date must succeed");
assert_eq!(ordinal_hebrew_date, hebrew_date, "Hebrew date construction from codes and ordinals should work the same for {hebrew_date:?}");
}
}
#[test] fn test_icu_bug_22441() { let yi = YearInfo::compute_for(88369);
assert_eq!(yi.keviyah.year_length(), 383);
}
#[test] fn test_negative_era_years() { let greg_date = Date::try_new_gregorian(-5000, 1, 1).unwrap(); let greg_year = greg_date.era_year();
assert_eq!(greg_date.inner.0.year, -5000);
assert_eq!(greg_year.era, "bce"); // In Gregorian, era year is 1 - extended year
assert_eq!(greg_year.year, 5001); let hebr_date = greg_date.to_calendar(Hebrew); let hebr_year = hebr_date.era_year();
assert_eq!(hebr_date.inner.0.year.value, -1240);
assert_eq!(hebr_year.era, "am"); // In Hebrew, there is no inverse era, so negative extended years are negative era years
assert_eq!(hebr_year.year, -1240);
}
#[test] fn test_weekdays() { // https://github.com/unicode-org/icu4x/issues/4893 let cal = Hebrew::new(); let era = "am"; let month_code = MonthCode::new_normal(1).unwrap(); let dt = Date::try_new_from_codes(Some(era), 3760, month_code, 1, cal).unwrap();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.