Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  hebrew.rs

  Sprache: Rust
 

// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use crate::calendar_arithmetic::{ArithmeticDate, DateFieldsResolver, ToExtendedYear};
use crate::error::{
    DateError, DateFromFieldsError, EcmaReferenceYearError, MonthCodeError, UnknownEraError,
};
use crate::options::{DateAddOptions, DateDifferenceOptions};
use crate::options::{DateFromFieldsOptions, Overflow};
use crate::types::{DateFields, MonthInfo, ValidMonthCode};
use crate::RangeError;
use crate::{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
pub struct 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)]
pub struct HebrewDateInner(ArithmeticDate<Hebrew>);

impl Hebrew {
    /// Construct a new [`Hebrew`]
    pub fn new() -> Self {
        Hebrew
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub(cratestruct HebrewYearInfo {
    keviyah: Keviyah,
    value: i32,
}

impl ToExtendedYear for HebrewYearInfo {
    fn to_extended_year(&self) -> i32 {
        self.value
    }
}

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 months_in_provided_year(info: HebrewYearInfo) -> u8 {
        if info.keviyah.is_leap() {
            13
        } else {
            12
        }
    }

    #[inline]
    fn year_info_from_era(
        &self,
        era: &[u8],
        era_year: i32,
    ) -> Result<Self::YearInfo, UnknownEraError> {
        match era {
            b"am" => Ok(HebrewYearInfo::compute(era_year)),
            _ => Err(UnknownEraError),
        }
    }

    #[inline]
    fn year_info_from_extended(&self, extended_year: i32) -> Self::YearInfo {
        HebrewYearInfo::compute(extended_year)
    }

    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() {
            (1false) => 5733,
            (2false) => 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,
            },
            (3false) => match day {
                // There is no day 30 in 5733 (there is in 5732)
                ..=29 => 5733,
                _ => 5732,
            },
            (4false) => match day {
                ..=26 => 5733,
                _ => 5732,
            },
            (5..=12false) => 5732,
            // Neither 5731 nor 5732 is a leap year
            (5true) => 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..=12false) => n + (n >= 6 && is_leap_year) as u8,
            (5true) => {
                if is_leap_year {
                    6
                } else if 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)
    }

    fn month_code_from_ordinal(
        &self,
        year: &Self::YearInfo,
        ordinal_month: u8,
    ) -> types::ValidMonthCode {
        let is_leap = year.keviyah.is_leap();
        ValidMonthCode::new_unchecked(
            ordinal_month - (is_leap && ordinal_month >= 6as u8,
            ordinal_month == 6 && is_leap,
        )
    }
}

impl crate::cal::scaffold::UnstableSealed for Hebrew {}
impl Calendar for Hebrew {
    type DateInner = HebrewDateInner;
    type Year = types::EraYear;
    type DifferenceError = core::convert::Infallible;

    fn from_codes(
        &self,
        era: Option<&str>,
        year: i32,
        month_code: types::MonthCode,
        day: u8,
    ) -> Result<Self::DateInner, DateError> {
        ArithmeticDate::from_codes(era, year, month_code, day, self).map(HebrewDateInner)
    }

    #[cfg(feature = "unstable")]
    fn from_fields(
        &self,
        fields: DateFields,
        options: DateFromFieldsOptions,
    ) -> Result<Self::DateInner, DateFromFieldsError> {
        ArithmeticDate::from_fields(fields, options, self).map(HebrewDateInner)
    }

    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);

