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


Quelle  islamic.rs   Sprache: unbekannt

 
// 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 ).

//! This module contains types and implementations for the Islamic calendars.
//!
//! ```rust
//! use icu::calendar::islamic::IslamicObservational;
//! use icu::calendar::{Date, DateTime, Ref};
//!
//! let islamic = IslamicObservational::new_always_calculating();
//! let islamic = Ref(&islamic); // to avoid cloning
//!
//! // `Date` type
//! let islamic_date =
//!     Date::try_new_observational_islamic_date(1348, 10, 11, islamic)
//!         .expect("Failed to initialize islamic Date instance.");
//!
//! // `DateTime` type
//! let islamic_datetime = DateTime::try_new_observational_islamic_datetime(
//!     1348, 10, 11, 13, 1, 0, islamic,
//! )
//! .expect("Failed to initialize islamic DateTime instance.");
//!
//! // `Date` checks
//! assert_eq!(islamic_date.year().number, 1348);
//! assert_eq!(islamic_date.month().ordinal, 10);
//! assert_eq!(islamic_date.day_of_month().0, 11);
//!
//! // `DateTime` checks
//! assert_eq!(islamic_datetime.date.year().number, 1348);
//! assert_eq!(islamic_datetime.date.month().ordinal, 10);
//! assert_eq!(islamic_datetime.date.day_of_month().0, 11);
//! assert_eq!(islamic_datetime.time.hour.number(), 13);
//! assert_eq!(islamic_datetime.time.minute.number(), 1);
//! assert_eq!(islamic_datetime.time.second.number(), 0);
//! ```

use crate::calendar_arithmetic::PrecomputedDataSource;
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::provider::islamic::{
    IslamicCacheV1, IslamicObservationalCacheV1Marker, IslamicUmmAlQuraCacheV1Marker,
    PackedIslamicYearInfo,
};
use crate::AnyCalendarKind;
use crate::AsCalendar;
use crate::Iso;
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time};
use calendrical_calculations::islamic::{
    IslamicBasedMarker, ObservationalIslamicMarker, SaudiIslamicMarker,
};
use calendrical_calculations::rata_die::RataDie;
use core::marker::PhantomData;
use icu_provider::prelude::*;
use tinystr::tinystr;

/// Islamic Observational Calendar (Default)
///
/// # Era codes
///
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
///
/// # Month codes
///
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
/// `"M01" - "M12"`.
#[derive(Clone, Debug, Default)]
pub struct IslamicObservational {
    data: Option<DataPayload<IslamicObservationalCacheV1Marker>>,
}

/// Civil / Arithmetical Islamic Calendar (Used for administrative purposes)
///
/// # Era codes
///
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
///
/// # Month codes
///
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
/// `"M01" - "M12"`.
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // unit struct
pub struct IslamicCivil;

/// Umm al-Qura Hijri Calendar (Used in Saudi Arabia)
///
/// # Era codes
///
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
///
/// # Month codes
///
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
/// `"M01" - "M12"`.
#[derive(Clone, Debug, Default)]
pub struct IslamicUmmAlQura {
    data: Option<DataPayload<IslamicUmmAlQuraCacheV1Marker>>,
}

/// A Tabular version of the Arithmetical Islamic Calendar
///
/// # Era codes
///
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
///
/// # Month codes
///
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
/// `"M01" - "M12"`.
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // unit struct
pub struct IslamicTabular;

impl IslamicObservational {
    /// Creates a new [`IslamicObservational`] with some compiled data containing precomputed calendrical calculations.
    ///
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
    ///
    /// [�� Help choosing a constructor](icu_provider::constructors)
    #[cfg(feature = "compiled_data")]
    pub const fn new() -> Self {
        Self {
            data: Some(DataPayload::from_static_ref(
                crate::provider::Baked::SINGLETON_CALENDAR_ISLAMICOBSERVATIONALCACHE_V1,
            )),
        }
    }

    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
        #[cfg(skip)]
        functions: [
            new,
            try_new_with_any_provider,
            try_new_with_buffer_provider,
            try_new_unstable,
            Self,
    ]);

    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
    pub fn try_new_unstable<D: DataProvider<IslamicObservationalCacheV1Marker> + ?Sized>(
        provider: &D,
    ) -> Result<Self, CalendarError> {
        Ok(Self {
            data: Some(provider.load(Default::default())?.take_payload()?),
        })
    }

    /// Construct a new [`IslamicObservational`] without any precomputed calendrical calculations.
    pub fn new_always_calculating() -> Self {
        Self { data: None }
    }
}

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

    /// Construct a new [`IslamicCivil`] (deprecated: we will not add precomputation to this calendar)
    #[deprecated = "Precomputation not needed for this calendar"]
    pub fn new_always_calculating() -> Self {
        Self
    }
}

impl IslamicUmmAlQura {
    /// Creates a new [`IslamicUmmAlQura`] with some compiled data containing precomputed calendrical calculations.
    ///
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
    ///
    /// [�� Help choosing a constructor](icu_provider::constructors)
    #[cfg(feature = "compiled_data")]
    pub const fn new() -> Self {
        Self {
            data: Some(DataPayload::from_static_ref(
                crate::provider::Baked::SINGLETON_CALENDAR_ISLAMICUMMALQURACACHE_V1,
            )),
        }
    }

    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
        #[cfg(skip)]
        functions: [
            new,
            try_new_with_any_provider,
            try_new_with_buffer_provider,
            try_new_unstable,
            Self,
    ]);

    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
    pub fn try_new_unstable<D: DataProvider<IslamicUmmAlQuraCacheV1Marker> + ?Sized>(
        provider: &D,
    ) -> Result<Self, CalendarError> {
        Ok(Self {
            data: Some(provider.load(Default::default())?.take_payload()?),
        })
    }

    /// Construct a new [`IslamicUmmAlQura`] without any precomputed calendrical calculations.
    pub fn new_always_calculating() -> Self {
        Self { data: None }
    }
}

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

    /// Construct a new [`IslamicTabular`] (deprecated: we will not add precomputation to this calendar)
    #[deprecated = "Precomputation not needed for this calendar"]
    pub fn new_always_calculating() -> Self {
        Self
    }
}

/// Compact representation of the length of an Islamic year.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum IslamicYearLength {
    /// Long (355-day) Islamic year
    L355,
    /// Short (354-day) Islamic year
    L354,
    /// Unexpectedly Short (353-day) Islamic year
    ///
    /// It is probably a bug when this year length is returned. See:
    /// <https://github.com/unicode-org/icu4x/issues/4930>
    L353,
}

impl Default for IslamicYearLength {
    fn default() -> Self {
        Self::L354
    }
}

