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/icu_calendar-1.5.2/src/islamic.rs
Line
Count
Source
1
// This file is part of ICU4X. For terms of use, please see the file
2
// called LICENSE at the top level of the ICU4X source tree
3
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5
//! This module contains types and implementations for the Islamic calendars.
6
//!
7
//! ```rust
8
//! use icu::calendar::islamic::IslamicObservational;
9
//! use icu::calendar::{Date, DateTime, Ref};
10
//!
11
//! let islamic = IslamicObservational::new_always_calculating();
12
//! let islamic = Ref(&islamic); // to avoid cloning
13
//!
14
//! // `Date` type
15
//! let islamic_date =
16
//!     Date::try_new_observational_islamic_date(1348, 10, 11, islamic)
17
//!         .expect("Failed to initialize islamic Date instance.");
18
//!
19
//! // `DateTime` type
20
//! let islamic_datetime = DateTime::try_new_observational_islamic_datetime(
21
//!     1348, 10, 11, 13, 1, 0, islamic,
22
//! )
23
//! .expect("Failed to initialize islamic DateTime instance.");
24
//!
25
//! // `Date` checks
26
//! assert_eq!(islamic_date.year().number, 1348);
27
//! assert_eq!(islamic_date.month().ordinal, 10);
28
//! assert_eq!(islamic_date.day_of_month().0, 11);
29
//!
30
//! // `DateTime` checks
31
//! assert_eq!(islamic_datetime.date.year().number, 1348);
32
//! assert_eq!(islamic_datetime.date.month().ordinal, 10);
33
//! assert_eq!(islamic_datetime.date.day_of_month().0, 11);
34
//! assert_eq!(islamic_datetime.time.hour.number(), 13);
35
//! assert_eq!(islamic_datetime.time.minute.number(), 1);
36
//! assert_eq!(islamic_datetime.time.second.number(), 0);
37
//! ```
38
39
use crate::calendar_arithmetic::PrecomputedDataSource;
40
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
41
use crate::provider::islamic::{
42
    IslamicCacheV1, IslamicObservationalCacheV1Marker, IslamicUmmAlQuraCacheV1Marker,
43
    PackedIslamicYearInfo,
44
};
45
use crate::AnyCalendarKind;
46
use crate::AsCalendar;
47
use crate::Iso;
48
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time};
49
use calendrical_calculations::islamic::{
50
    IslamicBasedMarker, ObservationalIslamicMarker, SaudiIslamicMarker,
51
};
52
use calendrical_calculations::rata_die::RataDie;
53
use core::marker::PhantomData;
54
use icu_provider::prelude::*;
55
use tinystr::tinystr;
56
57
/// Islamic Observational Calendar (Default)
58
///
59
/// # Era codes
60
///
61
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
62
///
63
/// # Month codes
64
///
65
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
66
/// `"M01" - "M12"`.
67
#[derive(Clone, Debug, Default)]
68
pub struct IslamicObservational {
69
    data: Option<DataPayload<IslamicObservationalCacheV1Marker>>,
70
}
71
72
/// Civil / Arithmetical Islamic Calendar (Used for administrative purposes)
73
///
74
/// # Era codes
75
///
76
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
77
///
78
/// # Month codes
79
///
80
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
81
/// `"M01" - "M12"`.
82
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
83
#[allow(clippy::exhaustive_structs)] // unit struct
84
pub struct IslamicCivil;
85
86
/// Umm al-Qura Hijri Calendar (Used in Saudi Arabia)
87
///
88
/// # Era codes
89
///
90
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
91
///
92
/// # Month codes
93
///
94
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
95
/// `"M01" - "M12"`.
96
#[derive(Clone, Debug, Default)]
97
pub struct IslamicUmmAlQura {
98
    data: Option<DataPayload<IslamicUmmAlQuraCacheV1Marker>>,
99
}
100
101
/// A Tabular version of the Arithmetical Islamic Calendar
102
///
103
/// # Era codes
104
///
105
/// This calendar supports a single era code, Anno Mundi, with code `"ah"`
106
///
107
/// # Month codes
108
///
109
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
110
/// `"M01" - "M12"`.
111
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
112
#[allow(clippy::exhaustive_structs)] // unit struct
113
pub struct IslamicTabular;
114
115
impl IslamicObservational {
116
    /// Creates a new [`IslamicObservational`] with some compiled data containing precomputed calendrical calculations.
117
    ///
118
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
119
    ///
120
    /// [📚 Help choosing a constructor](icu_provider::constructors)
121
    #[cfg(feature = "compiled_data")]
122
0
    pub const fn new() -> Self {
123
0
        Self {
124
0
            data: Some(DataPayload::from_static_ref(
125
0
                crate::provider::Baked::SINGLETON_CALENDAR_ISLAMICOBSERVATIONALCACHE_V1,
126
0
            )),
127
0
        }
128
0
    }
129
130
    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
131
        #[cfg(skip)]
132
        functions: [
133
            new,
134
            try_new_with_any_provider,
135
            try_new_with_buffer_provider,
136
            try_new_unstable,
137
            Self,
138
    ]);
139
140
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
141
0
    pub fn try_new_unstable<D: DataProvider<IslamicObservationalCacheV1Marker> + ?Sized>(
142
0
        provider: &D,
143
0
    ) -> Result<Self, CalendarError> {
144
        Ok(Self {
145
0
            data: Some(provider.load(Default::default())?.take_payload()?),
146
        })
147
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicObservational>::try_new_unstable::<icu_provider::any::DowncastingAnyProvider<icu_provider_adapters::empty::EmptyDataProvider>>
Unexecuted instantiation: <icu_calendar::islamic::IslamicObservational>::try_new_unstable::<_>
148
149
    /// Construct a new [`IslamicObservational`] without any precomputed calendrical calculations.
150
0
    pub fn new_always_calculating() -> Self {
151
0
        Self { data: None }
152
0
    }
153
}
154
155
impl IslamicCivil {
156
    /// Construct a new [`IslamicCivil`]
157
0
    pub fn new() -> Self {
158
0
        Self
159
0
    }
160
161
    /// Construct a new [`IslamicCivil`] (deprecated: we will not add precomputation to this calendar)
162
    #[deprecated = "Precomputation not needed for this calendar"]
163
0
    pub fn new_always_calculating() -> Self {
164
0
        Self
165
0
    }
166
}
167
168
impl IslamicUmmAlQura {
169
    /// Creates a new [`IslamicUmmAlQura`] with some compiled data containing precomputed calendrical calculations.
170
    ///
171
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
172
    ///
173
    /// [📚 Help choosing a constructor](icu_provider::constructors)
174
    #[cfg(feature = "compiled_data")]
175
0
    pub const fn new() -> Self {
176
0
        Self {
177
0
            data: Some(DataPayload::from_static_ref(
178
0
                crate::provider::Baked::SINGLETON_CALENDAR_ISLAMICUMMALQURACACHE_V1,
179
0
            )),
180
0
        }
181
0
    }
182
183
    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
184
        #[cfg(skip)]
185
        functions: [
186
            new,
187
            try_new_with_any_provider,
188
            try_new_with_buffer_provider,
189
            try_new_unstable,
190
            Self,
191
    ]);
