usecrate::calendar_arithmetic::ArithmeticDate; usecrate::calendar_arithmetic::DateFieldsResolver; usecrate::calendar_arithmetic::ToExtendedYear; usecrate::error::{DateError, DateFromFieldsError, EcmaReferenceYearError, UnknownEraError}; usecrate::options::DateFromFieldsOptions; usecrate::options::{DateAddOptions, DateDifferenceOptions}; usecrate::types::DateFields; usecrate::{types, Calendar, Date}; usecrate::{AsCalendar, RangeError}; use calendrical_calculations::islamic::{
ISLAMIC_EPOCH_FRIDAY, ISLAMIC_EPOCH_THURSDAY, WELL_BEHAVED_ASTRONOMICAL_RANGE,
}; use calendrical_calculations::rata_die::RataDie; use core::fmt::Debug; use icu_locale_core::preferences::extensions::unicode::keywords::{
CalendarAlgorithm, HijriCalendarAlgorithm,
}; use icu_provider::prelude::*; use tinystr::tinystr;
#[path = "hijri/simulated_mecca_data.rs"] mod simulated_mecca_data; #[path = "hijri/ummalqura_data.rs"] mod ummalqura_data;
/// The [Hijri Calendar](https://en.wikipedia.org/wiki/Islamic_calendar) /// /// There are many variants of this calendar, using different lunar observations or calculations /// (see [`Rules`]). Currently, [`Rules`] is an unstable trait, but some of its implementors /// are stable, and can be constructed via the various `Hijri::new_*` constructors. Please comment /// on [this issue](https://github.com/unicode-org/icu4x/issues/6962) /// if you would like to see this the ability to implement custom [`Rules`] stabilized. /// /// This implementation supports only variants where months are either 29 or 30 days. /// /// This corresponds to various `"islamic-*"` [CLDR calendars](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier), /// see the individual implementors of [`Rules`] ([`TabularAlgorithm`], [`UmmAlQura`], [`AstronomicalSimulation`]) for more information. /// /// # Era codes /// /// This calendar uses two era codes: `ah`, and `bh`, corresponding to the Anno Hegirae and Before Hijrah eras /// /// # Months and days /// /// The 12 months are called al-Muḥarram (`M01`), Ṣafar (`M02`), Rabīʿ al-ʾAwwal (`M03`), /// Rabīʿ ath-Thānī or Rabīʿ al-ʾĀkhir (`M04`), Jumādā al-ʾŪlā (`M05`), Jumādā ath-Thāniyah /// or Jumādā al-ʾĀkhirah (`M06`), Rajab (`M07`), Shaʿbān (`M08`), Ramaḍān (`M09`), Shawwāl (`M10`), /// Ḏū al-Qaʿdah (`M11`), Ḏū al-Ḥijjah (`M12`). /// /// As a true lunar calendar, the lengths of the months depend on the lunar cycle (a month starts on the day /// where the waxing crescent is first observed), and will be either 29 or 30 days. /// /// The lengths of the months are determined by the concrete [`Rules`] implementation. /// /// There are either 6 or 7 30-day months, so the length of the year is 354 or 355 days. /// /// # Calendar drift /// /// As a lunar calendar, this calendar does not intend to follow the solar year, and drifts more /// than 10 days per year with respect to the seasons. #[derive(Clone, Debug, Default, Copy)] #[allow(clippy::exhaustive_structs)] // newtype pubstruct Hijri<S>(pub S);
/// Defines a variant of the [`Hijri`] calendar. /// /// This crate includes the [`UmmAlQura`], [`AstronomicalSimulation`], and [`TabularAlgorithm`] /// rules, other rules can be implemented by users. /// /// <div class="stab unstable"> /// This trait is sealed; it should not be implemented by user code. If an API requests an item that implements this /// trait, please consider using a type from the implementors listed below. /// /// It is still possible to implement this trait in userland (since `UnstableSealed` is public), /// do not do so unless you are prepared for things to occasionally break. /// </div> pubtrait Rules: Clone + Debug + crate::cal::scaffold::UnstableSealed { /// Returns data about the given year. fn year_data(&self, extended_year: i32) -> HijriYearData;
/// Returns an ECMA reference year that contains the given month-day combination. /// /// If the day is out of range, it will return a year that contains the given month /// and the maximum day possible for that month. See [the spec][spec] for the /// precise algorithm used. /// /// This API only matters when using [`MissingFieldsStrategy::Ecma`] to compute /// a date without providing a year in [`Date::try_from_fields()`]. The default impl /// will just error, and custom calendars who do not care about ECMA/Temporal /// reference years do not need to override this. /// /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-nonisomonthdaytoisoreferencedate /// [`MissingFieldsStrategy::Ecma`]: crate::options::MissingFieldsStrategy::Ecma fn ecma_reference_year(
&self, // TODO: Consider accepting ValidMonthCode
_month_code: (u8, bool),
_day: u8,
) -> Result<i32, EcmaReferenceYearError> {
Err(EcmaReferenceYearError::Unimplemented)
}
/// The BCP-47 [`CalendarAlgorithm`] for the Hijri calendar using these rules, if defined. fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
None
}
/// The debug name for these rules. fn debug_name(&self) -> &'static str { "Hijri (custom rules)"
}
}
/// [`Hijri`] [`Rules`] based on an astronomical simulation for a particular location. /// /// These simulations are unofficial and are known to not necessarily match sightings /// on the ground. Unless you know otherwise for sure, instead of this variant, use /// [`UmmAlQura`], which uses the results of KACST's Mecca-based calculations. /// /// As floating point arithmetic degenerates for far-away dates, this falls back to /// the tabular calendar at some point. /// /// The precise behavior of this calendar may change in the future if: /// - We decide to tweak the precise astronomical simulation used /// - We decide to expand or reduce the range where we are using the astronomical simulation. /// /// This corresponds to the `"islamic-rgsa"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier) /// if constructed with [`Hijri::new_simulated_mecca()`]. #[derive(Copy, Clone, Debug)] pubstruct AstronomicalSimulation { pub(crate) location: SimulatedLocation,
}
let location = matchself.location {
SimulatedLocation::Mecca => calendrical_calculations::islamic::MECCA,
};
let start_day = calendrical_calculations::islamic::fixed_from_observational_islamic(
extended_year, 1, 1,
location,
); let next_start_day = calendrical_calculations::islamic::fixed_from_observational_islamic(
extended_year + 1, 1, 1,
location,
); match (next_start_day - start_day) as u16 { 355 | 354 => (), 353 => {
icu_provider::log::trace!( "({}) Found year {extended_year} AH with length {}. See <https://github.com/unicode-org/icu4x/issues/4930>", self.debug_name(),
next_start_day - start_day
);
}
other => {
debug_assert!(
!WELL_BEHAVED_ASTRONOMICAL_RANGE.contains(&start_day), "({}) Found year {extended_year} AH with length {}!", self.debug_name(),
other
)
}
}
let month_lengths = { letmut excess_days = 0; letmut month_lengths = core::array::from_fn(|month_idx| { let days_in_month =
calendrical_calculations::islamic::observational_islamic_month_days(
extended_year,
month_idx as u8 + 1,
location,
); match days_in_month { 29 => false, 30 => true, 31 => {
icu_provider::log::trace!( "({}) Found year {extended_year} AH with month length {days_in_month} for month {}.", self.debug_name(),
month_idx + 1
);
excess_days += 1; true
}
_ => {
debug_assert!(
!WELL_BEHAVED_ASTRONOMICAL_RANGE.contains(&start_day), "({}) Found year {extended_year} AH with month length {days_in_month} for month {}!", self.debug_name(),
month_idx + 1
); false
}
}
}); // To maintain invariants for calendar arithmetic, if astronomy finds // a 31-day month, "move" the day to the first 29-day month in the // same year to maintain all months at 29 or 30 days. if excess_days != 0 {
debug_assert!(
excess_days == 1 || !WELL_BEHAVED_ASTRONOMICAL_RANGE.contains(&start_day), "({}) Found year {extended_year} AH with more than one excess day!", self.debug_name()
); iflet Some(l) = month_lengths.iter_mut().find(|l| !(**l)) {
*l = true;
}
}
month_lengths
};
HijriYearData::try_new(extended_year, start_day, month_lengths)
.unwrap_or_else(|| UmmAlQura.year_data(extended_year))
}
}
/// [`Hijri`] [`Rules`] for the [Umm al-Qura](https://en.wikipedia.org/wiki/Islamic_calendar#Saudi_Arabia's_Umm_al-Qura_calendar) calendar. /// /// From the start of 1300 AH (1882-11-12 ISO) to the end of 1600 AH (2174-11-25 ISO), this /// `Rules` implementation uses Umm al-Qura month lengths obtained from /// [KACST](https://kacst.gov.sa/). Outside this range, this implementation falls back to /// [`TabularAlgorithm`] with [`TabularAlgorithmLeapYears::TypeII`] and [`TabularAlgorithmEpoch::Friday`]. /// /// The precise behavior of this calendar may change in the future if: /// - New ground truth is established by published government sources /// - We decide to use a different algorithm outside the KACST range /// - We decide to expand or reduce the range where we are correctly handling past dates. /// /// This corresponds to the `"islamic-umalqura"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier). #[derive(Copy, Clone, Debug, Default)] #[non_exhaustive] pubstruct UmmAlQura;
implcrate::cal::scaffold::UnstableSealed for UmmAlQura {} impl Rules for UmmAlQura { fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
Some(CalendarAlgorithm::Hijri(Some(
HijriCalendarAlgorithm::Umalqura,
)))
}
/// [`Hijri`] [`Rules`] for the [Tabular Hijri Algorithm](https://en.wikipedia.org/wiki/Tabular_Islamic_calendar). /// /// See [`TabularAlgorithmEpoch`] and [`TabularAlgorithmLeapYears`] for customization. /// /// The most common version of these rules uses [`TabularAlgorithmEpoch::Friday`] and [`TabularAlgorithmLeapYears::TypeII`]. /// /// When constructed with [`TabularAlgorithmLeapYears::TypeII`], and either [`TabularAlgorithmEpoch::Friday`] or [`TabularAlgorithmEpoch::Thursday`], /// this corresponds to the `"islamic-civil"` and `"islamic-tbla"` [CLDR calendars](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier) respectively. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pubstruct TabularAlgorithm { pub(crate) leap_years: TabularAlgorithmLeapYears, pub(crate) epoch: TabularAlgorithmEpoch,
}
impl TabularAlgorithm { /// Construct a new [`TabularAlgorithm`] with the given leap year rule and epoch. pubconstfn new(leap_years: TabularAlgorithmLeapYears, epoch: TabularAlgorithmEpoch) -> Self { Self { epoch, leap_years }
}
}
/// Creates a [`Hijri`] calendar using simulated sightings at Mecca. /// /// These simulations are unofficial and are known to not necessarily match sightings /// on the ground. Unless you know otherwise for sure, instead of this variant, use /// [`Hijri::new_umm_al_qura`], which uses the results of KACST's Mecca-based calculations. pubconstfn new_simulated_mecca() -> Self { Self(AstronomicalSimulation {
location: SimulatedLocation::Mecca,
})
}
/// The leap year rule for the [`TabularAlgorithm`] rules. /// /// This specifies which years of a 30-year cycle have an additional day at /// the end of the year. #[non_exhaustive] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pubenum TabularAlgorithmLeapYears { /// Leap years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29
TypeII,
}
/// Creates a [`Hijri`] calendar with tabular rules and the given leap year rule and epoch. pubconstfn new_tabular(
leap_years: TabularAlgorithmLeapYears,
epoch: TabularAlgorithmEpoch,
) -> Self { Self(TabularAlgorithm::new(leap_years, epoch))
}
}
/// Information about a Hijri year. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pubstruct HijriYearData {
packed: PackedHijriYearData,
extended_year: i32,
}
impl HijriYearData { /// Creates [`HijriYearData`] from the given parts. /// /// `start_day` is the date for the first day of the year, see [`Date::to_rata_die`] /// to obtain a [`RataDie`] from a [`Date`] in an arbitrary calendar. `start_day` has /// to be within 5 days of the start of the year of the [`TabularAlgorithm`]. /// /// `month_lengths[n - 1]` is true if the nth month has 30 days, and false otherwise. /// Either 6 or 7 months need to have 30 days. pubfn try_new(
extended_year: i32,
start_day: RataDie,
month_lengths: [bool; 12],
) -> Option<Self> {
Some(Self {
packed: PackedHijriYearData::try_new(extended_year, month_lengths, start_day)?,
extended_year,
})
}
/// The struct containing compiled Hijri YearInfo /// /// * `start_day` has to be within 5 days of the start of the year of the [`TabularAlgorithm`]. /// * `month_lengths[n - 1]` has either 6 or 7 long months. /// /// Bit structure /// /// ```text /// Bit: F.........C B.............0 /// Value: [ start day ][ month lengths ] /// ``` /// /// The start day is encoded as a signed offset from `Self::mean_tabular_start_day`. This number does not /// appear to be less than 2, however we use all remaining bits for it in case of drift in the math. /// The month lengths are stored as 1 = 30, 0 = 29 for each month including the leap month. /// /// <div class="stab unstable"> /// This code is considered unstable; it may change at any time, in breaking or non-breaking ways, /// including in SemVer minor releases. While the serde representation of data structs is guaranteed /// to be stable, their Rust representation might not be. Use with caution. /// </div> #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] struct PackedHijriYearData(u16);
// month is 1-indexed, but 0 is a valid input, producing 0 fn last_day_of_month(self, month: u8) -> u16 { // month is 1-indexed, so `29 * month` includes the current month letmut prev_month_lengths = 29 * month as u16; // month is 1-indexed, so `1 << month` is a mask with all zeroes except // for a 1 at the bit index at the next month. Subtracting 1 from it gets us // a bitmask for all months up to now let long_month_bits = self.0 & ((1 << month as u16) - 1);
prev_month_lengths += long_month_bits.count_ones().try_into().unwrap_or(0);
prev_month_lengths
}
constfn mean_tabular_start_day(extended_year: i32) -> RataDie { // -1 because the epoch is new year of year 1
calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY
.add((extended_year as i64 - 1) * (354 * 30 + 11) / 30)
}
}
#[test] fn computer_reference_years() { let rules = UmmAlQura;
fn compute_hijri_reference_year<C>(
ordinal_month: u8,
day: u8,
cal: &C,
year_info_from_extended: implFn(i32) -> C::YearInfo,
) -> Result<C::YearInfo, DateError> where
C: DateFieldsResolver,
{ let dec_31 = Date::from_rata_die( crate::cal::abstract_gregorian::LAST_DAY_OF_REFERENCE_YEAR, crate::Ref(cal),
); // December 31, 1972 occurs in the 11th month, 1392 AH, but the day could vary
debug_assert_eq!(dec_31.month().ordinal, 11); let (y0, y1, y2, y3) = if ordinal_month < 11 || (ordinal_month == 11 && day <= dec_31.day_of_month().0) {
(1389, 1390, 1391, 1392)
} else {
(1388, 1389, 1390, 1391)
}; let year_info = year_info_from_extended(y3); if day <= C::days_in_provided_month(year_info, ordinal_month) { return Ok(year_info);
} let year_info = year_info_from_extended(y2); if day <= C::days_in_provided_month(year_info, ordinal_month) { return Ok(year_info);
} let year_info = year_info_from_extended(y1); if day <= C::days_in_provided_month(year_info, ordinal_month) { return Ok(year_info);
} let year_info = year_info_from_extended(y0); // This function might be called with out-of-range days that are handled later. // Some calendars don't have day 30s in every month so we don't check those. if day <= 29 {
debug_assert!(
day <= C::days_in_provided_month(year_info, ordinal_month), "{ordinal_month}/{day}"
);
}
Ok(year_info)
} for month in1..=12 { for day in [30, 29] { let y = compute_hijri_reference_year(month, day, &Hijri(rules), |e| rules.year_data(e))
.unwrap()
.extended_year;
if day == 30 {
println!("({month}, {day}) => {y},")
} else {
println!("({month}, _) => {y},")
}
}
}
}
#[allow(clippy::derived_hash_with_manual_eq)] // bounds #[derive(Clone, Debug, Hash)] /// The inner date type used for representing [`Date`]s of [`Hijri`]. See [`Date`] and [`Hijri`] for more details. pubstruct HijriDateInner<R: Rules>(ArithmeticDate<Hijri<R>>);
impl<R: Rules> crate::cal::scaffold::UnstableSealed for Hijri<R> {} impl<R: Rules> Calendar for Hijri<R> { type DateInner = HijriDateInner<R>; type Year = types::EraYear; type DifferenceError = core::convert::Infallible;
fn from_rata_die(&self, rd: RataDie) -> Self::DateInner { // (354 * 30 + 11) / 30 is the mean year length for a tabular year // This is slightly different from the `calendrical_calculations::islamic::MEAN_YEAR_LENGTH`, which is based on // the (current) synodic month length. // // +1 because the epoch is new year of year 1 // Before the epoch the division will round up (towards 0), so we need to // subtract 1, which is the same as not adding the 1. let extended_year = (rd - calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY) * 30
/ (354 * 30 + 11)
+ (rd >= calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY) as i64;
let extended_year = extended_year.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
letmut year = self.0.year_data(extended_year);
// We rounded the extended year down, so we might need to use the next year if rd >= year.new_year() + year.packed.days_in_year() as i64 && extended_year < i32::MAX {
year = self.0.year_data(year.extended_year + 1)
}
// Clamp the RD to our year let rd = rd.clamp(
year.new_year(),
year.new_year() + year.packed.days_in_year() as i64,
);
let day_of_year = (rd - year.new_year()) as u16;
// 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) letmut month = (day_of_year / 30) as u8 + 1; letmut last_day_of_month = year.packed.last_day_of_month(month); letmut last_day_of_prev_month = year.packed.last_day_of_month(month - 1);
#[test] fn test_simulated_hijri_from_rd() { let calendar = Hijri::new_simulated_mecca(); for (case, f_date) in SIMULATED_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap(); let iso = Date::from_rata_die(RataDie::new(*f_date), crate::Iso);
#[test] fn test_rd_from_simulated_hijri() { let calendar = Hijri::new_simulated_mecca(); for (case, f_date) in SIMULATED_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap();
assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
}
}
#[test] fn test_rd_from_hijri() { let calendar = Hijri::new_tabular(
TabularAlgorithmLeapYears::TypeII,
TabularAlgorithmEpoch::Friday,
); for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap();
assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
}
}
#[test] fn test_hijri_from_rd() { let calendar = Hijri::new_tabular(
TabularAlgorithmLeapYears::TypeII,
TabularAlgorithmEpoch::Friday,
); for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap(); let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
assert_eq!(date, date_rd, "{case:?}");
}
}
#[test] fn test_rd_from_hijri_tbla() { let calendar = Hijri::new_tabular(
TabularAlgorithmLeapYears::TypeII,
TabularAlgorithmEpoch::Thursday,
); for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap();
assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
}
}
#[test] fn test_hijri_tbla_from_rd() { let calendar = Hijri::new_tabular(
TabularAlgorithmLeapYears::TypeII,
TabularAlgorithmEpoch::Thursday,
); for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap(); let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
assert_eq!(date, date_rd, "{case:?}");
}
}
#[test] fn test_saudi_hijri_from_rd() { let calendar = Hijri::new_umm_al_qura(); for (case, f_date) in UMMALQURA_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap(); let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
assert_eq!(date, date_rd, "{case:?}");
}
}
#[test] fn test_rd_from_saudi_hijri() { let calendar = Hijri::new_umm_al_qura(); for (case, f_date) in UMMALQURA_CASES.iter().zip(TEST_RD.iter()) { let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
.unwrap();
assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
}
}
#[ignore] // slow #[test] fn test_days_in_provided_year_simulated() { let calendar = Hijri::new_simulated_mecca(); // -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| {
Hijri::new_simulated_mecca()
.0
.year_data(year)
.packed
.days_in_year() as i64
})
.sum(); let expected_number_of_days = Date::try_new_hijri_with_calendar(END_YEAR, 1, 1, calendar)
.unwrap()
.to_rata_die()
- Date::try_new_hijri_with_calendar(START_YEAR, 1, 1, calendar)
.unwrap()
.to_rata_die(); // The number of days between Hijri 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] // slow #[test] fn test_days_in_provided_year_ummalqura() { let calendar = Hijri::new_umm_al_qura(); // -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| calendar.0.year_data(year).packed.days_in_year() as i64)
.sum(); let expected_number_of_days = Date::try_new_hijri_with_calendar(END_YEAR, 1, 1, calendar)
.unwrap()
.to_rata_die()
- (Date::try_new_hijri_with_calendar(START_YEAR, 1, 1, calendar).unwrap())
.to_rata_die(); // The number of days between Umm al-Qura Hijri years -1245 and 1518
#[test] fn test_regression_3868() { // This date used to panic on creation let iso = Date::try_new_iso(2011, 4, 4).unwrap(); let hijri = iso.to_calendar(Hijri::new_umm_al_qura()); // Data from https://www.ummulqura.org.sa/Index.aspx
assert_eq!(hijri.day_of_month().0, 30);
assert_eq!(hijri.month().ordinal, 4);
assert_eq!(hijri.era_year().year, 1432);
}
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.