impl IslamicYearLength {
    fn try_from_int(value: i64) -> Option<Self> {
        match value {
            355 => Some(Self::L355),
            354 => Some(Self::L354),
            353 => Some(Self::L353),
            _ => None,
        }
    }
    fn to_int(self) -> u16 {
        match self {
            Self::L355 => 355,
            Self::L354 => 354,
            Self::L353 => 353,
        }
    }
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct IslamicYearInfo {
    packed_data: PackedIslamicYearInfo,
    prev_year_length: IslamicYearLength,
}

impl IslamicYearInfo {
    pub(crate) const LONG_YEAR_LEN: u16 = 355;
    const SHORT_YEAR_LEN: u16 = 354;
    pub(crate) fn new(
        prev_packed: PackedIslamicYearInfo,
        this_packed: PackedIslamicYearInfo,
        extended_year: i32,
    ) -> (Self, i32) {
        let days_in_year = prev_packed.days_in_year();
        let days_in_year = match IslamicYearLength::try_from_int(days_in_year as i64) {
            Some(x) => x,
            None => {
                debug_assert!(false, "Found wrong year length for Islamic year {extended_year}: Expected 355, 354, or 353, got {days_in_year}");
                Default::default()
            }
        };
        let year_info = Self {
            prev_year_length: days_in_year,
            packed_data: this_packed,
        };
        (year_info, extended_year)
    }

    fn compute<IB: IslamicBasedMarker>(extended_year: i32) -> Self {
        let ny = IB::fixed_from_islamic(extended_year, 1, 1);
        let packed_data = PackedIslamicYearInfo::compute_with_ny::<IB>(extended_year, ny);
        let prev_ny = IB::fixed_from_islamic(extended_year - 1, 1, 1);
        let rd_diff = ny - prev_ny;
        let rd_diff = match IslamicYearLength::try_from_int(rd_diff) {
            Some(x) => x,
            None => {
                debug_assert!(false, "({}) Found wrong year length for Islamic year {extended_year}: Expected 355, 354, or 353, got {rd_diff}", IB::DEBUG_NAME);
                Default::default()
            }
        };
        Self {
            prev_year_length: rd_diff,
            packed_data,
        }
    }
    /// Get the new year R.D. given the extended year that this yearinfo is for    
    fn new_year<IB: IslamicBasedMarker>(self, extended_year: i32) -> RataDie {
        IB::mean_synodic_ny(extended_year) + i64::from(self.packed_data.ny_offset())
    }

    /// Get the date's R.D. given (y, m, d) in this info's year
    fn rd_for<IB: IslamicBasedMarker>(self, extended_year: i32, month: u8, day: u8) -> RataDie {
        let ny = self.new_year::<IB>(extended_year);
        let month_offset = if month == 1 {
            0
        } else {
            self.packed_data.last_day_of_month(month - 1)
        };
        // -1 since the offset is 1-indexed but the new year is also day 1
        ny - 1 + month_offset.into() + day.into()
    }

    #[inline]
    fn days_in_prev_year(self) -> u16 {
        self.prev_year_length.to_int()
    }
}

/// Contains any loaded precomputed data. If constructed with Default, will
/// *not* contain any extra data and will always compute stuff from scratch
#[derive(Default)]
pub(crate) struct IslamicPrecomputedData<'a, IB: IslamicBasedMarker> {
    data: Option<&'a IslamicCacheV1<'a>>,
    _ib: PhantomData<IB>,
}

impl<'b, IB: IslamicBasedMarker> PrecomputedDataSource<IslamicYearInfo>
    for IslamicPrecomputedData<'b, IB>
{
    fn load_or_compute_info(&self, extended_year: i32) -> IslamicYearInfo {
        self.data
            .and_then(|d| d.get_for_extended_year(extended_year))
            .unwrap_or_else(|| IslamicYearInfo::compute::<IB>(extended_year))
    }
}

/// Given a year info and the first month it is possible for this date to be in, return the
/// month and day this is in
fn compute_month_day(info: IslamicYearInfo, mut possible_month: u8, day_of_year: u16) -> (u8, u8) {
    let mut last_day_of_month = info.packed_data.last_day_of_month(possible_month);
    let mut last_day_of_prev_month = if possible_month == 1 {
        0
    } else {
        info.packed_data.last_day_of_month(possible_month - 1)
    };

    while day_of_year > last_day_of_month && possible_month <= 12 {
        possible_month += 1;
        last_day_of_prev_month = last_day_of_month;
        last_day_of_month = info.packed_data.last_day_of_month(possible_month);
    }
    let day = u8::try_from(day_of_year - last_day_of_prev_month);
    debug_assert!(
        day.is_ok(),
        "Found day {} that doesn't fit in month!",
        day_of_year - last_day_of_prev_month
    );
    (possible_month, day.unwrap_or(29))
}
impl<'b, IB: IslamicBasedMarker> IslamicPrecomputedData<'b, IB> {
    pub(crate) fn new(data: Option<&'b IslamicCacheV1<'b>>) -> Self {
        Self {
            data,
            _ib: PhantomData,
        }
    }
    /// Given an ISO date (in both ArithmeticDate and R.D. format), returns the IslamicYearInfo and extended year for that date, loading
    /// from cache or computing.
    fn load_or_compute_info_for_iso(&self, fixed: RataDie) -> (IslamicYearInfo, i32, u8, u8) {
        let cached = self.data.and_then(|d| d.get_for_fixed::<IB>(fixed));
        if let Some((cached, year)) = cached {
            let ny = cached.packed_data.ny::<IB>(year);
            let day_of_year = (fixed - ny) as u16 + 1;
            debug_assert!(day_of_year < 360);
            // We divide by 30, not 29, to account for the case where all months before this
            // were length 30 (possible near the beginning of the year)
            // We add +1 because months are 1-indexed
            let possible_month = u8::try_from(1 + (day_of_year / 30)).unwrap_or(1);
            let (m, d) = compute_month_day(cached, possible_month, day_of_year);
            return (cached, year, m, d);
        };
        // compute

        let (y, m, d) = IB::islamic_from_fixed(fixed);
        let info = IslamicYearInfo::compute::<IB>(y);
        let ny = info.packed_data.ny::<IB>(y);
        let day_of_year = (fixed - ny) as u16 + 1;
        // We can't use the m/d from islamic_from_fixed because that code
        // occasionally throws up 31-day months, which we normalize out. So we instead back-compute, starting with the previous month
        let (m, d) = if m > 1 {
            compute_month_day(info, m - 1, day_of_year)
        } else {
            (m, d)
        };
        (info, y, m, d)
    }
}

/// The inner date type used for representing [`Date`]s of [`IslamicObservational`]. See [`Date`] and [`IslamicObservational`] for more details.

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct IslamicDateInner(ArithmeticDate<IslamicObservational>);

impl CalendarArithmetic for IslamicObservational {
    type YearInfo = IslamicYearInfo;

    fn month_days(_year: i32, month: u8, year_info: IslamicYearInfo) -> u8 {
        year_info.packed_data.days_in_month(month)
    }

    fn months_for_every_year(_year: i32, _year_info: IslamicYearInfo) -> u8 {
        12
    }

    fn days_in_provided_year(_year: i32, year_info: IslamicYearInfo) -> u16 {
        year_info.packed_data.days_in_year()
    }