192
193
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
194
0
    pub fn try_new_unstable<D: DataProvider<IslamicUmmAlQuraCacheV1Marker> + ?Sized>(
195
0
        provider: &D,
196
0
    ) -> Result<Self, CalendarError> {
197
        Ok(Self {
198
0
            data: Some(provider.load(Default::default())?.take_payload()?),
199
        })
200
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicUmmAlQura>::try_new_unstable::<icu_provider::any::DowncastingAnyProvider<icu_provider_adapters::empty::EmptyDataProvider>>
Unexecuted instantiation: <icu_calendar::islamic::IslamicUmmAlQura>::try_new_unstable::<_>
201
202
    /// Construct a new [`IslamicUmmAlQura`] without any precomputed calendrical calculations.
203
0
    pub fn new_always_calculating() -> Self {
204
0
        Self { data: None }
205
0
    }
206
}
207
208
impl IslamicTabular {
209
    /// Construct a new [`IslamicTabular`]
210
0
    pub fn new() -> Self {
211
0
        Self
212
0
    }
213
214
    /// Construct a new [`IslamicTabular`] (deprecated: we will not add precomputation to this calendar)
215
    #[deprecated = "Precomputation not needed for this calendar"]
216
0
    pub fn new_always_calculating() -> Self {
217
0
        Self
218
0
    }
219
}
220
221
/// Compact representation of the length of an Islamic year.
222
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
223
enum IslamicYearLength {
224
    /// Long (355-day) Islamic year
225
    L355,
226
    /// Short (354-day) Islamic year
227
    L354,
228
    /// Unexpectedly Short (353-day) Islamic year
229
    ///
230
    /// It is probably a bug when this year length is returned. See:
231
    /// <https://github.com/unicode-org/icu4x/issues/4930>
232
    L353,
233
}
234
235
impl Default for IslamicYearLength {
236
0
    fn default() -> Self {
237
0
        Self::L354
238
0
    }
239
}
240
241
impl IslamicYearLength {
242
0
    fn try_from_int(value: i64) -> Option<Self> {
243
0
        match value {
244
0
            355 => Some(Self::L355),
245
0
            354 => Some(Self::L354),
246
0
            353 => Some(Self::L353),
247
0
            _ => None,
248
        }
249
0
    }
250
0
    fn to_int(self) -> u16 {
251
0
        match self {
252
0
            Self::L355 => 355,
253
0
            Self::L354 => 354,
254
0
            Self::L353 => 353,
255
        }
256
0
    }
257
}
258
259
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
260
pub(crate) struct IslamicYearInfo {
261
    packed_data: PackedIslamicYearInfo,
262
    prev_year_length: IslamicYearLength,
263
}
264
265
impl IslamicYearInfo {
266
    pub(crate) const LONG_YEAR_LEN: u16 = 355;
267
    const SHORT_YEAR_LEN: u16 = 354;
268
0
    pub(crate) fn new(
269
0
        prev_packed: PackedIslamicYearInfo,
270
0
        this_packed: PackedIslamicYearInfo,
271
0
        extended_year: i32,
272
0
    ) -> (Self, i32) {
273
0
        let days_in_year = prev_packed.days_in_year();
274
0
        let days_in_year = match IslamicYearLength::try_from_int(days_in_year as i64) {
275
0
            Some(x) => x,
276
            None => {
277
0
                debug_assert!(false, "Found wrong year length for Islamic year {extended_year}: Expected 355, 354, or 353, got {days_in_year}");
278
0
                Default::default()
279
            }
280
        };
281
0
        let year_info = Self {
282
0
            prev_year_length: days_in_year,
283
0
            packed_data: this_packed,
284
0
        };
285
0
        (year_info, extended_year)
286
0
    }
287
288
0
    fn compute<IB: IslamicBasedMarker>(extended_year: i32) -> Self {
289
0
        let ny = IB::fixed_from_islamic(extended_year, 1, 1);
290
0
        let packed_data = PackedIslamicYearInfo::compute_with_ny::<IB>(extended_year, ny);
291
0
        let prev_ny = IB::fixed_from_islamic(extended_year - 1, 1, 1);
292
0
        let rd_diff = ny - prev_ny;
293
0
        let rd_diff = match IslamicYearLength::try_from_int(rd_diff) {
294
0
            Some(x) => x,
295
            None => {
296
0
                debug_assert!(false, "({}) Found wrong year length for Islamic year {extended_year}: Expected 355, 354, or 353, got {rd_diff}", IB::DEBUG_NAME);
297
0
                Default::default()
298
            }
299
        };
300
0
        Self {
301
0
            prev_year_length: rd_diff,
302
0
            packed_data,
303
0
        }
304
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicYearInfo>::compute::<calendrical_calculations::islamic::SaudiIslamicMarker>
Unexecuted instantiation: <icu_calendar::islamic::IslamicYearInfo>::compute::<calendrical_calculations::islamic::ObservationalIslamicMarker>
305
    /// Get the new year R.D. given the extended year that this yearinfo is for    
306
0
    fn new_year<IB: IslamicBasedMarker>(self, extended_year: i32) -> RataDie {
307
0
        IB::mean_synodic_ny(extended_year) + i64::from(self.packed_data.ny_offset())
308
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicYearInfo>::new_year::<calendrical_calculations::islamic::SaudiIslamicMarker>
Unexecuted instantiation: <icu_calendar::islamic::IslamicYearInfo>::new_year::<calendrical_calculations::islamic::ObservationalIslamicMarker>
309
310
    /// Get the date's R.D. given (y, m, d) in this info's year
311
0
    fn rd_for<IB: IslamicBasedMarker>(self, extended_year: i32, month: u8, day: u8) -> RataDie {
312
0
        let ny = self.new_year::<IB>(extended_year);
313
0
        let month_offset = if month == 1 {
314
0
            0
315
        } else {
316
0
            self.packed_data.last_day_of_month(month - 1)
317
        };
318
        // -1 since the offset is 1-indexed but the new year is also day 1
319
0
        ny - 1 + month_offset.into() + day.into()
320
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicYearInfo>::rd_for::<calendrical_calculations::islamic::SaudiIslamicMarker>
Unexecuted instantiation: <icu_calendar::islamic::IslamicYearInfo>::rd_for::<calendrical_calculations::islamic::ObservationalIslamicMarker>
321
322
    #[inline]
323
0
    fn days_in_prev_year(self) -> u16 {
324
0
        self.prev_year_length.to_int()
325
0
    }
326
}
327
328
/// Contains any loaded precomputed data. If constructed with Default, will
329
/// *not* contain any extra data and will always compute stuff from scratch
330
#[derive(Default)]
331
pub(crate) struct IslamicPrecomputedData<'a, IB: IslamicBasedMarker> {
332
    data: Option<&'a IslamicCacheV1<'a>>,
333
    _ib: PhantomData<IB>,
334
}
335
336
impl<'b, IB: IslamicBasedMarker> PrecomputedDataSource<IslamicYearInfo>
337
    for IslamicPrecomputedData<'b, IB>
338
{
339
0
    fn load_or_compute_info(&self, extended_year: i32) -> IslamicYearInfo {
340
0
        self.data
341
0
            .and_then(|d| d.get_for_extended_year(extended_year))
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::SaudiIslamicMarker> as icu_calendar::calendar_arithmetic::PrecomputedDataSource<icu_calendar::islamic::IslamicYearInfo>>::load_or_compute_info::{closure#0}
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::ObservationalIslamicMarker> as icu_calendar::calendar_arithmetic::PrecomputedDataSource<icu_calendar::islamic::IslamicYearInfo>>::load_or_compute_info::{closure#0}
342
0
            .unwrap_or_else(|| IslamicYearInfo::compute::<IB>(extended_year))
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::SaudiIslamicMarker> as icu_calendar::calendar_arithmetic::PrecomputedDataSource<icu_calendar::islamic::IslamicYearInfo>>::load_or_compute_info::{closure#1}
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::ObservationalIslamicMarker> as icu_calendar::calendar_arithmetic::PrecomputedDataSource<icu_calendar::islamic::IslamicYearInfo>>::load_or_compute_info::{closure#1}
343
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::SaudiIslamicMarker> as icu_calendar::calendar_arithmetic::PrecomputedDataSource<icu_calendar::islamic::IslamicYearInfo>>::load_or_compute_info
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::ObservationalIslamicMarker> as icu_calendar::calendar_arithmetic::PrecomputedDataSource<icu_calendar::islamic::IslamicYearInfo>>::load_or_compute_info
344
}
345
346
/// Given a year info and the first month it is possible for this date to be in, return the
347
/// month and day this is in
348
0
fn compute_month_day(info: IslamicYearInfo, mut possible_month: u8, day_of_year: u16) -> (u8, u8) {
349
0
    let mut last_day_of_month = info.packed_data.last_day_of_month(possible_month);
350
0
    let mut last_day_of_prev_month = if possible_month == 1 {
351
0
        0
352
    } else {
353
0
        info.packed_data.last_day_of_month(possible_month - 1)
354
    };
355
356
0
    while day_of_year > last_day_of_month && possible_month <= 12 {
357
0
        possible_month += 1;
358
0
        last_day_of_prev_month = last_day_of_month;
359
0
        last_day_of_month = info.packed_data.last_day_of_month(possible_month);
360
0
    }
361
0
    let day = u8::try_from(day_of_year - last_day_of_prev_month);
362
0
    debug_assert!(
363
0
        day.is_ok(),
364
0
        "Found day {} that doesn't fit in month!",
365
0
        day_of_year - last_day_of_prev_month
366
    );
367
0
    (possible_month, day.unwrap_or(29))
368
0
}
369
impl<'b, IB: IslamicBasedMarker> IslamicPrecomputedData<'b, IB> {
370
0
    pub(crate) fn new(data: Option<&'b IslamicCacheV1<'b>>) -> Self {
371
0
        Self {
372
0
            data,
373
0
            _ib: PhantomData,
374
0
        }
375
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::SaudiIslamicMarker>>::new
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::ObservationalIslamicMarker>>::new
376
    /// Given an ISO date (in both ArithmeticDate and R.D. format), returns the IslamicYearInfo and extended year for that date, loading
377
    /// from cache or computing.
378
0
    fn load_or_compute_info_for_iso(&self, fixed: RataDie) -> (IslamicYearInfo, i32, u8, u8) {
379
0
        let cached = self.data.and_then(|d| d.get_for_fixed::<IB>(fixed));
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::SaudiIslamicMarker>>::load_or_compute_info_for_iso::{closure#0}
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::ObservationalIslamicMarker>>::load_or_compute_info_for_iso::{closure#0}
380
0
        if let Some((cached, year)) = cached {
381
0
            let ny = cached.packed_data.ny::<IB>(year);
382
0
            let day_of_year = (fixed - ny) as u16 + 1;
383
0
            debug_assert!(day_of_year < 360);
384
            // We divide by 30, not 29, to account for the case where all months before this
385
            // were length 30 (possible near the beginning of the year)
386
            // We add +1 because months are 1-indexed
387
0
            let possible_month = u8::try_from(1 + (day_of_year / 30)).unwrap_or(1);
388
0
            let (m, d) = compute_month_day(cached, possible_month, day_of_year);
389
0
            return (cached, year, m, d);
390
0
        };
391
        // compute
392
393
0
        let (y, m, d) = IB::islamic_from_fixed(fixed);
394
0
        let info = IslamicYearInfo::compute::<IB>(y);
395
0
        let ny = info.packed_data.ny::<IB>(y);
396
0
        let day_of_year = (fixed - ny) as u16 + 1;
397
        // We can't use the m/d from islamic_from_fixed because that code
398
        // occasionally throws up 31-day months, which we normalize out. So we instead back-compute, starting with the previous month
399
0
        let (m, d) = if m > 1 {
400
0
            compute_month_day(info, m - 1, day_of_year)
401
        } else {
402
0
            (m, d)
403
        };
404
0
        (info, y, m, d)
405
0
    }
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::SaudiIslamicMarker>>::load_or_compute_info_for_iso
Unexecuted instantiation: <icu_calendar::islamic::IslamicPrecomputedData<calendrical_calculations::islamic::ObservationalIslamicMarker>>::load_or_compute_info_for_iso
406
}
407
408
/// The inner date type used for representing [`Date`]s of [`IslamicObservational`]. See [`Date`] and [`IslamicObservational`] for more details.
409
410
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
411
pub struct IslamicDateInner(ArithmeticDate<IslamicObservational>);
412
413
impl CalendarArithmetic for IslamicObservational {
414
    type YearInfo = IslamicYearInfo;
415
416
0
    fn month_days(_year: i32, month: u8, year_info: IslamicYearInfo) -> u8 {
417
0
        year_info.packed_data.days_in_month(month)
418
0
    }
419
420
0
    fn months_for_every_year(_year: i32, _year_info: IslamicYearInfo) -> u8 {
421
0
        12
422
0
    }
423
424
0
    fn days_in_provided_year(_year: i32, year_info: IslamicYearInfo) -> u16 {
425
0
        year_info.packed_data.days_in_year()
426
0
    }
427
428
    // As an true lunar calendar, it does not have leap years.
429
0
    fn is_leap_year(_year: i32, year_info: IslamicYearInfo) -> bool {
430
0
        year_info.packed_data.days_in_year() != IslamicYearInfo::SHORT_YEAR_LEN
431
0
    }
432
433
0
    fn last_month_day_in_year(year: i32, year_info: IslamicYearInfo) -> (u8, u8) {
434
0
        let days = Self::month_days(year, 12, year_info);
435
436
0
        (12, days)
437
0
    }
438
}
439
440
impl Calendar for IslamicObservational {
441
    type DateInner = IslamicDateInner;
442
0
    fn date_from_codes(
443
0
        &self,
444
0
        era: types::Era,
445
0
        year: i32,
446
0
        month_code: types::MonthCode,
447
0
        day: u8,
448
0
    ) -> Result<Self::DateInner, CalendarError> {
449
0
        let year = if era.0 == tinystr!(16, "islamic") || era.0 == tinystr!(16, "ah") {
450
0
            year
451
        } else {
452
0
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
453
        };
454
0
        let month = if let Some((ordinal, false)) = month_code.parsed() {
455
0
            ordinal
456
        } else {
457
0
            return Err(CalendarError::UnknownMonthCode(
458
0
                month_code.0,
459
0
                self.debug_name(),
460
0
            ));
461
        };
462
0
        ArithmeticDate::new_from_ordinals_with_info(
463
0
            year,
464
0
            month,
465
0
            day,
466
0
            self.precomputed_data().load_or_compute_info(year),
467
        )
468
0
        .map(IslamicDateInner)
469
0
    }
470
471
0
    fn date_from_iso(&self, iso: Date<crate::Iso>) -> Self::DateInner {
472
0
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());
473
474
0
        let (year_info, y, m, d) = self
475
0
            .precomputed_data()
476
0
            .load_or_compute_info_for_iso(fixed_iso);
477
0
        IslamicDateInner(ArithmeticDate::new_unchecked_with_info(y, m, d, year_info))
478
0
    }
479
480
0
    fn date_to_iso(&self, date: &Self::DateInner) -> Date<crate::Iso> {
481
0
        let fixed = date.0.year_info.rd_for::<ObservationalIslamicMarker>(
482
0
            date.0.year,
483
0
            date.0.month,
484
0
            date.0.day,
485
        );
486
0
        Iso::iso_from_fixed(fixed)
487
0
    }
488
489
0
    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
490
0
        date.0.months_in_year()
491
0
    }
492
493
0
    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
494
0
        date.0.days_in_year()
495
0
    }
496
497
0
    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
498
0
        date.0.days_in_month()
499
0
    }
500
501
0
    fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
502
0
        Iso.day_of_week(self.date_to_iso(date).inner())
503
0
    }
504
505
0
    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
506
0
        date.0.offset_date(offset, &self.precomputed_data())
507
0
    }
508
509
0
    fn until(
510
0
        &self,
511
0
        date1: &Self::DateInner,
512
0
        date2: &Self::DateInner,
513
0
        _calendar2: &Self,
514
0
        _largest_unit: DateDurationUnit,
515
0
        _smallest_unit: DateDurationUnit,
516
0
    ) -> DateDuration<Self> {
517
0
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
518
0
    }
519
520
0
    fn debug_name(&self) -> &'static str {
521
0
        Self::DEBUG_NAME
522
0
    }
523
524
0
    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
525
0
        Self::year_as_islamic(date.0.year)
526
0
    }
527
528
0
    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
529
0
        Self::is_leap_year(date.0.year, date.0.year_info)
530
0
    }
531
532
0
    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
533
0
        date.0.month()
534
0
    }
535
536
0
    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
537
0
        date.0.day_of_month()
538
0
    }
