Coverage Report

Created: 2025-10-13 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/calendrical_calculations-0.1.2/src/islamic.rs
Line
Count
Source
1
use crate::astronomy::*;
2
use crate::helpers::{i64_to_saturated_i32, next};
3
use crate::rata_die::{Moment, RataDie};
4
#[allow(unused_imports)]
5
use core_maths::*;
6
7
// Different islamic calendars use different epochs (Thursday vs Friday) due to disagreement on the exact date of Mohammed's migration to Mecca.
8
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2066>
9
const FIXED_ISLAMIC_EPOCH_FRIDAY: RataDie = crate::julian::fixed_from_julian(622, 7, 16);
10
const FIXED_ISLAMIC_EPOCH_THURSDAY: RataDie = crate::julian::fixed_from_julian(622, 7, 15);
11
12
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6898>
13
const CAIRO: Location = Location {
14
    latitude: 30.1,
15
    longitude: 31.3,
16
    elevation: 200.0,
17
    zone: (1_f64 / 12_f64),
18
};
19
20
/// Common abstraction over islamic-style calendars
21
pub trait IslamicBasedMarker {
22
    /// The epoch of the calendar. Different calendars use a different epoch (Thu or Fri) due to disagreement on the exact date of Mohammed's migration to Mecca.
23
    const EPOCH: RataDie;
24
    /// The name of the calendar for debugging.
25
    const DEBUG_NAME: &'static str;
26
    /// Whether this calendar is known to have 353-day years.
27
    /// This is probably a bug; see <https://github.com/unicode-org/icu4x/issues/4930>
28
    const HAS_353_DAY_YEARS: bool;
29
    /// Given the extended year, calculate the approximate new year using the mean synodic month
30
0
    fn mean_synodic_ny(extended_year: i32) -> RataDie {
31
0
        Self::EPOCH + (f64::from((extended_year - 1) * 12) * MEAN_SYNODIC_MONTH).floor() as i64
32
0
    }
Unexecuted instantiation: <calendrical_calculations::islamic::SaudiIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::mean_synodic_ny
Unexecuted instantiation: <calendrical_calculations::islamic::ObservationalIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::mean_synodic_ny
Unexecuted instantiation: <_ as calendrical_calculations::islamic::IslamicBasedMarker>::mean_synodic_ny
33
    /// Given an iso date, calculate the *approximate* islamic year it corresponds to (for quick cache lookup)
34
0
    fn approximate_islamic_from_fixed(date: RataDie) -> i32 {
35
0
        let diff = date - Self::EPOCH;
36
0
        let months = diff as f64 / MEAN_SYNODIC_MONTH;
37
0
        let years = months / 12.;
38
0
        (years + 1.).floor() as i32
39
0
    }
Unexecuted instantiation: <calendrical_calculations::islamic::SaudiIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::approximate_islamic_from_fixed
Unexecuted instantiation: <calendrical_calculations::islamic::ObservationalIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::approximate_islamic_from_fixed
Unexecuted instantiation: <_ as calendrical_calculations::islamic::IslamicBasedMarker>::approximate_islamic_from_fixed
40
    /// Convert an islamic date in this calendar to a R.D.
41
    fn fixed_from_islamic(year: i32, month: u8, day: u8) -> RataDie;
42
    /// Convert an R.D. To an islamic date in this calendar
43
    fn islamic_from_fixed(date: RataDie) -> (i32, u8, u8);
44
45
    /// Given an extended year, calculate whether each month is 29 or 30 days long
46
0
    fn month_lengths_for_year(extended_year: i32, ny: RataDie) -> [bool; 12] {
47
0
        let next_ny = Self::fixed_from_islamic(extended_year + 1, 1, 1);
48
0
        match next_ny - ny {
49
0
            355 | 354 => (),
50
0
            353 if Self::HAS_353_DAY_YEARS => {
51
0
                #[cfg(feature = "logging")]
52
0
                log::trace!(
53
0
                    "({}) Found year {extended_year} AH with length {}. See <https://github.com/unicode-org/icu4x/issues/4930>",
54
0
                    Self::DEBUG_NAME,
55
0
                    next_ny - ny
56
0
                );
57
0
            }
58
0
            other => {
59
0
                debug_assert!(
60
0
                    false,
61
0
                    "({}) Found year {extended_year} AH with length {}!",
62
                    Self::DEBUG_NAME,
63
                    other
64
                )
65
            }
66
        }
67
0
        let mut prev_rd = ny;
68
0
        let mut excess_days = 0;
69
0
        let mut lengths = core::array::from_fn(|month_idx| {
70
0
            let month_idx = month_idx as u8;
71
0
            let new_rd = if month_idx < 11 {
72
0
                Self::fixed_from_islamic(extended_year, month_idx + 2, 1)
73
            } else {
74
0
                next_ny
75
            };
76
0
            let diff = new_rd - prev_rd;
77
0
            prev_rd = new_rd;
78
0
            match diff {
79
0
                29 => false,
80
0
                30 => true,
81
                31 => {
82
                    #[cfg(feature = "logging")]
83
                    log::trace!(
84
                        "({}) Found year {extended_year} AH with month length {diff} for month {}.",
85
                        Self::DEBUG_NAME,
86
                        month_idx + 1
87
                    );
88
0
                    excess_days += 1;
89
0
                    true
90
                }
91
                _ => {
92
0
                    debug_assert!(
93
0
                        false,
94
0
                        "({}) Found year {extended_year} AH with month length {diff} for month {}!",
95
                        Self::DEBUG_NAME,
96
0
                        month_idx + 1
97
                    );
98
0
                    false
99
                }
100
            }
101
0
        });
Unexecuted instantiation: <calendrical_calculations::islamic::SaudiIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year::{closure#0}
Unexecuted instantiation: <calendrical_calculations::islamic::ObservationalIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year::{closure#0}
Unexecuted instantiation: <_ as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year::{closure#0}
102
        // To maintain invariants for calendar arithmetic, if astronomy finds
103
        // a 31-day month, "move" the day to the first 29-day month in the
104
        // same year to maintain all months at 29 or 30 days.
105
0
        if excess_days != 0 {
106
0
            debug_assert_eq!(
107
                excess_days,
108
                1,
109
0
                "({}) Found year {extended_year} AH with more than one excess day!",
110
                Self::DEBUG_NAME
111
            );
112
0
            if let Some(l) = lengths.iter_mut().find(|l| !(**l)) {
Unexecuted instantiation: <calendrical_calculations::islamic::SaudiIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year::{closure#1}
Unexecuted instantiation: <calendrical_calculations::islamic::ObservationalIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year::{closure#1}
Unexecuted instantiation: <_ as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year::{closure#1}
113
0
                *l = true;
114
0
            }
115
0
        }
116
0
        lengths
117
0
    }
Unexecuted instantiation: <calendrical_calculations::islamic::SaudiIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year
Unexecuted instantiation: <calendrical_calculations::islamic::ObservationalIslamicMarker as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year
Unexecuted instantiation: <_ as calendrical_calculations::islamic::IslamicBasedMarker>::month_lengths_for_year
118
}
119
120
/// Marker type for observational islamic calendar, for use with [`IslamicBasedMarker`]
121
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
122
#[allow(clippy::exhaustive_structs)] // marker
123
pub struct ObservationalIslamicMarker;
124
125
/// Marker type for Saudi islamic calendar, for use with [`IslamicBasedMarker`]
126
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
127
#[allow(clippy::exhaustive_structs)] // marker
128
pub struct SaudiIslamicMarker;
129
130
/// Marker type for civil islamic calendar, for use with [`IslamicBasedMarker`]
131
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
132
#[allow(clippy::exhaustive_structs)] // marker
133
pub struct CivilIslamicMarker;
134
135
/// Marker type for observational islamic calendar, for use with [`IslamicBasedMarker`]
136
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
137
#[allow(clippy::exhaustive_structs)] // marker
138
pub struct TabularIslamicMarker;
139
140
impl IslamicBasedMarker for ObservationalIslamicMarker {
141
    const EPOCH: RataDie = FIXED_ISLAMIC_EPOCH_FRIDAY;
142
    const DEBUG_NAME: &'static str = "ObservationalIslamic";
143
    const HAS_353_DAY_YEARS: bool = true;
144
0
    fn fixed_from_islamic(year: i32, month: u8, day: u8) -> RataDie {
145
0
        fixed_from_islamic_observational(year, month, day)
146
0
    }
147
0
    fn islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
148
0
        observational_islamic_from_fixed(date)
149
0
    }
150
}
151
152
impl IslamicBasedMarker for SaudiIslamicMarker {
153
    const EPOCH: RataDie = FIXED_ISLAMIC_EPOCH_FRIDAY;
154
    const DEBUG_NAME: &'static str = "SaudiIslamic";
155
    const HAS_353_DAY_YEARS: bool = true;
156
0
    fn fixed_from_islamic(year: i32, month: u8, day: u8) -> RataDie {
157
0
        fixed_from_saudi_islamic(year, month, day)
158
0
    }
159
0
    fn islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
160
0
        saudi_islamic_from_fixed(date)
161
0
    }
162
}
163
164
impl IslamicBasedMarker for CivilIslamicMarker {
165
    const EPOCH: RataDie = FIXED_ISLAMIC_EPOCH_FRIDAY;
166
    const DEBUG_NAME: &'static str = "CivilIslamic";
167
    const HAS_353_DAY_YEARS: bool = false;
168
0
    fn fixed_from_islamic(year: i32, month: u8, day: u8) -> RataDie {
169
0
        fixed_from_islamic_civil(year, month, day)
170
0
    }
171
0
    fn islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
172
0
        islamic_civil_from_fixed(date)
173
0
    }
174
}
175
176
impl IslamicBasedMarker for TabularIslamicMarker {
177
    const EPOCH: RataDie = FIXED_ISLAMIC_EPOCH_THURSDAY;
178
    const DEBUG_NAME: &'static str = "TabularIslamic";
179
    const HAS_353_DAY_YEARS: bool = false;
180
0
    fn fixed_from_islamic(year: i32, month: u8, day: u8) -> RataDie {
181
0
        fixed_from_islamic_tabular(year, month, day)
182
0
    }
183
0
    fn islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
184
0
        islamic_tabular_from_fixed(date)
185
0
    }
186
}
187
188
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6904>
189
0
pub fn fixed_from_islamic_observational(year: i32, month: u8, day: u8) -> RataDie {
190
0
    let year = i64::from(year);
191
0
    let month = i64::from(month);
192
0
    let day = i64::from(day);
193
0
    let midmonth = FIXED_ISLAMIC_EPOCH_FRIDAY.to_f64_date()
194
0
        + (((year - 1) as f64) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH;
195
0
    let lunar_phase = Astronomical::calculate_new_moon_at_or_before(RataDie::new(midmonth as i64));
196
0
    Astronomical::phasis_on_or_before(RataDie::new(midmonth as i64), CAIRO, Some(lunar_phase)) + day
197
0
        - 1
198
0
}
199
200
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L6983-L6995>
201
0
pub fn observational_islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
202
0
    let lunar_phase = Astronomical::calculate_new_moon_at_or_before(date);
203
0
    let crescent = Astronomical::phasis_on_or_before(date, CAIRO, Some(lunar_phase));
204
0
    let elapsed_months =
205
0
        ((crescent - FIXED_ISLAMIC_EPOCH_FRIDAY) as f64 / MEAN_SYNODIC_MONTH).round() as i32;
206
0
    let year = elapsed_months.div_euclid(12) + 1;
207
0
    let month = elapsed_months.rem_euclid(12) + 1;
208
0
    let day = (date - crescent + 1) as u8;
209
210
0
    (year, month as u8, day)
211
0
}
212
213
// Saudi visibility criterion on eve of fixed date in Mecca.
214
// The start of the new month only happens if both of these criterias are met: The moon is a waxing crescent at sunset of the previous day
215
// and the moon sets after the sun on that same evening.
216
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6957>
217
0
fn saudi_criterion(date: RataDie) -> Option<bool> {
218
0
    let sunset = Astronomical::sunset((date - 1).as_moment(), MECCA)?;
219
0
    let tee = Location::universal_from_standard(sunset, MECCA);
220
0
    let phase = Astronomical::lunar_phase(tee, Astronomical::julian_centuries(tee));
221
0
    let moonlag = Astronomical::moonlag((date - 1).as_moment(), MECCA)?;
222
223
0
    Some(phase > 0.0 && phase < 90.0 && moonlag > 0.0)
224
0
}
225
226
0
pub(crate) fn adjusted_saudi_criterion(date: RataDie) -> bool {
227
0
    saudi_criterion(date).unwrap_or_default()
228
0
}
229
230
// Closest fixed date on or before date when Saudi visibility criterion is held.
231
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6966>
232
0
pub fn saudi_new_month_on_or_before(date: RataDie) -> RataDie {
233
0
    let last_new_moon = (Astronomical::lunar_phase_at_or_before(0.0, date.as_moment()))
234
0
        .inner()
235
0
        .floor(); // Gets the R.D Date of the prior new moon
236
0
    let age = date.to_f64_date() - last_new_moon;
237
    // Explanation of why the value 3.0 is chosen: https://github.com/unicode-org/icu4x/pull/3673/files#r1267460916
238
0
    let tau = if age <= 3.0 && !adjusted_saudi_criterion(date) {
239
        // Checks if the criterion is not yet visible on the evening of date
240
0
        last_new_moon - 30.0 // Goes back a month
241
    } else {
242
0
        last_new_moon
243
    };
244
245
0
    next(RataDie::new(tau as i64), adjusted_saudi_criterion) // Loop that increments the day and checks if the criterion is now visible
246
0
}
247
248
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6996>
249
0
pub fn saudi_islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
250
0
    let crescent = saudi_new_month_on_or_before(date);
251
0
    let elapsed_months =
252
0
        ((crescent - FIXED_ISLAMIC_EPOCH_FRIDAY) as f64 / MEAN_SYNODIC_MONTH).round() as i64;
253
0
    let year = i64_to_saturated_i32(elapsed_months.div_euclid(12) + 1);
254
0
    let month = (elapsed_months.rem_euclid(12) + 1) as u8;
255
0
    let day = ((date - crescent) + 1) as u8;
256
257
0
    (year, month, day)
258
0
}
259
260
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6981>
261
0
pub fn fixed_from_saudi_islamic(year: i32, month: u8, day: u8) -> RataDie {
262
0
    let midmonth = RataDie::new(
263
0
        FIXED_ISLAMIC_EPOCH_FRIDAY.to_i64_date()
264
0
            + (((year as f64 - 1.0) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH).floor()
265
0
                as i64,
266
    );
267
0
    let first_day_of_month = saudi_new_month_on_or_before(midmonth).to_i64_date();
268
269
0
    RataDie::new(first_day_of_month + day as i64 - 1)
270
0
}
271
272
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2076>
273
0
pub fn fixed_from_islamic_civil(year: i32, month: u8, day: u8) -> RataDie {
274
0
    let year = i64::from(year);
275
0
    let month = i64::from(month);
276
0
    let day = i64::from(day);
277
278
0
    RataDie::new(
279
0
        (FIXED_ISLAMIC_EPOCH_FRIDAY.to_i64_date() - 1)
280
0
            + (year - 1) * 354
281
0
            + (3 + year * 11).div_euclid(30)
282
0
            + 29 * (month - 1)
283
0
            + month.div_euclid(2)
284
0
            + day,
285
    )
286
0
}
287
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2090>
288
0
pub fn islamic_civil_from_fixed(date: RataDie) -> (i32, u8, u8) {
289
0
    let year =
290
0
        i64_to_saturated_i32(((date - FIXED_ISLAMIC_EPOCH_FRIDAY) * 30 + 10646).div_euclid(10631));
291
0
    let prior_days = date.to_f64_date() - fixed_from_islamic_civil(year, 1, 1).to_f64_date();
292
0
    debug_assert!(prior_days >= 0.0);
293
0
    debug_assert!(prior_days <= 354.);
294
0
    let month = (((prior_days * 11.0) + 330.0) / 325.0) as u8; // Prior days is maximum 354 (when year length is 355), making the value always less than 12
295
0
    debug_assert!(month <= 12);
296
0
    let day =
297
0
        (date.to_f64_date() - fixed_from_islamic_civil(year, month, 1).to_f64_date() + 1.0) as u8; // The value will always be number between 1-30 because of the difference between the date and lunar ordinals function.
298
299
0
    (year, month, day)
300
0
}
301
302
/// Lisp code reference:https: //github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2076
303
0
pub fn fixed_from_islamic_tabular(year: i32, month: u8, day: u8) -> RataDie {
304
0
    let year = i64::from(year);
305
0
    let month = i64::from(month);
306
0
    let day = i64::from(day);
307
0
    RataDie::new(
308
0
        (FIXED_ISLAMIC_EPOCH_THURSDAY.to_i64_date() - 1)
309
0
            + (year - 1) * 354
310
0
            + (3 + year * 11).div_euclid(30)
311
0
            + 29 * (month - 1)
312
0
            + month.div_euclid(2)
313
0
            + day,
314
    )
315
0
}
316
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2090>
317
0
pub fn islamic_tabular_from_fixed(date: RataDie) -> (i32, u8, u8) {
318
0
    let year = i64_to_saturated_i32(
319
0
        ((date - FIXED_ISLAMIC_EPOCH_THURSDAY) * 30 + 10646).div_euclid(10631),
320
    );
321
0
    let prior_days = date.to_f64_date() - fixed_from_islamic_tabular(year, 1, 1).to_f64_date();
322
0
    debug_assert!(prior_days >= 0.0);
323
0
    debug_assert!(prior_days <= 354.);
324
0
    let month = (((prior_days * 11.0) + 330.0) / 325.0) as u8; // Prior days is maximum 354 (when year length is 355), making the value always less than 12
325
0
    debug_assert!(month <= 12);
326
0
    let day =
327
0
        (date.to_f64_date() - fixed_from_islamic_tabular(year, month, 1).to_f64_date() + 1.0) as u8; // The value will always be number between 1-30 because of the difference between the date and lunar ordinals function.
328
329
0
    (year, month, day)
330
0
}
331
332
/// The number of days in a month for the observational islamic calendar
333
0
pub fn observational_islamic_month_days(year: i32, month: u8) -> u8 {
334
0
    let midmonth = FIXED_ISLAMIC_EPOCH_FRIDAY.to_f64_date()
335
0
        + (((year - 1) as f64) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH;
336
337
0
    let lunar_phase: f64 =
338
0
        Astronomical::calculate_new_moon_at_or_before(RataDie::new(midmonth as i64));
339
0
    let f_date =
340
0
        Astronomical::phasis_on_or_before(RataDie::new(midmonth as i64), CAIRO, Some(lunar_phase));
341
342
0
    Astronomical::month_length(f_date, CAIRO)
343
0
}
344
345
/// The number of days in a month for the Saudi (Umm Al-Qura) calendar
346
0
pub fn saudi_islamic_month_days(year: i32, month: u8) -> u8 {
347
    // We cannot use month_days from the book here, that is for the observational calendar
348
    //
349
    // Instead we subtract the two new months calculated using the saudi criterion
350
0
    let midmonth = Moment::new(
351
0
        FIXED_ISLAMIC_EPOCH_FRIDAY.to_f64_date()
352
0
            + (((year - 1) as f64) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH,
353
    );
354
0
    let midmonth_next = midmonth + MEAN_SYNODIC_MONTH;
355
356
0
    let month_start = saudi_new_month_on_or_before(midmonth.as_rata_die());
357
0
    let next_month_start = saudi_new_month_on_or_before(midmonth_next.as_rata_die());
358
359
0
    let diff = next_month_start - month_start;
360
0
    debug_assert!(
361
0
        diff <= 30,
362
0
        "umm-al-qura months must not be more than 30 days"
363
    );
364
0
    u8::try_from(diff).unwrap_or(30)
365
0
}
366
367
#[cfg(test)]
368
mod tests {
369
    use super::*;
370
371
    static TEST_FIXED_DATE: [i64; 33] = [
372
        -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
373
        470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
374
        664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
375
    ];
376
    // Removed: 601716 and 727274 fixed dates
377
    static TEST_FIXED_DATE_UMMALQURA: [i64; 31] = [
378
        -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
379
        470160, 473837, 507850, 524156, 544676, 567118, 569477, 613424, 626596, 645554, 664224,
380
        671401, 694799, 704424, 708842, 709409, 709580, 728714, 744313, 764652,
381
    ];
382
    // Values from lisp code
383
    static SAUDI_CRITERION_EXPECTED: [bool; 33] = [
384
        false, false, true, false, false, true, false, true, false, false, true, false, false,
385
        true, true, true, true, false, false, true, true, true, false, false, false, false, false,
386
        false, true, false, true, false, true,
387
    ];
388
    // Values from lisp code, removed two expected months.
389
    static SAUDI_NEW_MONTH_OR_BEFORE_EXPECTED: [f64; 31] = [
390
        -214203.0, -61412.0, 25467.0, 49210.0, 171290.0, 210152.0, 253414.0, 369735.0, 400063.0,
391
        434348.0, 452598.0, 470139.0, 473830.0, 507850.0, 524150.0, 544674.0, 567118.0, 569450.0,
392
        613421.0, 626592.0, 645551.0, 664214.0, 671391.0, 694779.0, 704405.0, 708835.0, 709396.0,
393
        709573.0, 728709.0, 744301.0, 764647.0,
394
    ];
395
    #[test]
396
    fn test_islamic_epoch_friday() {
397
        let epoch = FIXED_ISLAMIC_EPOCH_FRIDAY.to_i64_date();
398
        // Iso year of Islamic Epoch
399
        let epoch_year_from_fixed = crate::iso::iso_year_from_fixed(RataDie::new(epoch));
400
        // 622 is the correct ISO year for the Islamic Epoch
401
        assert_eq!(epoch_year_from_fixed, 622);
402
    }
403
404
    #[test]
405
    fn test_islamic_epoch_thursday() {
406
        let epoch = FIXED_ISLAMIC_EPOCH_THURSDAY.to_i64_date();
407
        // Iso year of Islamic Epoch
408
        let epoch_year_from_fixed = crate::iso::iso_year_from_fixed(RataDie::new(epoch));
409
        // 622 is the correct ISO year for the Islamic Epoch
410
        assert_eq!(epoch_year_from_fixed, 622);
411
    }
412
413
    #[test]
414
    fn test_saudi_criterion() {
415
        for (boolean, f_date) in SAUDI_CRITERION_EXPECTED.iter().zip(TEST_FIXED_DATE.iter()) {
416
            let bool_result = saudi_criterion(RataDie::new(*f_date)).unwrap();
417
            assert_eq!(*boolean, bool_result, "{f_date:?}");
418
        }
419
    }
420
421
    #[test]
422
    fn test_saudi_new_month_or_before() {
423
        for (date, f_date) in SAUDI_NEW_MONTH_OR_BEFORE_EXPECTED
424
            .iter()
425
            .zip(TEST_FIXED_DATE_UMMALQURA.iter())
426
        {
427
            let date_result = saudi_new_month_on_or_before(RataDie::new(*f_date)).to_f64_date();
428
            assert_eq!(*date, date_result, "{f_date:?}");
429
        }
430
    }
431
}