    // As an true lunar calendar, it does not have leap years.
    fn is_leap_year(_year: i32, year_info: IslamicYearInfo) -> bool {
        year_info.packed_data.days_in_year() != IslamicYearInfo::SHORT_YEAR_LEN
    }

    fn last_month_day_in_year(year: i32, year_info: IslamicYearInfo) -> (u8, u8) {
        let days = Self::month_days(year, 12, year_info);

        (12, days)
    }
}

impl Calendar for IslamicObservational {
    type DateInner = IslamicDateInner;
    fn date_from_codes(
        &self,
        era: types::Era,
        year: i32,
        month_code: types::MonthCode,
        day: u8,
    ) -> Result<Self::DateInner, CalendarError> {
        let year = if era.0 == tinystr!(16, "islamic") || era.0 == tinystr!(16, "ah") {
            year
        } else {
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
        };
        let month = if let Some((ordinal, false)) = month_code.parsed() {
            ordinal
        } else {
            return Err(CalendarError::UnknownMonthCode(
                month_code.0,
                self.debug_name(),
            ));
        };
        ArithmeticDate::new_from_ordinals_with_info(
            year,
            month,
            day,
            self.precomputed_data().load_or_compute_info(year),
        )
        .map(IslamicDateInner)
    }

    fn date_from_iso(&self, iso: Date<crate::Iso>) -> Self::DateInner {
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());

        let (year_info, y, m, d) = self
            .precomputed_data()
            .load_or_compute_info_for_iso(fixed_iso);
        IslamicDateInner(ArithmeticDate::new_unchecked_with_info(y, m, d, year_info))
    }

    fn date_to_iso(&self, date: &Self::DateInner) -> Date<crate::Iso> {
        let fixed = date.0.year_info.rd_for::<ObservationalIslamicMarker>(
            date.0.year,
            date.0.month,
            date.0.day,
        );
        Iso::iso_from_fixed(fixed)
    }

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

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

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

    fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
        Iso.day_of_week(self.date_to_iso(date).inner())
    }

    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
        date.0.offset_date(offset, &self.precomputed_data())
    }

    fn until(
        &self,
        date1: &Self::DateInner,
        date2: &Self::DateInner,
        _calendar2: &Self,
        _largest_unit: DateDurationUnit,
        _smallest_unit: DateDurationUnit,
    ) -> DateDuration<Self> {
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
    }

    fn debug_name(&self) -> &'static str {
        Self::DEBUG_NAME
    }

    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
        Self::year_as_islamic(date.0.year)
    }

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

    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
        date.0.month()
    }

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

    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
        let prev_year = date.0.year.saturating_sub(1);
        let next_year = date.0.year.saturating_add(1);
        types::DayOfYearInfo {
            day_of_year: date.0.day_of_year(),
            days_in_year: date.0.days_in_year(),
            prev_year: Self::year_as_islamic(prev_year),
            days_in_prev_year: date.0.year_info.days_in_prev_year(),
            next_year: Self::year_as_islamic(next_year),
        }
    }

    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
        Some(AnyCalendarKind::IslamicObservational)
    }
}

impl IslamicObservational {
    fn precomputed_data(&self) -> IslamicPrecomputedData<ObservationalIslamicMarker> {
        IslamicPrecomputedData::new(self.data.as_ref().map(|x| x.get()))
    }

    fn year_as_islamic(year: i32) -> types::FormattableYear {
        types::FormattableYear {
            era: types::Era(tinystr!(16, "islamic")),
            number: year,
            cyclic: None,
            related_iso: None,
        }
    }
    pub(crate) const DEBUG_NAME: &'static str = "Islamic (observational)";
}

impl<A: AsCalendar<Calendar = IslamicObservational>> Date<A> {
    /// Construct new Islamic Observational Date.
    ///
    /// Has no negative years, only era is the AH.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicObservational;
    /// use icu::calendar::Date;
    ///
    /// let islamic = IslamicObservational::new_always_calculating();
    ///
    /// let date_islamic =
    ///     Date::try_new_observational_islamic_date(1392, 4, 25, islamic)
    ///         .expect("Failed to initialize Islamic Date instance.");
    ///
    /// assert_eq!(date_islamic.year().number, 1392);
    /// assert_eq!(date_islamic.month().ordinal, 4);
    /// assert_eq!(date_islamic.day_of_month().0, 25);
    /// ```
    pub fn try_new_observational_islamic_date(
        year: i32,
        month: u8,
        day: u8,
        calendar: A,
    ) -> Result<Date<A>, CalendarError> {
        let year_info = calendar
            .as_calendar()
            .precomputed_data()
            .load_or_compute_info(year);
        ArithmeticDate::new_from_ordinals_with_info(year, month, day, year_info)
            .map(IslamicDateInner)
            .map(|inner| Date::from_raw(inner, calendar))
    }
}

impl<A: AsCalendar<Calendar = IslamicObservational>> DateTime<A> {
    /// Construct a new Islamic Observational datetime from integers.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicObservational;
    /// use icu::calendar::DateTime;
    ///
    /// let islamic = IslamicObservational::new_always_calculating();
    ///
    /// let datetime_islamic = DateTime::try_new_observational_islamic_datetime(
    ///     474, 10, 11, 13, 1, 0, islamic,
    /// )
    /// .expect("Failed to initialize Islamic DateTime instance.");
    ///
    /// assert_eq!(datetime_islamic.date.year().number, 474);
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
    /// ```
    pub fn try_new_observational_islamic_datetime(
        year: i32,
        month: u8,
        day: u8,
        hour: u8,
        minute: u8,
        second: u8,
        calendar: A,
    ) -> Result<DateTime<A>, CalendarError> {
        Ok(DateTime {
            date: Date::try_new_observational_islamic_date(year, month, day, calendar)?,
            time: Time::try_new(hour, minute, second, 0)?,
        })
    }
}

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
/// The inner date type used for representing [`Date`]s of [`IslamicUmmAlQura`]. See [`Date`] and [`IslamicUmmAlQura`] for more details.
pub struct IslamicUmmAlQuraDateInner(ArithmeticDate<IslamicUmmAlQura>);

impl CalendarArithmetic for IslamicUmmAlQura {
    type YearInfo = IslamicYearInfo;

    fn month_days(_year: i32, month: u8, year_info: IslamicYearInfo) -> u8 {
        year_info.packed_data.days_in_month(month)
    }

    fn months_for_every_year(_year: i32, _year_info: IslamicYearInfo) -> u8 {
        12
    }

    fn days_in_provided_year(_year: i32, year_info: IslamicYearInfo) -> u16 {
        year_info.packed_data.days_in_year()
    }

    // As an true lunar calendar, it does not have leap years.
    fn is_leap_year(_year: i32, year_info: IslamicYearInfo) -> bool {
        year_info.packed_data.days_in_year() != IslamicYearInfo::SHORT_YEAR_LEN
    }