539
540
0
    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
541
0
        let prev_year = date.0.year.saturating_sub(1);
542
0
        let next_year = date.0.year.saturating_add(1);
543
0
        types::DayOfYearInfo {
544
0
            day_of_year: date.0.day_of_year(),
545
0
            days_in_year: date.0.days_in_year(),
546
0
            prev_year: Self::year_as_islamic(prev_year),
547
0
            days_in_prev_year: date.0.year_info.days_in_prev_year(),
548
0
            next_year: Self::year_as_islamic(next_year),
549
0
        }
550
0
    }
551
552
0
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
553
0
        Some(AnyCalendarKind::IslamicObservational)
554
0
    }
555
}
556
557
impl IslamicObservational {
558
0
    fn precomputed_data(&self) -> IslamicPrecomputedData<ObservationalIslamicMarker> {
559
0
        IslamicPrecomputedData::new(self.data.as_ref().map(|x| x.get()))
560
0
    }
561
562
0
    fn year_as_islamic(year: i32) -> types::FormattableYear {
563
0
        types::FormattableYear {
564
0
            era: types::Era(tinystr!(16, "islamic")),
565
0
            number: year,
566
0
            cyclic: None,
567
0
            related_iso: None,
568
0
        }
569
0
    }
570
    pub(crate) const DEBUG_NAME: &'static str = "Islamic (observational)";
571
}
572
573
impl<A: AsCalendar<Calendar = IslamicObservational>> Date<A> {
574
    /// Construct new Islamic Observational Date.
575
    ///
576
    /// Has no negative years, only era is the AH.
577
    ///
578
    /// ```rust
579
    /// use icu::calendar::islamic::IslamicObservational;
580
    /// use icu::calendar::Date;
581
    ///
582
    /// let islamic = IslamicObservational::new_always_calculating();
583
    ///
584
    /// let date_islamic =
585
    ///     Date::try_new_observational_islamic_date(1392, 4, 25, islamic)
586
    ///         .expect("Failed to initialize Islamic Date instance.");
587
    ///
588
    /// assert_eq!(date_islamic.year().number, 1392);
589
    /// assert_eq!(date_islamic.month().ordinal, 4);
590
    /// assert_eq!(date_islamic.day_of_month().0, 25);
591
    /// ```
592
0
    pub fn try_new_observational_islamic_date(
593
0
        year: i32,
594
0
        month: u8,
595
0
        day: u8,
596
0
        calendar: A,
597
0
    ) -> Result<Date<A>, CalendarError> {
598
0
        let year_info = calendar
599
0
            .as_calendar()
600
0
            .precomputed_data()
601
0
            .load_or_compute_info(year);
602
0
        ArithmeticDate::new_from_ordinals_with_info(year, month, day, year_info)
603
0
            .map(IslamicDateInner)
604
0
            .map(|inner| Date::from_raw(inner, calendar))
605
0
    }
606
}
607
608
impl<A: AsCalendar<Calendar = IslamicObservational>> DateTime<A> {
609
    /// Construct a new Islamic Observational datetime from integers.
610
    ///
611
    /// ```rust
612
    /// use icu::calendar::islamic::IslamicObservational;
613
    /// use icu::calendar::DateTime;
614
    ///
615
    /// let islamic = IslamicObservational::new_always_calculating();
616
    ///
617
    /// let datetime_islamic = DateTime::try_new_observational_islamic_datetime(
618
    ///     474, 10, 11, 13, 1, 0, islamic,
619
    /// )
620
    /// .expect("Failed to initialize Islamic DateTime instance.");
621
    ///
622
    /// assert_eq!(datetime_islamic.date.year().number, 474);
623
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
624
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
625
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
626
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
627
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
628
    /// ```
629
0
    pub fn try_new_observational_islamic_datetime(
630
0
        year: i32,
631
0
        month: u8,
632
0
        day: u8,
633
0
        hour: u8,
634
0
        minute: u8,
635
0
        second: u8,
636
0
        calendar: A,
637
0
    ) -> Result<DateTime<A>, CalendarError> {
638
        Ok(DateTime {
639
0
            date: Date::try_new_observational_islamic_date(year, month, day, calendar)?,
640
0
            time: Time::try_new(hour, minute, second, 0)?,
641
        })
642
0
    }
643
}
644
645
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
646
/// The inner date type used for representing [`Date`]s of [`IslamicUmmAlQura`]. See [`Date`] and [`IslamicUmmAlQura`] for more details.
647
pub struct IslamicUmmAlQuraDateInner(ArithmeticDate<IslamicUmmAlQura>);
648
649
impl CalendarArithmetic for IslamicUmmAlQura {
650
    type YearInfo = IslamicYearInfo;
651
652
0
    fn month_days(_year: i32, month: u8, year_info: IslamicYearInfo) -> u8 {
653
0
        year_info.packed_data.days_in_month(month)
654
0
    }
655
656
0
    fn months_for_every_year(_year: i32, _year_info: IslamicYearInfo) -> u8 {
657
0
        12
658
0
    }
659
660
0
    fn days_in_provided_year(_year: i32, year_info: IslamicYearInfo) -> u16 {
661
0
        year_info.packed_data.days_in_year()
662
0
    }
663
664
    // As an true lunar calendar, it does not have leap years.
665
0
    fn is_leap_year(_year: i32, year_info: IslamicYearInfo) -> bool {
666
0
        year_info.packed_data.days_in_year() != IslamicYearInfo::SHORT_YEAR_LEN
667
0
    }
668
669
0
    fn last_month_day_in_year(year: i32, year_info: IslamicYearInfo) -> (u8, u8) {
670
0
        let days = Self::month_days(year, 12, year_info);
671
672
0
        (12, days)
673
0
    }
674
}
675
676
impl Calendar for IslamicUmmAlQura {
677
    type DateInner = IslamicUmmAlQuraDateInner;
678
0
    fn date_from_codes(
679
0
        &self,
680
0
        era: types::Era,
681
0
        year: i32,
682
0
        month_code: types::MonthCode,
683
0
        day: u8,
684
0
    ) -> Result<Self::DateInner, CalendarError> {
685
0
        let year = if era.0 == tinystr!(16, "islamic-umalqura")
686
0
            || era.0 == tinystr!(16, "islamic")
687
0
            || era.0 == tinystr!(16, "ah")
688
        {
689
0
            year
690
        } else {
691
0
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
692
        };
693
694
0
        let month = if let Some((ordinal, false)) = month_code.parsed() {
695
0
            ordinal
696
        } else {
697
0
            return Err(CalendarError::UnknownMonthCode(
698
0
                month_code.0,
699
0
                self.debug_name(),
700
0
            ));
701
        };
702
0
        ArithmeticDate::new_from_ordinals_with_info(
703
0
            year,
704
0
            month,
705
0
            day,
706
0
            self.precomputed_data().load_or_compute_info(year),
707
        )
708
0
        .map(IslamicUmmAlQuraDateInner)
709
0
    }
710
711
0
    fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
712
0
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());
713
714
0
        let (year_info, y, m, d) = self
