// This file is part of ICU4X. // // The contents of this file implement algorithms from Calendrical Calculations // by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018), // which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/> // under the Apache-2.0 license. Accordingly, this file is released under // the Apache License, Version 2.0 which can be found at the calendrical_calculations // package root or at http://www.apache.org/licenses/LICENSE-2.0.
usecrate::helpers::{final_func, i64_to_i32, next_u8}; usecrate::rata_die::{Moment, RataDie}; #[allow(unused_imports)] use core_maths::*;
/// Biblical Hebrew dates. The months are reckoned a bit strangely, with the new year occurring on /// Tishri (as in the civil calendar) but the months being numbered in a different order #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)] #[allow(clippy::exhaustive_structs)] pubstruct BookHebrew { /// The year pub year: i32, /// The month pub month: u8, /// The day pub day: u8,
}
// The BookHebrew Months /// The biblical month number used for the month of Nisan pubconst NISAN: u8 = 1; /// The biblical month number used for the month of Iyyar pubconst IYYAR: u8 = 2; /// The biblical month number used for the month of Sivan pubconst SIVAN: u8 = 3; /// The biblical month number used for the month of Tammuz pubconst TAMMUZ: u8 = 4; /// The biblical month number used for the month of Av pubconst AV: u8 = 5; /// The biblical month number used for the month of Elul pubconst ELUL: u8 = 6; /// The biblical month number used for the month of Tishri pubconst TISHRI: u8 = 7; /// The biblical month number used for the month of Marheshvan pubconst MARHESHVAN: u8 = 8; /// The biblical month number used for the month of Kislev pubconst KISLEV: u8 = 9; /// The biblical month number used for the month of Tevet pubconst TEVET: u8 = 10; /// The biblical month number used for the month of Shevat pubconst SHEVAT: u8 = 11; /// The biblical month number used for the month of Adar (and Adar I) pubconst ADAR: u8 = 12; /// The biblical month number used for the month of Adar II pubconst ADARII: u8 = 13;
// BIBLICAL HEBREW CALENDAR FUNCTIONS
impl BookHebrew { /// The civil calendar has the same year and day numbering as the book one, but the months are numbered /// differently pubfn to_civil_date(self) -> (i32, u8, u8) { let biblical_month = self.month; let biblical_year = self.year; letmut civil_month;
civil_month = (biblical_month + 6) % 12;
/// The civil calendar has the same year and day numbering as the book one, but the months are numbered /// differently pubfn from_civil_date(civil_year: i32, civil_month: u8, civil_day: u8) -> Self { letmut biblical_month;
if civil_month <= 6 {
biblical_month = civil_month + 6; // months 1-6 correspond to biblical months 7-12
} else {
biblical_month = civil_month - 6; // months 7-12 correspond to biblical months 1-6 ifSelf::is_hebrew_leap_year(civil_year) {
biblical_month -= 1
} if biblical_month == 0 { // Special case for Adar II in a leap year
biblical_month = 13;
}
}
BookHebrew {
year: civil_year,
month: biblical_month,
day: civil_day,
}
} // Moment of mean conjunction (New Moon) of h_month in BookHebrew /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2244> #[allow(dead_code)] pub(crate) fn molad(book_year: i32, book_month: u8) -> Moment { let y = if book_month < TISHRI {
book_year + 1
} else {
book_year
}; // Treat Nisan as start of year
let months_elapsed = (book_month as f64 - TISHRI as f64) // Months this year
+ ((235.0 * y as f64 - 234.0) / 19.0).floor(); // Months until New Year.
// Number of days elapsed from the (Sunday) noon prior to the epoch of the BookHebrew Calendar to the molad of Tishri of BookHebrew year (h_year) or one day later /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2261> fn book_hebrew_calendar_elapsed_days(book_year: i32) -> i32 { let months_elapsed = ((235.0 * book_year as f64 - 234.0) / 19.0).floor() as i64; let parts_elapsed = 12084 + 13753 * months_elapsed; let days = 29 * months_elapsed + (parts_elapsed as f64 / 25920.0).floor() as i64;
if (3 * (days + 1)).rem_euclid(7) < 3 {
days as i32 + 1
} else {
days as i32
}
}
// Delays to start of BookHebrew year to keep ordinary year in range 353-356 and leap year in range 383-386 /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2301> fn book_hebrew_year_length_correction(book_year: i32) -> u8 { let ny0 = Self::book_hebrew_calendar_elapsed_days(book_year - 1); let ny1 = Self::book_hebrew_calendar_elapsed_days(book_year); let ny2 = Self::book_hebrew_calendar_elapsed_days(book_year + 1);
// Fixed date of BookHebrew new year /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2294> pubfn book_hebrew_new_year(book_year: i32) -> RataDie {
RataDie::new(
FIXED_HEBREW_EPOCH.to_i64_date()
+ Self::book_hebrew_calendar_elapsed_days(book_year) as i64
+ Self::book_hebrew_year_length_correction(book_year) as i64,
)
}
// True if the month Marheshvan is going to be long in given BookHebrew year /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2321> #[allow(dead_code)] fn is_long_marheshvan(book_year: i32) -> bool { let long_marheshavan_year_lengths = [355, 385];
long_marheshavan_year_lengths.contains(&Self::days_in_book_hebrew_year(book_year))
}
// True if the month Kislve is going to be short in given BookHebrew year /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2326> #[allow(dead_code)] fn is_short_kislev(book_year: i32) -> bool { let short_kislev_year_lengths = [353, 383];
short_kislev_year_lengths.contains(&Self::days_in_book_hebrew_year(book_year))
}
// Last day of month (h_month) in BookHebrew year (book_year) /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2230> pubfn last_day_of_book_hebrew_month(book_year: i32, book_month: u8) -> u8 { match book_month {
IYYAR | TAMMUZ | ELUL | TEVET | ADARII => 29,
ADAR => { if !Self::is_hebrew_leap_year(book_year) { 29
} else { 30
}
}
MARHESHVAN => { if !Self::is_long_marheshvan(book_year) { 29
} else { 30
}
}
KISLEV => { ifSelf::is_short_kislev(book_year) { 29
} else { 30
}
}
_ => 30,
}
}
letmut total_days = Self::book_hebrew_new_year(book_year) + book_day.into() - 1; // (day - 1) Days so far this month.
if book_month < TISHRI { // Then add days in prior months this year before for m in
(TISHRI..=Self::last_month_of_book_hebrew_year(book_year)).chain(NISAN..book_month)
{
total_days += Self::last_day_of_book_hebrew_month(book_year, m).into();
}
} else { // Else add days in prior months this year for m in TISHRI..book_month {
total_days += Self::last_day_of_book_hebrew_month(book_year, m).into();
}
}
total_days
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2352> pubfn book_hebrew_from_fixed(date: RataDie) -> BookHebrew { let approx = i64_to_i32( 1 + ((date - FIXED_HEBREW_EPOCH) as f64).div_euclid(35975351.0 / 98496.0) as i64, // The value 35975351/98496, the average length of a BookHebrew year, can be approximated by 365.25
)
.unwrap_or_else(|e| e.saturate());
// Search forward for the year let year_condition = |year: i32| Self::book_hebrew_new_year(year) <= date; let year = final_func(approx - 1, year_condition);
// Starting month for search for month. let start = if date
< Self::fixed_from_book_hebrew(BookHebrew {
year,
month: NISAN,
day: 1,
}) {
TISHRI
} else {
NISAN
};
let month_condition = |m: u8| {
date <= Self::fixed_from_book_hebrew(BookHebrew {
year,
month: m,
day: Self::last_day_of_book_hebrew_month(year, m),
})
}; // Search forward from either Tishri or Nisan. let month = next_u8(start, month_condition);
// Calculate the day by subtraction. let day = (date
- Self::fixed_from_book_hebrew(BookHebrew {
year,
month,
day: 1,
}))
+ 1;
#[test] fn test_hebrew_epoch() { // page 119 of the Calendrical Calculations book let fixed_hebrew_date = -1373427.0;
assert_eq!(FIXED_HEBREW_EPOCH.to_f64_date(), fixed_hebrew_date);
}
#[test] fn test_hebrew_molad() { let precision = 1_00000f64; for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_MOLAD_DATES.iter()) { let molad =
(BookHebrew::molad(case.year, case.month).inner() * precision).round() / precision; let final_expected = (expected * precision).round() / precision;
assert_eq!(molad, final_expected, "{case:?}");
}
}
#[test] fn test_last_book_hebrew_month() { for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_LAST_HEBREW_MONTH.iter()) { let last_month = BookHebrew::last_month_of_book_hebrew_year(case.year);
assert_eq!(last_month, *expected);
}
}
#[test] fn test_book_hebrew_calendar_elapsed_days() { for (case, expected) in HEBREW_DATES
.iter()
.zip(EXPECTED_HEBREW_ELASPED_CALENDAR_DAYS.iter())
{ let elapsed_days = BookHebrew::book_hebrew_calendar_elapsed_days(case.year);
assert_eq!(elapsed_days, *expected);
}
}
#[test] fn test_civil_to_book_conversion() { for (f_date, case) in TEST_FIXED_DATE.iter().zip(HEBREW_DATES.iter()) { let book_hebrew = BookHebrew::book_hebrew_from_fixed(RataDie::new(*f_date)); let (y, m, d) = book_hebrew.to_civil_date(); let book_hebrew = BookHebrew::from_civil_date(y, m, d);
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.