    fn last_month_day_in_year(year: i32, year_info: IslamicYearInfo) -> (u8, u8) {
        let days = Self::month_days(year, 12, year_info);

        (12, days)
    }
}

impl Calendar for IslamicUmmAlQura {
    type DateInner = IslamicUmmAlQuraDateInner;
    fn date_from_codes(
        &self,
        era: types::Era,
        year: i32,
        month_code: types::MonthCode,
        day: u8,
    ) -> Result<Self::DateInner, CalendarError> {
        let year = if era.0 == tinystr!(16, "islamic-umalqura")
            || era.0 == tinystr!(16, "islamic")
            || era.0 == tinystr!(16, "ah")
        {
            year
        } else {
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
        };

        let month = if let Some((ordinal, false)) = month_code.parsed() {
            ordinal
        } else {
            return Err(CalendarError::UnknownMonthCode(
                month_code.0,
                self.debug_name(),
            ));
        };
        ArithmeticDate::new_from_ordinals_with_info(
            year,
            month,
            day,
            self.precomputed_data().load_or_compute_info(year),
        )
        .map(IslamicUmmAlQuraDateInner)
    }

    fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());

        let (year_info, y, m, d) = self
            .precomputed_data()
            .load_or_compute_info_for_iso(fixed_iso);
        IslamicUmmAlQuraDateInner(ArithmeticDate::new_unchecked_with_info(y, m, d, year_info))
    }

    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
        let fixed =
            date.0
                .year_info
                .rd_for::<SaudiIslamicMarker>(date.0.year, date.0.month, date.0.day);
        Iso::iso_from_fixed(fixed)
    }

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

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

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

    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
        date.0.offset_date(offset, &self.precomputed_data())
    }

    fn until(
        &self,
        date1: &Self::DateInner,
        date2: &Self::DateInner,
        _calendar2: &Self,
        _largest_unit: DateDurationUnit,
        _smallest_unit: DateDurationUnit,
    ) -> DateDuration<Self> {
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
    }

    fn debug_name(&self) -> &'static str {
        Self::DEBUG_NAME
    }

    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
        Self::year_as_islamic(date.0.year)
    }

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

    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
        date.0.month()
    }

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

    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
        let prev_year = date.0.year.saturating_sub(1);
        let next_year = date.0.year.saturating_add(1);
        types::DayOfYearInfo {
            day_of_year: date.0.day_of_year(),
            days_in_year: date.0.days_in_year(),
            prev_year: Self::year_as_islamic(prev_year),
            days_in_prev_year: date.0.year_info.days_in_prev_year(),
            next_year: Self::year_as_islamic(next_year),
        }
    }

    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
        Some(AnyCalendarKind::IslamicUmmAlQura)
    }
}

impl IslamicUmmAlQura {
    fn precomputed_data(&self) -> IslamicPrecomputedData<SaudiIslamicMarker> {
        IslamicPrecomputedData::new(self.data.as_ref().map(|x| x.get()))
    }

    fn year_as_islamic(year: i32) -> types::FormattableYear {
        types::FormattableYear {
            era: types::Era(tinystr!(16, "islamic")),
            number: year,
            cyclic: None,
            related_iso: None,
        }
    }
    pub(crate) const DEBUG_NAME: &'static str = "Islamic (Umm al-Qura)";
}

impl<A: AsCalendar<Calendar = IslamicUmmAlQura>> Date<A> {
    /// Construct new Islamic Umm al-Qura Date.
    ///
    /// Has no negative years, only era is the AH.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicUmmAlQura;
    /// use icu::calendar::Date;
    ///
    /// let islamic = IslamicUmmAlQura::new_always_calculating();
    ///
    /// let date_islamic = Date::try_new_ummalqura_date(1392, 4, 25, islamic)
    ///     .expect("Failed to initialize Islamic Date instance.");
    ///
    /// assert_eq!(date_islamic.year().number, 1392);
    /// assert_eq!(date_islamic.month().ordinal, 4);
    /// assert_eq!(date_islamic.day_of_month().0, 25);
    /// ```
    pub fn try_new_ummalqura_date(
        year: i32,
        month: u8,
        day: u8,
        calendar: A,
    ) -> Result<Date<A>, CalendarError> {
        let year_info = calendar
            .as_calendar()
            .precomputed_data()
            .load_or_compute_info(year);
        ArithmeticDate::new_from_ordinals_with_info(year, month, day, year_info)
            .map(IslamicUmmAlQuraDateInner)
            .map(|inner| Date::from_raw(inner, calendar))
    }
}

impl<A: AsCalendar<Calendar = IslamicUmmAlQura>> DateTime<A> {
    /// Construct a new Islamic Umm al-Qura datetime from integers.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicUmmAlQura;
    /// use icu::calendar::DateTime;
    ///
    /// let islamic = IslamicUmmAlQura::new_always_calculating();
    ///
    /// let datetime_islamic =
    ///     DateTime::try_new_ummalqura_datetime(474, 10, 11, 13, 1, 0, islamic)
    ///         .expect("Failed to initialize Islamic DateTime instance.");
    ///
    /// assert_eq!(datetime_islamic.date.year().number, 474);
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
    /// ```
    pub fn try_new_ummalqura_datetime(
        year: i32,
        month: u8,
        day: u8,
        hour: u8,
        minute: u8,
        second: u8,
        calendar: A,
    ) -> Result<DateTime<A>, CalendarError> {
        Ok(DateTime {
            date: Date::try_new_ummalqura_date(year, month, day, calendar)?,
            time: Time::try_new(hour, minute, second, 0)?,
        })
    }
}

/// The inner date type used for representing [`Date`]s of [`IslamicCivil`]. See [`Date`] and [`IslamicCivil`] for more details.

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct IslamicCivilDateInner(ArithmeticDate<IslamicCivil>);

impl CalendarArithmetic for IslamicCivil {
    type YearInfo = ();

    fn month_days(year: i32, month: u8, _data: ()) -> u8 {
        match month {
            1 | 3 | 5 | 7 | 9 | 11 => 30,
            2 | 4 | 6 | 8 | 10 => 29,
            12 if Self::is_leap_year(year, ()) => 30,
            12 => 29,
            _ => 0,
        }
    }

    fn months_for_every_year(_year: i32, _data: ()) -> u8 {
        12
    }

    fn days_in_provided_year(year: i32, _data: ()) -> u16 {
        if Self::is_leap_year(year, ()) {
            IslamicYearInfo::LONG_YEAR_LEN
        } else {
            IslamicYearInfo::SHORT_YEAR_LEN
        }
    }

    fn is_leap_year(year: i32, _data: ()) -> bool {
        (14 + 11 * year).rem_euclid(30) < 11
    }

    fn last_month_day_in_year(year: i32, _data: ()) -> (u8, u8) {
        if Self::is_leap_year(year, ()) {
            (12, 30)
        } else {
            (12, 29)
        }
    }
}

impl Calendar for IslamicCivil {
    type DateInner = IslamicCivilDateInner;