715
0
            .precomputed_data()
716
0
            .load_or_compute_info_for_iso(fixed_iso);
717
0
        IslamicUmmAlQuraDateInner(ArithmeticDate::new_unchecked_with_info(y, m, d, year_info))
718
0
    }
719
720
0
    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
721
0
        let fixed =
722
0
            date.0
723
0
                .year_info
724
0
                .rd_for::<SaudiIslamicMarker>(date.0.year, date.0.month, date.0.day);
725
0
        Iso::iso_from_fixed(fixed)
726
0
    }
727
728
0
    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
729
0
        date.0.months_in_year()
730
0
    }
731
732
0
    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
733
0
        date.0.days_in_year()
734
0
    }
735
736
0
    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
737
0
        date.0.days_in_month()
738
0
    }
739
740
0
    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
741
0
        date.0.offset_date(offset, &self.precomputed_data())
742
0
    }
743
744
0
    fn until(
745
0
        &self,
746
0
        date1: &Self::DateInner,
747
0
        date2: &Self::DateInner,
748
0
        _calendar2: &Self,
749
0
        _largest_unit: DateDurationUnit,
750
0
        _smallest_unit: DateDurationUnit,
751
0
    ) -> DateDuration<Self> {
752
0
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
753
0
    }
754
755
0
    fn debug_name(&self) -> &'static str {
756
0
        Self::DEBUG_NAME
757
0
    }
758
759
0
    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
760
0
        Self::year_as_islamic(date.0.year)
761
0
    }
762
763
0
    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
764
0
        Self::is_leap_year(date.0.year, date.0.year_info)
765
0
    }
766
767
0
    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
768
0
        date.0.month()
769
0
    }
770
771
0
    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
772
0
        date.0.day_of_month()
773
0
    }
774
775
0
    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
776
0
        let prev_year = date.0.year.saturating_sub(1);
777
0
        let next_year = date.0.year.saturating_add(1);
778
0
        types::DayOfYearInfo {
779
0
            day_of_year: date.0.day_of_year(),
780
0
            days_in_year: date.0.days_in_year(),
781
0
            prev_year: Self::year_as_islamic(prev_year),
782
0
            days_in_prev_year: date.0.year_info.days_in_prev_year(),
783
0
            next_year: Self::year_as_islamic(next_year),
784
0
        }
785
0
    }
786
787
0
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
788
0
        Some(AnyCalendarKind::IslamicUmmAlQura)
789
0
    }
790
}
791
792
impl IslamicUmmAlQura {
793
0
    fn precomputed_data(&self) -> IslamicPrecomputedData<SaudiIslamicMarker> {
794
0
        IslamicPrecomputedData::new(self.data.as_ref().map(|x| x.get()))
795
0
    }
796
797
0
    fn year_as_islamic(year: i32) -> types::FormattableYear {
798
0
        types::FormattableYear {
799
0
            era: types::Era(tinystr!(16, "islamic")),
800
0
            number: year,
801
0
            cyclic: None,
802
0
            related_iso: None,
803
0
        }
804
0
    }
805
    pub(crate) const DEBUG_NAME: &'static str = "Islamic (Umm al-Qura)";
806
}
807
808
impl<A: AsCalendar<Calendar = IslamicUmmAlQura>> Date<A> {
809
    /// Construct new Islamic Umm al-Qura Date.
810
    ///
811
    /// Has no negative years, only era is the AH.
812
    ///
813
    /// ```rust
814
    /// use icu::calendar::islamic::IslamicUmmAlQura;
815
    /// use icu::calendar::Date;
816
    ///
817
    /// let islamic = IslamicUmmAlQura::new_always_calculating();
818
    ///
819
    /// let date_islamic = Date::try_new_ummalqura_date(1392, 4, 25, islamic)
820
    ///     .expect("Failed to initialize Islamic Date instance.");
821
    ///
822
    /// assert_eq!(date_islamic.year().number, 1392);
823
    /// assert_eq!(date_islamic.month().ordinal, 4);
824
    /// assert_eq!(date_islamic.day_of_month().0, 25);
825
    /// ```
826
0
    pub fn try_new_ummalqura_date(
827
0
        year: i32,
828
0
        month: u8,
829
0
        day: u8,
830
0
        calendar: A,
831
0
    ) -> Result<Date<A>, CalendarError> {
832
0
        let year_info = calendar
833
0
            .as_calendar()
834
0
            .precomputed_data()
835
0
            .load_or_compute_info(year);
836
0
        ArithmeticDate::new_from_ordinals_with_info(year, month, day, year_info)
837
0
            .map(IslamicUmmAlQuraDateInner)
838
0
            .map(|inner| Date::from_raw(inner, calendar))
839
0
    }
840
}
841
842
impl<A: AsCalendar<Calendar = IslamicUmmAlQura>> DateTime<A> {
843
    /// Construct a new Islamic Umm al-Qura datetime from integers.
844
    ///
845
    /// ```rust
846
    /// use icu::calendar::islamic::IslamicUmmAlQura;
847
    /// use icu::calendar::DateTime;
848
    ///
849
    /// let islamic = IslamicUmmAlQura::new_always_calculating();
850
    ///
851
    /// let datetime_islamic =
852
    ///     DateTime::try_new_ummalqura_datetime(474, 10, 11, 13, 1, 0, islamic)
853
    ///         .expect("Failed to initialize Islamic DateTime instance.");
854
    ///
855
    /// assert_eq!(datetime_islamic.date.year().number, 474);
856
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
857
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
858
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
859
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
860
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
861
    /// ```
862
0
    pub fn try_new_ummalqura_datetime(
863
0
        year: i32,
864
0
        month: u8,
865
0
        day: u8,
866
0
        hour: u8,
867
0
        minute: u8,
868
0
        second: u8,
869
0
        calendar: A,
870
0
    ) -> Result<DateTime<A>, CalendarError> {
871
        Ok(DateTime {
872
0
            date: Date::try_new_ummalqura_date(year, month, day, calendar)?,
873
0
            time: Time::try_new(hour, minute, second, 0)?,
874
        })
875
0
    }
876
}
877
878
/// The inner date type used for representing [`Date`]s of [`IslamicCivil`]. See [`Date`] and [`IslamicCivil`] for more details.
879
880
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
881
pub struct IslamicCivilDateInner(ArithmeticDate<IslamicCivil>);
882
883
impl CalendarArithmetic for IslamicCivil {
884
    type YearInfo = ();
885
886
0
    fn month_days(year: i32, month: u8, _data: ()) -> u8 {
887
0
        match month {
888
0
            1 | 3 | 5 | 7 | 9 | 11 => 30,
889
0
            2 | 4 | 6 | 8 | 10 => 29,
890
0
            12 if Self::is_leap_year(year, ()) => 30,
891
0
            12 => 29,
892
0
            _ => 0,
893
        }
894
0
    }
895
896
0
    fn months_for_every_year(_year: i32, _data: ()) -> u8 {
897
0
        12
898
0
    }
899
900
0
    fn days_in_provided_year(year: i32, _data: ()) -> u16 {
901
0
        if Self::is_leap_year(year, ()) {
902
0
            IslamicYearInfo::LONG_YEAR_LEN
903
        } else {
904
0
            IslamicYearInfo::SHORT_YEAR_LEN
905
        }
906
0
    }
907
908
0
    fn is_leap_year(year: i32, _data: ()) -> bool {
909
0
        (14 + 11 * year).rem_euclid(30) < 11
910
0
    }
911
912
0
    fn last_month_day_in_year(year: i32, _data: ()) -> (u8, u8) {
913
0
        if Self::is_leap_year(year, ()) {
914
0
            (12, 30)
915
        } else {
916
0
            (12, 29)
917
        }
918
0
    }
919
}
920
921
impl Calendar for IslamicCivil {
922
    type DateInner = IslamicCivilDateInner;
923
924
0
    fn date_from_codes(
925
0
        &self,
926
0
        era: types::Era,
927
0
        year: i32,
928
0
        month_code: types::MonthCode,
929
0
        day: u8,
930
0
    ) -> Result<Self::DateInner, CalendarError> {
931
0
        let year = if era.0 == tinystr!(16, "islamic-civil")
932
0
            || era.0 == tinystr!(16, "islamicc")
933
0
            || era.0 == tinystr!(16, "islamic")
934
0
            || era.0 == tinystr!(16, "ah")
935
        {
936
            // TODO: Check name and alias
937
0
            year
938
        } else {
939
0
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
940
        };
941
942
0
        ArithmeticDate::new_from_codes(self, year, month_code, day).map(IslamicCivilDateInner)
943
0
    }
944
945
0
    fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
946
0
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());
947
0
        Self::islamic_from_fixed(fixed_iso).inner
948
0
    }
949
950
0
    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
951
0
        let fixed_islamic = Self::fixed_from_islamic(*date);
952
0
        Iso::iso_from_fixed(fixed_islamic)
953
0
    }
954
955
0
    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
956
0
        date.0.months_in_year()
957
0
    }