        HebrewDateInner(ArithmeticDate::new_unchecked(
            HebrewYearInfo {
                keviyah,
                value: year,
            },
            month,
            day,
        ))
    }

    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
    }

    fn has_cheap_iso_conversion(&self) -> bool {
        false
    }

    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
        Self::months_in_provided_year(date.0.year)
    }

    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
        date.0.year.keviyah.year_length()
    }

    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
        Self::days_in_provided_month(date.0.year, date.0.month)
    }

    #[cfg(feature = "unstable")]
    fn add(
        &self,
        date: &Self::DateInner,
        duration: types::DateDuration,
        options: DateAddOptions,
    ) -> Result<Self::DateInner, DateError> {
        date.0.added(duration, self, options).map(HebrewDateInner)
    }

    #[cfg(feature = "unstable")]
    fn until(
        &self,
        date1: &Self::DateInner,
        date2: &Self::DateInner,
        options: DateDifferenceOptions,
    ) -> Result<types::DateDuration, Self::DifferenceError> {
        Ok(date1.0.until(&date2.0self, options))
    }

    fn debug_name(&self) -> &'static str {
        "Hebrew"
    }

    fn year_info(&self, date: &Self::DateInner) -> Self::Year {
        let extended_year = date.0.year.value;
        types::EraYear {
            era_index: Some(0),
            era: tinystr!(16"am"),
            year: extended_year,
            extended_year,
            ambiguity: types::YearAmbiguity::CenturyRequired,
        }
    }

    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
        date.0.year.keviyah.is_leap()
    }

    fn month(&self, date: &Self::DateInner) -> MonthInfo {
        let valid_standard_code = self.month_code_from_ordinal(&date.0.year, date.0.month);

        let valid_formatting_code = if valid_standard_code.number() == 6 && date.0.month == 7 {
            ValidMonthCode::new_unchecked(6true// M06L
        } else {
            valid_standard_code
        };

        types::MonthInfo {
            ordinal: date.0.month,
            standard_code: valid_standard_code.to_month_code(),
            valid_standard_code,
            formatting_code: valid_formatting_code.to_month_code(),
            valid_formatting_code,
        }
    }

    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
        types::DayOfMonth(date.0.day)
    }

    fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
        types::DayOfYear(date.0.year.keviyah.days_preceding(date.0.month) + date.0.day as u16)
    }

    fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
        Some(crate::preferences::CalendarAlgorithm::Hebrew)
    }
}

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`")]
    pub fn try_new_hebrew(
        year: i32,
        ordinal_month: u8,
        day: u8,
    ) -> Result<Date<Hebrew>, RangeError> {
        let year = HebrewYearInfo::compute(year);

        ArithmeticDate::try_from_ymd(year, ordinal_month, day)
            .map(HebrewDateInner)
            .map(|inner| Date::from_raw(inner, Hebrew))
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::types::MonthCode;

    pub const TISHREI: ValidMonthCode = ValidMonthCode::new_unchecked(1false);
    pub const ḤESHVAN: ValidMonthCode = ValidMonthCode::new_unchecked(2false);
    pub const KISLEV: ValidMonthCode = ValidMonthCode::new_unchecked(3false);
    pub const TEVET: ValidMonthCode = ValidMonthCode::new_unchecked(4false);
    pub const SHEVAT: ValidMonthCode = ValidMonthCode::new_unchecked(5false);
    pub const ADARI: ValidMonthCode = ValidMonthCode::new_unchecked(5true);
    pub const ADAR: ValidMonthCode = ValidMonthCode::new_unchecked(6false);
    pub const NISAN: ValidMonthCode = ValidMonthCode::new_unchecked(7false);
    pub const IYYAR: ValidMonthCode = ValidMonthCode::new_unchecked(8false);
    pub const SIVAN: ValidMonthCode = ValidMonthCode::new_unchecked(9false);
    pub const TAMMUZ: ValidMonthCode = ValidMonthCode::new_unchecked(10false);
    pub const AV: ValidMonthCode = ValidMonthCode::new_unchecked(11false);
    pub const ELUL: ValidMonthCode = ValidMonthCode::new_unchecked(12false);

    /// The leap years used in the tests below
    const LEAP_YEARS_IN_TESTS: [i32; 1] = [5782];
    /// (iso, hebrew) pairs of testcases. If any of the years here
    /// are leap years please add them to LEAP_YEARS_IN_TESTS (we have this manually
    /// so we don't end up exercising potentially buggy codepaths to test this)
    #[expect(clippy::type_complexity)]
    const ISO_HEBREW_DATE_PAIRS: [((i32, u8, u8), (i32, ValidMonthCode, u8)); 48] = [
        ((2021110), (5781, TEVET, 26)),
        ((2021125), (5781, SHEVAT, 12)),
        ((2021210), (5781, SHEVAT, 28)),
        ((2021225), (5781, ADAR, 13)),
        ((2021310), (5781, ADAR, 26)),
        ((2021325), (5781, NISAN, 12)),
        ((2021410), (5781, NISAN, 28)),
        ((2021425), (5781, IYYAR, 13)),
        ((2021510), (5781, IYYAR, 28)),
        ((2021525), (5781, SIVAN, 14)),
        ((2021610), (5781, SIVAN, 30)),
        ((2021625), (5781, TAMMUZ, 15)),
        ((2021710), (5781, AV, 1)),
        ((2021725), (5781, AV, 16)),
        ((2021810), (5781, ELUL, 2)),
        ((2021825), (5781, ELUL, 17)),
        ((2021910), (5782, TISHREI, 4)),
        ((2021925), (5782, TISHREI, 19)),
        ((20211010), (5782, ḤESHVAN, 4)),
        ((20211025), (5782, ḤESHVAN, 19)),
        ((20211110), (5782, KISLEV, 6)),
        ((20211125), (5782, KISLEV, 21)),
        ((20211210), (5782, TEVET, 6)),
        ((20211225), (5782, TEVET, 21)),
        ((2022110), (5782, SHEVAT, 8)),
        ((2022125), (5782, SHEVAT, 23)),
        ((2022210), (5782, ADARI, 9)),
        ((2022225), (5782, ADARI, 24)),
        ((2022310), (5782, ADAR, 7)),
        ((2022325), (5782, ADAR, 22)),
        ((2022410), (5782, NISAN, 9)),
        ((2022425), (5782, NISAN, 24)),
        ((2022510), (5782, IYYAR, 9)),
        ((2022525), (5782, IYYAR, 24)),
        ((2022610), (5782, SIVAN, 11)),
        ((2022625), (5782, SIVAN, 26)),
        ((2022710), (5782, TAMMUZ, 11)),
        ((2022725), (5782, TAMMUZ, 26)),
        ((2022810), (5782, AV, 13)),
        ((2022825), (5782, AV, 28)),
        ((2022910), (5782, ELUL, 14)),
        ((2022925), (5782, ELUL, 29)),
        ((20221010), (5783, TISHREI, 15)),
        ((20221025), (5783, TISHREI, 30)),
        ((20221110), (5783, ḤESHVAN, 16)),
        ((20221125), (5783, KISLEV, 1)),
        ((20221210), (5783, KISLEV, 16)),
        ((20221225), (5783, TEVET, 1)),
    ];

    #[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:?}"
            );

            let ordinal_month = if (m == ADARI || m.number() >= ADAR.number())
                && LEAP_YEARS_IN_TESTS.contains(&y)
            {
                m.number() + 1
            } else {
                assert!(m != ADARI);
                m.number()
            };

            #[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(-500011).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();

        // Should be Saturday per:
        // https://www.hebcal.com/converter?hd=1&hm=Tishrei&hy=3760&h2g=1
        assert_eq!(6, dt.day_of_week() as usize);
    }
}

Messung V0.5 in Prozent
C=89 H=100 G=94

¤ Dauer der Verarbeitung: 0.8 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002