    fn date_from_codes(
        &self,
        era: types::Era,
        year: i32,
        month_code: types::MonthCode,
        day: u8,
    ) -> Result<Self::DateInner, CalendarError> {
        let year = if era.0 == tinystr!(16, "islamic-civil")
            || era.0 == tinystr!(16, "islamicc")
            || era.0 == tinystr!(16, "islamic")
            || era.0 == tinystr!(16, "ah")
        {
            // TODO: Check name and alias
            year
        } else {
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
        };

        ArithmeticDate::new_from_codes(self, year, month_code, day).map(IslamicCivilDateInner)
    }

    fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());
        Self::islamic_from_fixed(fixed_iso).inner
    }

    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
        let fixed_islamic = Self::fixed_from_islamic(*date);
        Iso::iso_from_fixed(fixed_islamic)
    }

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

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

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

    fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
        Iso.day_of_week(self.date_to_iso(date).inner())
    }

    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
        date.0.offset_date(offset, &())
    }

    fn until(
        &self,
        date1: &Self::DateInner,
        date2: &Self::DateInner,
        _calendar2: &Self,
        _largest_unit: DateDurationUnit,
        _smallest_unit: DateDurationUnit,
    ) -> DateDuration<Self> {
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
    }

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

    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
        Self::year_as_islamic(date.0.year)
    }

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

    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
        date.0.month()
    }

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

    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
        let prev_year = date.0.year.saturating_sub(1);
        let next_year = date.0.year.saturating_add(1);
        types::DayOfYearInfo {
            day_of_year: date.0.day_of_year(),
            days_in_year: date.0.days_in_year(),
            prev_year: Self::year_as_islamic(prev_year),
            days_in_prev_year: Self::days_in_provided_year(prev_year, ()),
            next_year: Self::year_as_islamic(next_year),
        }
    }
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
        Some(AnyCalendarKind::IslamicCivil)
    }
}

impl IslamicCivil {
    fn fixed_from_islamic(i_date: IslamicCivilDateInner) -> RataDie {
        calendrical_calculations::islamic::fixed_from_islamic_civil(
            i_date.0.year,
            i_date.0.month,
            i_date.0.day,
        )
    }

    fn islamic_from_fixed(date: RataDie) -> Date<IslamicCivil> {
        let (y, m, d) = calendrical_calculations::islamic::islamic_civil_from_fixed(date);

        debug_assert!(
            Date::try_new_islamic_civil_date_with_calendar(y, m, d, IslamicCivil).is_ok()
        );
        Date::from_raw(
            IslamicCivilDateInner(ArithmeticDate::new_unchecked(y, m, d)),
            IslamicCivil,
        )
    }

    fn year_as_islamic(year: i32) -> types::FormattableYear {
        types::FormattableYear {
            era: types::Era(tinystr!(16, "islamic")),
            number: year,
            cyclic: None,
            related_iso: None,
        }
    }
}

impl<A: AsCalendar<Calendar = IslamicCivil>> Date<A> {
    /// Construct new Civil Islamic Date.
    ///
    /// Has no negative years, only era is the AH.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicCivil;
    /// use icu::calendar::Date;
    ///
    /// let islamic = IslamicCivil::new_always_calculating();
    ///
    /// let date_islamic =
    ///     Date::try_new_islamic_civil_date_with_calendar(1392, 4, 25, islamic)
    ///         .expect("Failed to initialize Islamic Date instance.");
    ///
    /// assert_eq!(date_islamic.year().number, 1392);
    /// assert_eq!(date_islamic.month().ordinal, 4);
    /// assert_eq!(date_islamic.day_of_month().0, 25);
    /// ```
    pub fn try_new_islamic_civil_date_with_calendar(
        year: i32,
        month: u8,
        day: u8,
        calendar: A,
    ) -> Result<Date<A>, CalendarError> {
        ArithmeticDate::new_from_ordinals(year, month, day)
            .map(IslamicCivilDateInner)
            .map(|inner| Date::from_raw(inner, calendar))
    }
}

impl<A: AsCalendar<Calendar = IslamicCivil>> DateTime<A> {
    /// Construct a new Civil Islamic datetime from integers.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicCivil;
    /// use icu::calendar::DateTime;
    ///
    /// let islamic = IslamicCivil::new_always_calculating();
    ///
    /// let datetime_islamic =
    ///     DateTime::try_new_islamic_civil_datetime_with_calendar(
    ///         474, 10, 11, 13, 1, 0, islamic,
    ///     )
    ///     .expect("Failed to initialize Islamic DateTime instance.");
    ///
    /// assert_eq!(datetime_islamic.date.year().number, 474);
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
    /// ```
    pub fn try_new_islamic_civil_datetime_with_calendar(
        year: i32,
        month: u8,
        day: u8,
        hour: u8,
        minute: u8,
        second: u8,
        calendar: A,
    ) -> Result<DateTime<A>, CalendarError> {
        Ok(DateTime {
            date: Date::try_new_islamic_civil_date_with_calendar(year, month, day, calendar)?,
            time: Time::try_new(hour, minute, second, 0)?,
        })
    }
}

/// The inner date type used for representing [`Date`]s of [`IslamicTabular`]. See [`Date`] and [`IslamicTabular`] for more details.

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct IslamicTabularDateInner(ArithmeticDate<IslamicTabular>);

impl CalendarArithmetic for IslamicTabular {
    type YearInfo = ();

    fn month_days(year: i32, month: u8, _data: ()) -> u8 {
        match month {
            1 | 3 | 5 | 7 | 9 | 11 => 30,
            2 | 4 | 6 | 8 | 10 => 29,
            12 if Self::is_leap_year(year, ()) => 30,
            12 => 29,
            _ => 0,
        }
    }

    fn months_for_every_year(_year: i32, _data: ()) -> u8 {
        12
    }

    fn days_in_provided_year(year: i32, _data: ()) -> u16 {
        if Self::is_leap_year(year, ()) {
            IslamicYearInfo::LONG_YEAR_LEN
        } else {
            IslamicYearInfo::SHORT_YEAR_LEN
        }
    }

    fn is_leap_year(year: i32, _data: ()) -> bool {
        (14 + 11 * year).rem_euclid(30) < 11
    }

    fn last_month_day_in_year(year: i32, _data: ()) -> (u8, u8) {
        if Self::is_leap_year(year, ()) {
            (12, 30)
        } else {
            (12, 29)
        }
    }
}

impl Calendar for IslamicTabular {
    type DateInner = IslamicTabularDateInner;

    fn date_from_codes(
        &self,
        era: types::Era,
        year: i32,
        month_code: types::MonthCode,
        day: u8,
    ) -> Result<Self::DateInner, CalendarError> {
        let year = if era.0 == tinystr!(16, "islamic-tbla")
            || era.0 == tinystr!(16, "islamic")
            || era.0 == tinystr!(16, "ah")
        {
            year
        } else {
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
        };

        ArithmeticDate::new_from_codes(self, year, month_code, day).map(IslamicTabularDateInner)
    }

    fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());
        Self::islamic_from_fixed(fixed_iso).inner
    }

    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
        let fixed_islamic = Self::fixed_from_islamic(*date);
        Iso::iso_from_fixed(fixed_islamic)
    }

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

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

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

    fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
        Iso.day_of_week(self.date_to_iso(date).inner())
    }

    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
        date.0.offset_date(offset, &())
    }

    fn until(
        &self,
        date1: &Self::DateInner,
        date2: &Self::DateInner,
        _calendar2: &Self,
        _largest_unit: DateDurationUnit,
        _smallest_unit: DateDurationUnit,
    ) -> DateDuration<Self> {
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
    }

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

    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
        Self::year_as_islamic(date.0.year)
    }

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

    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
        date.0.month()
    }

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

    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
        let prev_year = date.0.year.saturating_sub(1);
        let next_year = date.0.year.saturating_add(1);
        types::DayOfYearInfo {
            day_of_year: date.0.day_of_year(),
            days_in_year: date.0.days_in_year(),
            prev_year: Self::year_as_islamic(prev_year),
            days_in_prev_year: Self::days_in_provided_year(prev_year, ()),
            next_year: Self::year_as_islamic(next_year),
        }
    }
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
        Some(AnyCalendarKind::IslamicTabular)
    }
}

impl IslamicTabular {
    fn fixed_from_islamic(i_date: IslamicTabularDateInner) -> RataDie {
        calendrical_calculations::islamic::fixed_from_islamic_tabular(
            i_date.0.year,
            i_date.0.month,
            i_date.0.day,
        )
    }

    fn islamic_from_fixed(date: RataDie) -> Date<IslamicTabular> {
        let (y, m, d) = calendrical_calculations::islamic::islamic_tabular_from_fixed(date);

        debug_assert!(
            Date::try_new_islamic_civil_date_with_calendar(y, m, d, IslamicCivil).is_ok()
        );
        Date::from_raw(
            IslamicTabularDateInner(ArithmeticDate::new_unchecked(y, m, d)),
            IslamicTabular,
        )
    }

    fn year_as_islamic(year: i32) -> types::FormattableYear {
        types::FormattableYear {
            era: types::Era(tinystr!(16, "islamic")),
            number: year,
            cyclic: None,
            related_iso: None,
        }
    }
}

impl<A: AsCalendar<Calendar = IslamicTabular>> Date<A> {
    /// Construct new Tabular Islamic Date.
    ///
    /// Has no negative years, only era is the AH.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicTabular;
    /// use icu::calendar::Date;
    ///
    /// let islamic = IslamicTabular::new_always_calculating();
    ///
    /// let date_islamic =
    ///     Date::try_new_islamic_tabular_date_with_calendar(1392, 4, 25, islamic)
    ///         .expect("Failed to initialize Islamic Date instance.");
    ///
    /// assert_eq!(date_islamic.year().number, 1392);
    /// assert_eq!(date_islamic.month().ordinal, 4);
    /// assert_eq!(date_islamic.day_of_month().0, 25);
    /// ```
    pub fn try_new_islamic_tabular_date_with_calendar(
        year: i32,
        month: u8,
        day: u8,
        calendar: A,
    ) -> Result<Date<A>, CalendarError> {
        ArithmeticDate::new_from_ordinals(year, month, day)
            .map(IslamicTabularDateInner)
            .map(|inner| Date::from_raw(inner, calendar))
    }
}