958
959
0
    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
960
0
        date.0.days_in_year()
961
0
    }
962
963
0
    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
964
0
        date.0.days_in_month()
965
0
    }
966
967
0
    fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
968
0
        Iso.day_of_week(self.date_to_iso(date).inner())
969
0
    }
970
971
0
    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
972
0
        date.0.offset_date(offset, &())
973
0
    }
974
975
0
    fn until(
976
0
        &self,
977
0
        date1: &Self::DateInner,
978
0
        date2: &Self::DateInner,
979
0
        _calendar2: &Self,
980
0
        _largest_unit: DateDurationUnit,
981
0
        _smallest_unit: DateDurationUnit,
982
0
    ) -> DateDuration<Self> {
983
0
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
984
0
    }
985
986
0
    fn debug_name(&self) -> &'static str {
987
0
        "Islamic (civil)"
988
0
    }
989
990
0
    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
991
0
        Self::year_as_islamic(date.0.year)
992
0
    }
993
994
0
    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
995
0
        Self::is_leap_year(date.0.year, ())
996
0
    }
997
998
0
    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
999
0
        date.0.month()
1000
0
    }
1001
1002
0
    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
1003
0
        date.0.day_of_month()
1004
0
    }
1005
1006
0
    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
1007
0
        let prev_year = date.0.year.saturating_sub(1);
1008
0
        let next_year = date.0.year.saturating_add(1);
1009
0
        types::DayOfYearInfo {
1010
0
            day_of_year: date.0.day_of_year(),
1011
0
            days_in_year: date.0.days_in_year(),
1012
0
            prev_year: Self::year_as_islamic(prev_year),
1013
0
            days_in_prev_year: Self::days_in_provided_year(prev_year, ()),
1014
0
            next_year: Self::year_as_islamic(next_year),
1015
0
        }
1016
0
    }
1017
0
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
1018
0
        Some(AnyCalendarKind::IslamicCivil)
1019
0
    }
1020
}
1021
1022
impl IslamicCivil {
1023
0
    fn fixed_from_islamic(i_date: IslamicCivilDateInner) -> RataDie {
1024
0
        calendrical_calculations::islamic::fixed_from_islamic_civil(
1025
0
            i_date.0.year,
1026
0
            i_date.0.month,
1027
0
            i_date.0.day,
1028
        )
1029
0
    }
1030
1031
0
    fn islamic_from_fixed(date: RataDie) -> Date<IslamicCivil> {
1032
0
        let (y, m, d) = calendrical_calculations::islamic::islamic_civil_from_fixed(date);
1033
1034
0
        debug_assert!(
1035
0
            Date::try_new_islamic_civil_date_with_calendar(y, m, d, IslamicCivil).is_ok()
1036
        );
1037
0
        Date::from_raw(
1038
0
            IslamicCivilDateInner(ArithmeticDate::new_unchecked(y, m, d)),
1039
0
            IslamicCivil,
1040
        )
1041
0
    }
1042
1043
0
    fn year_as_islamic(year: i32) -> types::FormattableYear {
1044
0
        types::FormattableYear {
1045
0
            era: types::Era(tinystr!(16, "islamic")),
1046
0
            number: year,
1047
0
            cyclic: None,
1048
0
            related_iso: None,
1049
0
        }
1050
0
    }
1051
}
1052
1053
impl<A: AsCalendar<Calendar = IslamicCivil>> Date<A> {
1054
    /// Construct new Civil Islamic Date.
1055
    ///
1056
    /// Has no negative years, only era is the AH.
1057
    ///
1058
    /// ```rust
1059
    /// use icu::calendar::islamic::IslamicCivil;
1060
    /// use icu::calendar::Date;
1061
    ///
1062
    /// let islamic = IslamicCivil::new_always_calculating();
1063
    ///
1064
    /// let date_islamic =
1065
    ///     Date::try_new_islamic_civil_date_with_calendar(1392, 4, 25, islamic)
1066
    ///         .expect("Failed to initialize Islamic Date instance.");
1067
    ///
1068
    /// assert_eq!(date_islamic.year().number, 1392);
1069
    /// assert_eq!(date_islamic.month().ordinal, 4);
1070
    /// assert_eq!(date_islamic.day_of_month().0, 25);
1071
    /// ```
1072
0
    pub fn try_new_islamic_civil_date_with_calendar(
1073
0
        year: i32,
1074
0
        month: u8,
1075
0
        day: u8,
1076
0
        calendar: A,
1077
0
    ) -> Result<Date<A>, CalendarError> {
1078
0
        ArithmeticDate::new_from_ordinals(year, month, day)
1079
0
            .map(IslamicCivilDateInner)
1080
0
            .map(|inner| Date::from_raw(inner, calendar))
1081
0
    }
1082
}
1083
1084
impl<A: AsCalendar<Calendar = IslamicCivil>> DateTime<A> {
1085
    /// Construct a new Civil Islamic datetime from integers.
1086
    ///
1087
    /// ```rust
1088
    /// use icu::calendar::islamic::IslamicCivil;
1089
    /// use icu::calendar::DateTime;
1090
    ///
1091
    /// let islamic = IslamicCivil::new_always_calculating();
1092
    ///
1093
    /// let datetime_islamic =
1094
    ///     DateTime::try_new_islamic_civil_datetime_with_calendar(
1095
    ///         474, 10, 11, 13, 1, 0, islamic,
1096
    ///     )
1097
    ///     .expect("Failed to initialize Islamic DateTime instance.");
1098
    ///
1099
    /// assert_eq!(datetime_islamic.date.year().number, 474);
1100
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
1101
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
1102
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
1103
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
1104
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
1105
    /// ```
1106
0
    pub fn try_new_islamic_civil_datetime_with_calendar(
1107
0
        year: i32,
1108
0
        month: u8,
1109
0
        day: u8,
1110
0
        hour: u8,
1111
0
        minute: u8,
1112
0
        second: u8,
1113
0
        calendar: A,
1114
0
    ) -> Result<DateTime<A>, CalendarError> {
1115
        Ok(DateTime {
1116
0
            date: Date::try_new_islamic_civil_date_with_calendar(year, month, day, calendar)?,
1117
0
            time: Time::try_new(hour, minute, second, 0)?,
1118
        })
1119
0
    }
1120
}
1121
1122
/// The inner date type used for representing [`Date`]s of [`IslamicTabular`]. See [`Date`] and [`IslamicTabular`] for more details.
1123
1124
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
1125
pub struct IslamicTabularDateInner(ArithmeticDate<IslamicTabular>);
1126
1127
impl CalendarArithmetic for IslamicTabular {
1128
    type YearInfo = ();
1129
1130
0
    fn month_days(year: i32, month: u8, _data: ()) -> u8 {
1131
0
        match month {
1132
0
            1 | 3 | 5 | 7 | 9 | 11 => 30,
1133
0
            2 | 4 | 6 | 8 | 10 => 29,
1134
0
            12 if Self::is_leap_year(year, ()) => 30,
1135
0
            12 => 29,
1136
0
            _ => 0,
1137
        }
1138
0
    }
1139
1140
0
    fn months_for_every_year(_year: i32, _data: ()) -> u8 {
1141
0
        12
1142
0
    }
1143
1144
0
    fn days_in_provided_year(year: i32, _data: ()) -> u16 {
1145
0
        if Self::is_leap_year(year, ()) {
1146
0
            IslamicYearInfo::LONG_YEAR_LEN
1147
        } else {
1148
0
            IslamicYearInfo::SHORT_YEAR_LEN
1149
        }
1150
0
    }
1151
1152
0
    fn is_leap_year(year: i32, _data: ()) -> bool {
1153
0
        (14 + 11 * year).rem_euclid(30) < 11
1154
0
    }
1155
1156
0
    fn last_month_day_in_year(year: i32, _data: ()) -> (u8, u8) {
1157
0
        if Self::is_leap_year(year, ()) {
1158
0
            (12, 30)
1159
        } else {
1160
0
            (12, 29)
1161
        }
1162
0
    }
1163
}
1164
1165
impl Calendar for IslamicTabular {
1166
    type DateInner = IslamicTabularDateInner;
1167
1168
0
    fn date_from_codes(
1169
0
        &self,
1170
0
        era: types::Era,
1171
0
        year: i32,
1172
0
        month_code: types::MonthCode,
1173
0
        day: u8,
1174
0
    ) -> Result<Self::DateInner, CalendarError> {
1175
0
        let year = if era.0 == tinystr!(16, "islamic-tbla")
1176
0
            || era.0 == tinystr!(16, "islamic")
1177
0
            || era.0 == tinystr!(16, "ah")
1178
        {
1179
0
            year
1180
        } else {
1181
0
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
1182
        };
1183
1184
0
        ArithmeticDate::new_from_codes(self, year, month_code, day).map(IslamicTabularDateInner)
1185
0
    }
1186
1187
0
    fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
1188
0
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());
1189
0
        Self::islamic_from_fixed(fixed_iso).inner
1190
0
    }
1191
1192
0
    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
1193
0
        let fixed_islamic = Self::fixed_from_islamic(*date);
1194
0
        Iso::iso_from_fixed(fixed_islamic)
1195
0
    }
1196
1197
0
    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
1198
0
        date.0.months_in_year()
1199
0
    }
1200
1201
0
    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
1202
0
        date.0.days_in_year()
1203
0
    }
1204
1205
0
    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
1206
0
        date.0.days_in_month()
1207
0
    }
1208
1209
0
    fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
1210
0
        Iso.day_of_week(self.date_to_iso(date).inner())
1211
0
    }
1212
1213
0
    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
1214
0
        date.0.offset_date(offset, &())
1215
0
    }
1216
1217
0
    fn until(
1218
0
        &self,
1219
0
        date1: &Self::DateInner,
1220
0
        date2: &Self::DateInner,
1221
0
        _calendar2: &Self,
1222
0
        _largest_unit: DateDurationUnit,
1223
0
        _smallest_unit: DateDurationUnit,
1224
0
    ) -> DateDuration<Self> {
1225
0
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
1226
0
    }
1227
1228
0
    fn debug_name(&self) -> &'static str {
1229
0
        "Islamic (tabular)"
1230
0
    }
1231
1232
0
    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
1233
0
        Self::year_as_islamic(date.0.year)
1234
0
    }
1235
1236
0
    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
1237
0
        Self::is_leap_year(date.0.year, ())
1238
0
    }
1239
1240
0
    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
1241
0
        date.0.month()
1242
0
    }
1243
1244
0
    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
1245
0
        date.0.day_of_month()
1246
0
    }
1247
1248
0
    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
1249
0
        let prev_year = date.0.year.saturating_sub(1);
1250
0
        let next_year = date.0.year.saturating_add(1);
1251
0
        types::DayOfYearInfo {
1252
0
            day_of_year: date.0.day_of_year(),
1253
0
            days_in_year: date.0.days_in_year(),
1254
0
            prev_year: Self::year_as_islamic(prev_year),
1255
0
            days_in_prev_year: Self::days_in_provided_year(prev_year, ()),
1256
0
            next_year: Self::year_as_islamic(next_year),
1257
0
        }
1258
0
    }
1259
0
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
1260
0
        Some(AnyCalendarKind::IslamicTabular)
1261
0
    }
1262
}
1263
1264
impl IslamicTabular {
1265
0
    fn fixed_from_islamic(i_date: IslamicTabularDateInner) -> RataDie {
1266
0
        calendrical_calculations::islamic::fixed_from_islamic_tabular(
1267
0
            i_date.0.year,
1268
0
            i_date.0.month,
1269
0
            i_date.0.day,
1270
        )
1271
0
    }
1272
1273
0
    fn islamic_from_fixed(date: RataDie) -> Date<IslamicTabular> {
1274
0
        let (y, m, d) = calendrical_calculations::islamic::islamic_tabular_from_fixed(date);
1275
1276
0
        debug_assert!(
1277
0
            Date::try_new_islamic_civil_date_with_calendar(y, m, d, IslamicCivil).is_ok()
1278
        );
1279
0
        Date::from_raw(
1280
0
            IslamicTabularDateInner(ArithmeticDate::new_unchecked(y, m, d)),
1281
0
            IslamicTabular,
1282
        )
1283
0
    }
1284
1285
0
    fn year_as_islamic(year: i32) -> types::FormattableYear {
1286
0
        types::FormattableYear {
1287
0
            era: types::Era(tinystr!(16, "islamic")),
1288
0
            number: year,
1289
0
            cyclic: None,
1290
0
            related_iso: None,
1291
0
        }
1292
0
    }
1293
}
1294
1295
impl<A: AsCalendar<Calendar = IslamicTabular>> Date<A> {
1296
    /// Construct new Tabular Islamic Date.
1297
    ///
1298
    /// Has no negative years, only era is the AH.
1299
    ///
1300
    /// ```rust
1301
    /// use icu::calendar::islamic::IslamicTabular;
1302
    /// use icu::calendar::Date;
1303
    ///
1304
    /// let islamic = IslamicTabular::new_always_calculating();
1305
    ///
1306
    /// let date_islamic =
1307
    ///     Date::try_new_islamic_tabular_date_with_calendar(1392, 4, 25, islamic)
1308
    ///         .expect("Failed to initialize Islamic Date instance.");
1309
    ///
1310
    /// assert_eq!(date_islamic.year().number, 1392);
1311
    /// assert_eq!(date_islamic.month().ordinal, 4);
1312
    /// assert_eq!(date_islamic.day_of_month().0, 25);
1313
    /// ```
1314
0
    pub fn try_new_islamic_tabular_date_with_calendar(
1315
0
        year: i32,
1316
0
        month: u8,
1317
0
        day: u8,
1318
0
        calendar: A,
1319
0
    ) -> Result<Date<A>, CalendarError> {
1320
0
        ArithmeticDate::new_from_ordinals(year, month, day)
1321
0
            .map(IslamicTabularDateInner)
1322
0
            .map(|inner| Date::from_raw(inner, calendar))
1323
0
    }
1324
}
1325
1326
impl<A: AsCalendar<Calendar = IslamicTabular>> DateTime<A> {
1327
    /// Construct a new Tabular Islamic datetime from integers.
1328
    ///
1329
    /// ```rust
1330
    /// use icu::calendar::islamic::IslamicTabular;
1331
    /// use icu::calendar::DateTime;
1332
    ///
1333
    /// let islamic = IslamicTabular::new_always_calculating();
1334
    ///
1335
    /// let datetime_islamic =
1336
    ///     DateTime::try_new_islamic_tabular_datetime_with_calendar(
1337
    ///         474, 10, 11, 13, 1, 0, islamic,
1338
    ///     )
1339
    ///     .expect("Failed to initialize Islamic DateTime instance.");
1340
    ///
1341
    /// assert_eq!(datetime_islamic.date.year().number, 474);
1342
    /// assert_eq!(datetime_islamic.date.month().ordinal, 10);
1343
    /// assert_eq!(datetime_islamic.date.day_of_month().0, 11);
1344
    /// assert_eq!(datetime_islamic.time.hour.number(), 13);
1345
    /// assert_eq!(datetime_islamic.time.minute.number(), 1);
1346
    /// assert_eq!(datetime_islamic.time.second.number(), 0);
1347
    /// ```
1348
0
    pub fn try_new_islamic_tabular_datetime_with_calendar(
1349
0
        year: i32,
1350
0
        month: u8,
1351
0
        day: u8,
1352
0
        hour: u8,
1353
0
        minute: u8,
1354
0
        second: u8,
1355
0
        calendar: A,
1356
0
    ) -> Result<DateTime<A>, CalendarError> {
1357
        Ok(DateTime {
1358
0
            date: Date::try_new_islamic_tabular_date_with_calendar(year, month, day, calendar)?,
1359
0
            time: Time::try_new(hour, minute, second, 0)?,
1360
        })
1361
0
    }
1362
}
1363
1364
#[cfg(test)]
1365
mod test {
1366
    use super::*;
1367
    use crate::Ref;
1368
1369
    const START_YEAR: i32 = -1245;
1370
    const END_YEAR: i32 = 1518;
1371
1372
    #[derive(Debug)]
1373
    struct DateCase {
1374
        year: i32,
1375
        month: u8,
1376
        day: u8,
1377
    }
1378
1379
    static TEST_FIXED_DATE: [i64; 33] = [
1380
        -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
1381
        470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
1382
        664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
1383
    ];
1384
    // Removed: 601716 and 727274 fixed dates
1385
    static TEST_FIXED_DATE_UMMALQURA: [i64; 31] = [
1386
        -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
1387
        470160, 473837, 507850, 524156, 544676, 567118, 569477, 613424, 626596, 645554, 664224,
1388
        671401, 694799, 704424, 708842, 709409, 709580, 728714, 744313, 764652,
1389
    ];
1390
1391
    static UMMALQURA_DATE_EXPECTED: [DateCase; 31] = [
1392
        DateCase {
1393
            year: -1245,
1394
            month: 12,
1395
            day: 11,
1396
        },
1397
        DateCase {
1398
            year: -813,
1399
            month: 2,
1400
            day: 26,
1401
        },
1402
        DateCase {
1403
            year: -568,
1404
            month: 4,
1405
            day: 3,
1406
        },
1407
        DateCase {
1408
            year: -501,
1409
            month: 4,
1410
            day: 8,
1411
        },
1412
        DateCase {
1413
            year: -157,
1414
            month: 10,
1415
            day: 18,
1416
        },
1417
        DateCase {
1418
            year: -47,
1419
            month: 6,
1420
            day: 4,
1421
        },
1422
        DateCase {
1423
            year: 75,
1424
            month: 7,
1425
            day: 14,
1426
        },
1427
        DateCase {
1428
            year: 403,
1429
            month: 10,
1430
            day: 6,
1431
        },
1432
        DateCase {
1433
            year: 489,
1434
            month: 5,
1435
            day: 23,
1436
        },
1437
        DateCase {
1438
            year: 586,
1439
            month: 2,
1440
            day: 8,
1441
        },
1442
        DateCase {
1443
            year: 637,
1444
            month: 8,
1445
            day: 8,
1446
        },
1447
        DateCase {
1448
            year: 687,
1449
            month: 2,
1450
            day: 22,
1451
        },
1452
        DateCase {
1453
            year: 697,
1454
            month: 7,
1455
            day: 8,
1456
        },
1457
        DateCase {
1458
            year: 793,
1459
            month: 7,
1460
            day: 1,
1461
        },
1462
        DateCase {
1463
            year: 839,
1464
            month: 7,
1465
            day: 7,
1466
        },
1467
        DateCase {
1468
            year: 897,
1469
            month: 6,
1470
            day: 3,
1471
        },
1472
        DateCase {
1473
            year: 960,
1474
            month: 10,
1475
            day: 1,
1476
        },
1477
        DateCase {
1478
            year: 967,
1479
            month: 5,
1480
            day: 28,
1481
        },
1482
        DateCase {
1483
            year: 1091,
1484
            month: 6,
1485
            day: 4,
1486
        },
1487
        DateCase {
1488
            year: 1128,
1489
            month: 8,
1490
            day: 5,
1491
        },
1492
        DateCase {
1493
            year: 1182,
1494
            month: 2,
1495
            day: 4,
1496
        },
1497
        DateCase {
1498
            year: 1234,
1499
            month: 10,
1500
            day: 11,
1501
        },
1502
        DateCase {
1503
            year: 1255,
1504
            month: 1,
1505
            day: 11,
1506
        },
1507
        DateCase {
1508
            year: 1321,
1509
            month: 1,
1510
            day: 21,
1511
        },
1512
        DateCase {
1513
            year: 1348,
1514
            month: 3,
1515
            day: 20,
1516
        },
1517
        DateCase {
1518
            year: 1360,
1519
            month: 9,
1520
            day: 8,
1521
        },
1522
        DateCase {
1523
            year: 1362,
1524
            month: 4,
1525
            day: 14,
1526
        },
1527
        DateCase {
1528
            year: 1362,
1529
            month: 10,
1530
            day: 8,
1531
        },
1532
        DateCase {
1533
            year: 1416,
1534
            month: 10,
1535
            day: 6,
1536
        },
1537
        DateCase {
1538
            year: 1460,
1539
            month: 10,
1540
            day: 13,
1541
        },
1542
        DateCase {
1543
            year: 1518,
1544
            month: 3,
1545
            day: 6,
1546
        },
1547
    ];
1548
1549
    static OBSERVATIONAL_CASES: [DateCase; 33] = [
1550
        DateCase {
1551
            year: -1245,
1552
            month: 12,
1553
            day: 11,
1554
        },
1555
        DateCase {
1556
            year: -813,
1557
            month: 2,
1558
            day: 25,
1559
        },
1560
        DateCase {
1561
            year: -568,
1562
            month: 4,
1563
            day: 2,
1564
        },
1565
        DateCase {
1566
            year: -501,
1567
            month: 4,
1568
            day: 7,
1569
        },
1570
        DateCase {
1571
            year: -157,
1572
            month: 10,
1573
            day: 18,
1574
        },
1575
        DateCase {
1576
            year: -47,
1577
            month: 6,
1578
            day: 3,
1579
        },
1580
        DateCase {
1581
            year: 75,
1582
            month: 7,
1583
            day: 13,
1584
        },
1585
        DateCase {
1586
            year: 403,
1587
            month: 10,
1588
            day: 5,
1589
        },
1590
        DateCase {
1591
            year: 489,
1592
            month: 5,
1593
            day: 22,
1594
        },
1595
        DateCase {
1596
            year: 586,
1597
            month: 2,
1598
            day: 7,
1599
        },
1600
        DateCase {
1601
            year: 637,
1602
            month: 8,
1603
            day: 7,
1604
        },
1605
        DateCase {
1606
            year: 687,
1607
            month: 2,
1608
            day: 21,
1609
        },
1610
        DateCase {
1611
            year: 697,
1612
            month: 7,
1613
            day: 7,
1614
        },
1615
        DateCase {
1616
            year: 793,
1617
            month: 6,
1618
            day: 30,
1619
        },
1620
        DateCase {
1621
            year: 839,
1622
            month: 7,
1623
            day: 6,
1624
        },
1625
        DateCase {
1626
            year: 897,
1627
            month: 6,
1628
            day: 2,
1629
        },
1630
        DateCase {
1631
            year: 960,
1632
            month: 9,
1633
            day: 30,
1634
        },
1635
        DateCase {
1636
            year: 967,
1637
            month: 5,
1638
            day: 27,
1639
        },
1640
        DateCase {
1641
            year: 1058,
1642
            month: 5,
1643
            day: 18,
1644
        },
1645
        DateCase {
1646
            year: 1091,
1647
            month: 6,
1648
            day: 3,
1649
        },
1650
        DateCase {
1651
            year: 1128,
1652
            month: 8,
1653
            day: 4,
1654
        },
1655
        DateCase {
1656
            year: 1182,
1657
            month: 2,
1658
            day: 4,
1659
        },
1660
        DateCase {
1661
            year: 1234,
1662
            month: 10,
1663
            day: 10,
1664
        },
1665
        DateCase {
1666
            year: 1255,
1667
            month: 1,
1668
            day: 11,
1669
        },
1670
        DateCase {
1671
            year: 1321,
1672
            month: 1,
1673
            day: 20,
1674
        },
1675
        DateCase {
1676
            year: 1348,
1677
            month: 3,
1678
            day: 19,
1679
        },
1680
        DateCase {
1681
            year: 1360,
1682
            month: 9,
1683
            day: 7,
1684
        },
1685
        DateCase {
1686
            year: 1362,
1687
            month: 4,
1688
            day: 14,
1689
        },
1690
        DateCase {
1691
            year: 1362,
1692
            month: 10,
1693
            day: 7,
1694
        },
1695
        DateCase {
1696
            year: 1412,
1697
            month: 9,
1698
            day: 12,
1699
        },
1700
        DateCase {
1701
            year: 1416,
1702
            month: 10,
1703
            day: 5,
1704
        },
1705
        DateCase {
1706
            year: 1460,
1707
            month: 10,
1708
            day: 12,
1709
        },
1710
        DateCase {
1711
            year: 1518,
1712
            month: 3,
1713
            day: 5,
1714
        },
1715
    ];
1716
1717
    static ARITHMETIC_CASES: [DateCase; 33] = [
1718
        DateCase {
1719
            year: -1245,
1720
            month: 12,
1721
            day: 9,
1722
        },
1723
        DateCase {
1724
            year: -813,
1725
            month: 2,
1726
            day: 23,
1727
        },
1728
        DateCase {
1729
            year: -568,
1730
            month: 4,
1731
            day: 1,
1732
        },
1733
        DateCase {
1734
            year: -501,
1735
            month: 4,
1736
            day: 6,
1737
        },
1738
        DateCase {
1739
            year: -157,
1740
            month: 10,
1741
            day: 17,
1742
        },
1743
        DateCase {
1744
            year: -47,
1745
            month: 6,
1746
            day: 3,
1747
        },
1748
        DateCase {
1749
            year: 75,
1750
            month: 7,
1751
            day: 13,
1752
        },
1753
        DateCase {
1754
            year: 403,
1755
            month: 10,
1756
            day: 5,
1757
        },
1758
        DateCase {
1759
            year: 489,
1760
            month: 5,
1761
            day: 22,
1762
        },
1763
        DateCase {
1764
            year: 586,
1765
            month: 2,
1766
            day: 7,
1767
        },
1768
        DateCase {
1769
            year: 637,
1770
            month: 8,
1771
            day: 7,
1772
        },
1773
        DateCase {
1774
            year: 687,
1775
            month: 2,
1776
            day: 20,
1777
        },
1778
        DateCase {
1779
            year: 697,
1780
            month: 7,
1781
            day: 7,
1782
        },
1783
        DateCase {
1784
            year: 793,
1785
            month: 7,
1786
            day: 1,
1787
        },
1788
        DateCase {
1789
            year: 839,
1790
            month: 7,
1791
            day: 6,
1792
        },
1793
        DateCase {
1794
            year: 897,
1795
            month: 6,
1796
            day: 1,
1797
        },
1798
        DateCase {
1799
            year: 960,
1800
            month: 9,
1801
            day: 30,
1802
        },
1803
        DateCase {
1804
            year: 967,
1805
            month: 5,
1806
            day: 27,
1807
        },
1808
        DateCase {
1809
            year: 1058,
1810
            month: 5,
1811
            day: 18,
1812
        },
1813
        DateCase {
1814
            year: 1091,
1815
            month: 6,
1816
            day: 2,
1817
        },
1818
        DateCase {
1819
            year: 1128,
1820
            month: 8,
1821
            day: 4,
1822
        },
1823
        DateCase {
1824
            year: 1182,
1825
            month: 2,
1826
            day: 3,
1827
        },
1828
        DateCase {
1829
            year: 1234,
1830
            month: 10,
1831
            day: 10,
1832
        },
1833
        DateCase {
1834
            year: 1255,
1835
            month: 1,
1836
            day: 11,
1837
        },
1838
        DateCase {
1839
            year: 1321,
1840
            month: 1,
1841
            day: 21,
1842
        },
1843
        DateCase {
1844
            year: 1348,
1845
            month: 3,
1846
            day: 19,
1847
        },
1848
        DateCase {
1849
            year: 1360,
1850
            month: 9,
1851
            day: 8,
1852
        },
1853
        DateCase {
1854
            year: 1362,
1855
            month: 4,
1856
            day: 13,
1857
        },
1858
        DateCase {
1859
            year: 1362,
1860
            month: 10,
1861
            day: 7,
1862
        },
1863
        DateCase {
1864
            year: 1412,
1865
            month: 9,
1866
            day: 13,
1867
        },
1868
        DateCase {
1869
            year: 1416,
1870
            month: 10,
1871
            day: 5,
1872
        },
1873
        DateCase {
1874
            year: 1460,
1875
            month: 10,
1876
            day: 12,
1877
        },
1878
        DateCase {
1879
            year: 1518,
1880
            month: 3,
1881
            day: 5,
1882
        },
1883
    ];
1884
1885
    static TABULAR_CASES: [DateCase; 33] = [
1886
        DateCase {
1887
            year: -1245,
1888
            month: 12,
1889
            day: 10,
1890
        },
1891
        DateCase {
1892
            year: -813,
1893
            month: 2,
1894
            day: 24,
1895
        },
1896
        DateCase {
1897
            year: -568,
1898
            month: 4,
1899
            day: 2,
1900
        },
1901
        DateCase {
1902
            year: -501,
1903
            month: 4,
1904
            day: 7,
1905
        },
1906
        DateCase {
1907
            year: -157,
1908
            month: 10,
1909
            day: 18,
1910
        },
1911
        DateCase {
1912
            year: -47,
1913
            month: 6,
1914
            day: 4,
1915
        },
1916
        DateCase {
1917
            year: 75,
1918
            month: 7,
1919
            day: 14,
1920
        },
1921
        DateCase {
1922
            year: 403,
1923
            month: 10,
1924
            day: 6,
1925
        },
1926
        DateCase {
1927
            year: 489,
1928
            month: 5,
1929
            day: 23,
1930
        },
1931
        DateCase {
1932
            year: 586,
1933
            month: 2,
1934
            day: 8,
1935
        },
1936
        DateCase {
1937
            year: 637,
1938
            month: 8,
1939
            day: 8,
1940
        },
1941
        DateCase {
1942
            year: 687,
1943
            month: 2,
1944
            day: 21,
1945
        },
1946
        DateCase {
1947
            year: 697,
1948
            month: 7,
1949
            day: 8,
1950
        },
1951
        DateCase {
1952
            year: 793,
1953
            month: 7,
1954
            day: 2,
1955
        },
1956
        DateCase {
1957
            year: 839,
1958
            month: 7,
1959
            day: 7,
1960
        },
1961
        DateCase {
1962
            year: 897,
1963
            month: 6,
1964
            day: 2,
1965
        },
1966
        DateCase {
1967
            year: 960,
1968
            month: 10,
1969
            day: 1,
1970
        },
1971
        DateCase {
1972
            year: 967,
1973
            month: 5,
1974
            day: 28,
1975
        },
1976
        DateCase {
1977
            year: 1058,
1978
            month: 5,
1979
            day: 19,
1980
        },
1981
        DateCase {
1982
            year: 1091,
1983
            month: 6,
1984
            day: 3,
1985
        },
1986
        DateCase {
1987
            year: 1128,
1988
            month: 8,
1989
            day: 5,
1990
        },
1991
        DateCase {
1992
            year: 1182,
1993
            month: 2,
1994
            day: 4,
1995
        },
1996
        DateCase {
1997
            year: 1234,
1998
            month: 10,
1999
            day: 11,
2000
        },
2001
        DateCase {
2002
            year: 1255,
2003
            month: 1,
2004
            day: 12,
2005
        },
2006
        DateCase {
2007
            year: 1321,
2008
            month: 1,
2009
            day: 22,
2010
        },
2011
        DateCase {
2012
            year: 1348,
2013
            month: 3,
2014
            day: 20,
2015
        },
2016
        DateCase {
2017
            year: 1360,
2018
            month: 9,
2019
            day: 9,
2020
        },
2021
        DateCase {
2022
            year: 1362,
2023
            month: 4,
2024
            day: 14,
2025
        },
2026
        DateCase {
2027
            year: 1362,
2028
            month: 10,
2029
            day: 8,
2030
        },
2031
        DateCase {
2032
            year: 1412,
2033
            month: 9,
2034
            day: 14,
2035
        },
2036
        DateCase {
2037
            year: 1416,
2038
            month: 10,
2039
            day: 6,
2040
        },
2041
        DateCase {
2042
            year: 1460,
2043
            month: 10,
2044
            day: 13,
2045
        },
2046
        DateCase {
2047
            year: 1518,
2048
            month: 3,
2049
            day: 6,
2050
        },
2051
    ];
2052
2053
    #[test]
2054
    fn test_observational_islamic_from_fixed() {
2055
        let calendar = IslamicObservational::new();
2056
        let calendar = Ref(&calendar);
2057
        for (case, f_date) in OBSERVATIONAL_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
2058
            let date =
2059
                Date::try_new_observational_islamic_date(case.year, case.month, case.day, calendar)
2060
                    .unwrap();
2061
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));
2062
2063
            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
2064
        }
2065
    }
2066
2067
    #[test]
2068
    fn test_fixed_from_observational_islamic() {
2069
        let calendar = IslamicObservational::new();
2070
        let calendar = Ref(&calendar);
2071
        for (case, f_date) in OBSERVATIONAL_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
2072
            let date =
2073
                Date::try_new_observational_islamic_date(case.year, case.month, case.day, calendar)
2074
                    .unwrap();
2075
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
2076
        }
2077
    }
2078
2079
    #[test]
2080
    fn test_fixed_from_islamic() {
2081
        let calendar = IslamicCivil::new();
2082
        let calendar = Ref(&calendar);
2083
        for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
2084
            let date = Date::try_new_islamic_civil_date_with_calendar(
2085
                case.year, case.month, case.day, calendar,
2086
            )
2087
            .unwrap();
2088
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
2089
        }
2090
    }
2091
2092
    #[test]
2093
    fn test_islamic_from_fixed() {
2094
        let calendar = IslamicCivil::new();
2095
        let calendar = Ref(&calendar);
2096
        for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
2097
            let date = Date::try_new_islamic_civil_date_with_calendar(
2098
                case.year, case.month, case.day, calendar,
2099
            )
2100
            .unwrap();
2101
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));
2102
2103
            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
2104
        }
2105
    }
2106
2107
    #[test]
2108
    fn test_fixed_from_islamic_tbla() {
2109
        let calendar = IslamicTabular::new();
2110
        let calendar = Ref(&calendar);
2111
        for (case, f_date) in TABULAR_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
2112
            let date = Date::try_new_islamic_tabular_date_with_calendar(
2113
                case.year, case.month, case.day, calendar,
2114
            )
2115
            .unwrap();
2116
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
2117
        }
2118
    }
2119
2120
    #[test]
2121
    fn test_islamic_tbla_from_fixed() {
2122
        let calendar = IslamicTabular::new();
2123
        let calendar = Ref(&calendar);
2124
        for (case, f_date) in TABULAR_CASES.iter().zip(TEST_FIXED_DATE.iter()) {
2125
            let date = Date::try_new_islamic_tabular_date_with_calendar(
2126
                case.year, case.month, case.day, calendar,
2127
            )
2128
            .unwrap();
2129
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));
2130
2131
            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
2132
        }
2133
    }
2134
2135
    #[test]
2136
    fn test_saudi_islamic_from_fixed() {
2137
        let calendar = IslamicUmmAlQura::new();
2138
        let calendar = Ref(&calendar);
2139
        for (case, f_date) in UMMALQURA_DATE_EXPECTED
2140
            .iter()
2141
            .zip(TEST_FIXED_DATE_UMMALQURA.iter())
2142
        {
2143
            let date =
2144
                Date::try_new_ummalqura_date(case.year, case.month, case.day, calendar).unwrap();
2145
            let iso = Iso::iso_from_fixed(RataDie::new(*f_date));
2146
2147
            assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
2148
        }
2149
    }
2150
2151
    #[test]
2152
    fn test_fixed_from_saudi_islamic() {
2153
        let calendar = IslamicUmmAlQura::new();
2154
        let calendar = Ref(&calendar);
2155
        for (case, f_date) in UMMALQURA_DATE_EXPECTED
2156
            .iter()
2157
            .zip(TEST_FIXED_DATE_UMMALQURA.iter())
2158
        {
2159
            let date =
2160
                Date::try_new_ummalqura_date(case.year, case.month, case.day, calendar).unwrap();
2161
            assert_eq!(date.to_fixed(), RataDie::new(*f_date), "{case:?}");
2162
        }
2163
    }
2164
2165
    #[ignore]
2166
    #[test]
2167
    fn test_days_in_provided_year_observational() {
2168
        let calendar = IslamicObservational::new();
2169
        let calendar = Ref(&calendar);
2170
        // -1245 1 1 = -214526 (R.D Date)
2171
        // 1518 1 1 = 764589 (R.D Date)
2172
        let sum_days_in_year: i64 = (START_YEAR..END_YEAR)
2173
            .map(|year| {
2174
                IslamicObservational::days_in_provided_year(
2175
                    year,
2176
                    IslamicYearInfo::compute::<ObservationalIslamicMarker>(year),
2177
                ) as i64
2178
            })
2179
            .sum();
2180
        let expected_number_of_days =
2181
            Date::try_new_observational_islamic_date(END_YEAR, 1, 1, calendar)
2182
                .unwrap()
2183
                .to_fixed()
2184
                - Date::try_new_observational_islamic_date(START_YEAR, 1, 1, calendar)
2185
                    .unwrap()
2186
                    .to_fixed(); // The number of days between Islamic years -1245 and 1518
2187
        let tolerance = 1; // One day tolerance (See Astronomical::month_length for more context)
2188
2189
        assert!(
2190
            (sum_days_in_year - expected_number_of_days).abs() <= tolerance,
2191
            "Difference between sum_days_in_year and expected_number_of_days is more than the tolerance"
2192
        );
2193
    }
2194
2195
    #[ignore]
2196
    #[test]
2197
    fn test_days_in_provided_year_ummalqura() {
2198
        let calendar = IslamicUmmAlQura::new();
2199
        let calendar = Ref(&calendar);
2200
        // -1245 1 1 = -214528 (R.D Date)
2201
        // 1518 1 1 = 764588 (R.D Date)
2202
        let sum_days_in_year: i64 = (START_YEAR..END_YEAR)
2203
            .map(|year| {
2204
                IslamicUmmAlQura::days_in_provided_year(
2205
                    year,
2206
                    IslamicYearInfo::compute::<SaudiIslamicMarker>(year),
2207
                ) as i64
2208
            })
2209
            .sum();
2210
        let expected_number_of_days = Date::try_new_ummalqura_date(END_YEAR, 1, 1, calendar)
2211
            .unwrap()
2212
            .to_fixed()
2213
            - Date::try_new_ummalqura_date(START_YEAR, 1, 1, calendar)
2214
                .unwrap()
2215
                .to_fixed(); // The number of days between Umm al-Qura Islamic years -1245 and 1518
2216
2217
        assert_eq!(sum_days_in_year, expected_number_of_days);
2218
    }
2219
2220
    #[test]
2221
    fn test_regression_3868() {
2222
        // This date used to panic on creation
2223
        let iso = Date::try_new_iso_date(2011, 4, 4).unwrap();
2224
        let islamic = iso.to_calendar(IslamicUmmAlQura::new());
2225
        // Data from https://www.ummulqura.org.sa/Index.aspx
2226
        assert_eq!(islamic.day_of_month().0, 30);
2227
        assert_eq!(islamic.month().ordinal, 4);
2228
        assert_eq!(islamic.year().number, 1432);
2229
    }
2230
2231
    #[test]
2232
    fn test_regression_4914() {
2233
        // https://github.com/unicode-org/icu4x/issues/4914
2234
        let cal = IslamicUmmAlQura::new_always_calculating();
2235
        let era = "ah".parse().unwrap();
2236
        let year = -6823;
2237
        let month_code = "M01".parse().unwrap();
2238
        let dt = cal.date_from_codes(era, year, month_code, 1).unwrap();
2239
        assert_eq!(dt.0.day, 1);
2240
        assert_eq!(dt.0.month, 1);
2241
        assert_eq!(dt.0.year, -6823);
2242
    }
2243
}