impl<A: AsCalendar<Calendar = IslamicTabular>> DateTime<A> {
    /// Construct a new Tabular Islamic datetime from integers.
    ///
    /// ```rust
    /// use icu::calendar::islamic::IslamicTabular;
    /// use icu::calendar::DateTime;
    ///
    /// let islamic = IslamicTabular::new_always_calculating();
    ///
    /// let datetime_islamic =
    ///     DateTime::try_new_islamic_tabular_datetime_with_calendar(
    ///         474, 10, 11, 13, 1, 0, islamic,
    ///     )
    ///     .expect("Failed to initialize Islamic DateTime instance.");
    ///
    /// assert_eq!(datetime_islamic.date.year().number, 474);
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
    /// ```
    pub fn try_new_islamic_tabular_datetime_with_calendar(
        year: i32,
        month: u8,
        day: u8,
        hour: u8,
        minute: u8,
        second: u8,
        calendar: A,
    ) -> Result<DateTime<A>, CalendarError> {
        Ok(DateTime {
            date: Date::try_new_islamic_tabular_date_with_calendar(year, month, day, calendar)?,
            time: Time::try_new(hour, minute, second, 0)?,
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::Ref;

    const START_YEAR: i32 = -1245;
    const END_YEAR: i32 = 1518;

    #[derive(Debug)]
    struct DateCase {
        year: i32,
        month: u8,
        day: u8,
    }

    static TEST_FIXED_DATE: [i64; 33] = [
        -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
        470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
        664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
    ];
    // Removed: 601716 and 727274 fixed dates
    static TEST_FIXED_DATE_UMMALQURA: [i64; 31] = [
        -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
        470160, 473837, 507850, 524156, 544676, 567118, 569477, 613424, 626596, 645554, 664224,
        671401, 694799, 704424, 708842, 709409, 709580, 728714, 744313, 764652,
    ];

    static UMMALQURA_DATE_EXPECTED: [DateCase; 31] = [
        DateCase {
            year: -1245,
            month: 12,
            day: 11,
        },
        DateCase {
            year: -813,
            month: 2,
            day: 26,
        },
        DateCase {
            year: -568,
            month: 4,
            day: 3,
        },
        DateCase {
            year: -501,
            month: 4,
            day: 8,
        },
        DateCase {
            year: -157,
            month: 10,
            day: 18,
        },
        DateCase {
            year: -47,
            month: 6,
            day: 4,
        },
        DateCase {
            year: 75,
            month: 7,
            day: 14,
        },
        DateCase {
            year: 403,
            month: 10,
            day: 6,
        },
        DateCase {
            year: 489,
            month: 5,
            day: 23,
        },
        DateCase {
            year: 586,
            month: 2,
            day: 8,
        },
        DateCase {
            year: 637,
            month: 8,
            day: 8,
        },
        DateCase {
            year: 687,
            month: 2,
            day: 22,
        },
        DateCase {
            year: 697,
            month: 7,
            day: 8,
        },
        DateCase {
            year: 793,
            month: 7,
            day: 1,
        },
        DateCase {
            year: 839,
            month: 7,
            day: 7,
        },
        DateCase {
            year: 897,
            month: 6,
            day: 3,
        },
        DateCase {
            year: 960,
            month: 10,
            day: 1,
        },
        DateCase {
            year: 967,
            month: 5,
            day: 28,
        },
        DateCase {
            year: 1091,
            month: 6,
            day: 4,
        },
        DateCase {
            year: 1128,
            month: 8,
            day: 5,
        },
        DateCase {
            year: 1182,
            month: 2,
            day: 4,
        },
        DateCase {
            year: 1234,
            month: 10,
            day: 11,
        },
        DateCase {
            year: 1255,
            month: 1,
            day: 11,
        },
        DateCase {
            year: 1321,
            month: 1,
            day: 21,
        },
        DateCase {
            year: 1348,
            month: 3,
            day: 20,
        },
        DateCase {
            year: 1360,
            month: 9,
            day: 8,
        },
        DateCase {
            year: 1362,
            month: 4,
            day: 14,
        },
        DateCase {
            year: 1362,
            month: 10,
            day: 8,
        },
        DateCase {
            year: 1416,
            month: 10,
            day: 6,
        },
        DateCase {
            year: 1460,
            month: 10,
            day: 13,
        },
        DateCase {
            year: 1518,
            month: 3,
            day: 6,
        },
    ];

    static OBSERVATIONAL_CASES: [DateCase; 33] = [
        DateCase {
            year: -1245,
            month: 12,
            day: 11,
        },
        DateCase {
            year: -813,
            month: 2,
            day: 25,
        },
        DateCase {
            year: -568,
            month: 4,
            day: 2,
        },
        DateCase {
            year: -501,
            month: 4,
            day: 7,
        },
        DateCase {
            year: -157,
            month: 10,
            day: 18,
        },
        DateCase {
            year: -47,
            month: 6,
            day: 3,
        },
        DateCase {
            year: 75,
            month: 7,
            day: 13,
        },
        DateCase {
            year: 403,
            month: 10,
            day: 5,
        },
        DateCase {
            year: 489,
            month: 5,
            day: 22,
        },
        DateCase {
            year: 586,
            month: 2,
            day: 7,
        },
        DateCase {
            year: 637,
            month: 8,
            day: 7,
        },
        DateCase {
            year: 687,
            month: 2,
            day: 21,
        },
        DateCase {
            year: 697,
            month: 7,
            day: 7,
        },
        DateCase {
            year: 793,
            month: 6,
            day: 30,
        },
        DateCase {
            year: 839,
            month: 7,
            day: 6,
        },
        DateCase {
            year: 897,
            month: 6,
            day: 2,
        },
        DateCase {
            year: 960,
            month: 9,
            day: 30,
        },
        DateCase {
            year: 967,
            month: 5,
            day: 27,
        },
        DateCase {
            year: 1058,
            month: 5,
            day: 18,
        },
        DateCase {
            year: 1091,
            month: 6,
            day: 3,
        },
        DateCase {
            year: 1128,
            month: 8,
            day: 4,
        },
        DateCase {
            year: 1182,
            month: 2,
            day: 4,
        },
        DateCase {
            year: 1234,
            month: 10,
            day: 10,
        },
        DateCase {
            year: 1255,
            month: 1,
            day: 11,
        },
        DateCase {
            year: 1321,
            month: 1,
            day: 20,
        },
        DateCase {
            year: 1348,
            month: 3,
            day: 19,
        },
        DateCase {
            year: 1360,
            month: 9,
            day: 7,
        },
        DateCase {
            year: 1362,
            month: 4,
            day: 14,
        },
        DateCase {
            year: 1362,
            month: 10,
            day: 7,
        },
        DateCase {
            year: 1412,
            month: 9,
            day: 12,
        },
        DateCase {
            year: 1416,
            month: 10,
            day: 5,
        },
        DateCase {
            year: 1460,
            month: 10,
            day: 12,
        },
        DateCase {
            year: 1518,
            month: 3,
            day: 5,
        },
    ];

    static ARITHMETIC_CASES: [DateCase; 33] = [
        DateCase {
            year: -1245,
            month: 12,
            day: 9,
        },
        DateCase {
            year: -813,
            month: 2,
            day: 23,
        },
        DateCase {
            year: -568,
            month: 4,
            day: 1,
        },
        DateCase {
            year: -501,
            month: 4,
            day: 6,
        },
        DateCase {
            year: -157,
            month: 10,
            day: 17,
        },
        DateCase {
            year: -47,
            month: 6,
            day: 3,
        },
        DateCase {
            year: 75,
            month: 7,
            day: 13,
        },
        DateCase {
            year: 403,
            month: 10,
            day: 5,
        },
        DateCase {
            year: 489,
            month: 5,
            day: 22,
        },
        DateCase {
            year: 586,
            month: 2,
            day: 7,
        },
        DateCase {
            year: 637,
            month: 8,
            day: 7,
        },
        DateCase {
            year: 687,
            month: 2,
            day: 20,
        },
        DateCase {
            year: 697,
            month: 7,
            day: 7,
        },
        DateCase {
            year: 793,
            month: 7,
            day: 1,
        },
        DateCase {
            year: 839,
            month: 7,
            day: 6,
        },
        DateCase {
            year: 897,
            month: 6,
            day: 1,
        },
        DateCase {
            year: 960,
            month: 9,
            day: 30,
        },
        DateCase {
            year: 967,
            month: 5,
            day: 27,
        },
        DateCase {
            year: 1058,
            month: 5,
            day: 18,
        },
        DateCase {
            year: 1091,
            month: 6,
            day: 2,
        },
        DateCase {
            year: 1128,
            month: 8,
            day: 4,
        },
        DateCase {
            year: 1182,
            month: 2,
            day: 3,
        },
        DateCase {
            year: 1234,
            month: 10,
            day: 10,
        },
        DateCase {
            year: 1255,
            month: 1,
            day: 11,
        },
        DateCase {
            year: 1321,
            month: 1,
            day: 21,
        },
        DateCase {
            year: 1348,
            month: 3,
            day: 19,
        },
        DateCase {
            year: 1360,
            month: 9,
            day: 8,
        },
        DateCase {
            year: 1362,
            month: 4,
            day: 13,
        },
        DateCase {
            year: 1362,
            month: 10,
            day: 7,
        },
        DateCase {
            year: 1412,
            month: 9,
            day: 13,
        },
        DateCase {
            year: 1416,
            month: 10,
            day: 5,
        },
        DateCase {
            year: 1460,
            month: 10,
            day: 12,
        },
        DateCase {
            year: 1518,
            month: 3,
            day: 5,
        },
    ];

    static TABULAR_CASES: [DateCase; 33] = [
        DateCase {
            year: -1245,
            month: 12,
            day: 10,
        },
        DateCase {
            year: -813,
            month: 2,
            day: 24,
        },
        DateCase {
            year: -568,
            month: 4,
            day: 2,
        },
        DateCase {
            year: -501,
            month: 4,
            day: 7,
        },
        DateCase {
            year: -157,
            month: 10,
            day: 18,
        },
        DateCase {
            year: -47,
            month: 6,
            day: 4,
        },
        DateCase {
            year: 75,
            month: 7,
            day: 14,
        },
        DateCase {
            year: 403,
            month: 10,
            day: 6,
        },
        DateCase {
            year: 489,
            month: 5,
            day: 23,
        },
        DateCase {
            year: 586,
            month: 2,
            day: 8,
        },
        DateCase {
            year: 637,
            month: 8,
            day: 8,
        },
        DateCase {
            year: 687,
            month: 2,
            day: 21,
        },
        DateCase {
            year: 697,
            month: 7,
            day: 8,
        },
        DateCase {
            year: 793,
            month: 7,
            day: 2,
        },
        DateCase {
            year: 839,
            month: 7,
            day: 7,
        },
        DateCase {
            year: 897,
            month: 6,
            day: 2,
        },
        DateCase {
            year: 960,
            month: 10,
            day: 1,
        },
        DateCase {
            year: 967,
            month: 5,
            day: 28,
        },
        DateCase {
            year: 1058,
            month: 5,
            day: 19,
        },
        DateCase {
            year: 1091,
            month: 6,
            day: 3,
        },
        DateCase {
            year: 1128,
            month: 8,
            day: 5,
        },
        DateCase {
            year: 1182,
            month: 2,
            day: 4,
        },
        DateCase {
            year: 1234,
            month: 10,
            day: 11,
        },
        DateCase {
            year: 1255,
            month: 1,
            day: 12,
        },
        DateCase {
            year: 1321,
            month: 1,
            day: 22,
        },
        DateCase {
            year: 1348,
            month: 3,
            day: 20,
        },
        DateCase {
            year: 1360,
            month: 9,
            day: 9,
        },
        DateCase {
            year: 1362,
            month: 4,
            day: 14,
        },
        DateCase {
            year: 1362,
            month: 10,
            day: 8,
        },
        DateCase {
            year: 1412,
            month: 9,
            day: 14,
        },
        DateCase {
            year: 1416,
            month: 10,
            day: 6,
        },
        DateCase {
            year: 1460,
            month: 10,
            day: 13,
        },
        DateCase {
            year: 1518,
            month: 3,
            day: 6,
        },
    ];

    #[test]
    fn test_observational_islamic_from_fixed() {
        let calendar = IslamicObservational::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in OBSERVATIONAL_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
            let date =
                Date::try_new_observational_islamic_date(case.year, case.month, case.day, calendar)
                    .unwrap();
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));

            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
        }
    }

    #[test]
    fn test_fixed_from_observational_islamic() {
        let calendar = IslamicObservational::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in OBSERVATIONAL_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
            let date =
                Date::try_new_observational_islamic_date(case.year, case.month, case.day, calendar)
                    .unwrap();
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
        }
    }

    #[test]
    fn test_fixed_from_islamic() {
        let calendar = IslamicCivil::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
            let date = Date::try_new_islamic_civil_date_with_calendar(
                case.year, case.month, case.day, calendar,
            )
            .unwrap();
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
        }
    }

    #[test]
    fn test_islamic_from_fixed() {
        let calendar = IslamicCivil::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
            let date = Date::try_new_islamic_civil_date_with_calendar(
                case.year, case.month, case.day, calendar,
            )
            .unwrap();
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));

            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
        }
    }

    #[test]
    fn test_fixed_from_islamic_tbla() {
        let calendar = IslamicTabular::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in TABULAR_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
            let date = Date::try_new_islamic_tabular_date_with_calendar(
                case.year, case.month, case.day, calendar,
            )
            .unwrap();
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
        }
    }

    #[test]
    fn test_islamic_tbla_from_fixed() {
        let calendar = IslamicTabular::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in TABULAR_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
            let date = Date::try_new_islamic_tabular_date_with_calendar(
                case.year, case.month, case.day, calendar,
            )
            .unwrap();
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));

            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
        }
    }

    #[test]
    fn test_saudi_islamic_from_fixed() {
        let calendar = IslamicUmmAlQura::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in UMMALQURA_DATE_EXPECTED
            .iter()
            .zip(TEST_FIXED_DATE_UMMALQURA.iter())
        {
            let date =
                Date::try_new_ummalqura_date(case.year, case.month, case.day, calendar).unwrap();
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));

            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
        }
    }

    #[test]
    fn test_fixed_from_saudi_islamic() {
        let calendar = IslamicUmmAlQura::new();
        let calendar = Ref(&calendar);
        for (case, f_date) in UMMALQURA_DATE_EXPECTED
            .iter()
            .zip(TEST_FIXED_DATE_UMMALQURA.iter())
        {
            let date =
                Date::try_new_ummalqura_date(case.year, case.month, case.day, calendar).unwrap();
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
        }
    }

    #[ignore]
    #[test]
    fn test_days_in_provided_year_observational() {
        let calendar = IslamicObservational::new();
        let calendar = Ref(&calendar);
        // -1245 1 1 = -214526 (R.D Date)
        // 1518 1 1 = 764589 (R.D Date)
        let sum_days_in_year: i64 = (START_YEAR..END_YEAR)
            .map(|year| {
                IslamicObservational::days_in_provided_year(
                    year,
                    IslamicYearInfo::compute::<ObservationalIslamicMarker>(year),
                ) as i64
            })
            .sum();
        let expected_number_of_days =
            Date::try_new_observational_islamic_date(END_YEAR, 1, 1, calendar)
                .unwrap()
                .to_fixed()
                - Date::try_new_observational_islamic_date(START_YEAR, 1, 1, calendar)
                    .unwrap()
                    .to_fixed(); // The number of days between Islamic years -1245 and 1518
        let tolerance = 1; // One day tolerance (See Astronomical::month_length for more context)

        assert!(
            (sum_days_in_year - expected_number_of_days).abs() <= tolerance,
            "Difference between sum_days_in_year and expected_number_of_days is more than the tolerance"
        );
    }

    #[ignore]
    #[test]
    fn test_days_in_provided_year_ummalqura() {
        let calendar = IslamicUmmAlQura::new();
        let calendar = Ref(&calendar);
        // -1245 1 1 = -214528 (R.D Date)
        // 1518 1 1 = 764588 (R.D Date)
        let sum_days_in_year: i64 = (START_YEAR..END_YEAR)
            .map(|year| {
                IslamicUmmAlQura::days_in_provided_year(
                    year,
                    IslamicYearInfo::compute::<SaudiIslamicMarker>(year),
                ) as i64
            })
            .sum();
        let expected_number_of_days = Date::try_new_ummalqura_date(END_YEAR, 1, 1, calendar)
            .unwrap()
            .to_fixed()
            - Date::try_new_ummalqura_date(START_YEAR, 1, 1, calendar)
                .unwrap()
                .to_fixed(); // The number of days between Umm al-Qura Islamic years -1245 and 1518

        assert_eq!(sum_days_in_year, expected_number_of_days);
    }

    #[test]
    fn test_regression_3868() {
        // This date used to panic on creation
        let iso = Date::try_new_iso_date(2011, 4, 4).unwrap();
        let islamic = iso.to_calendar(IslamicUmmAlQura::new());
        // Data from https://www.ummulqura.org.sa/Index.aspx
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.47 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge