1use crate::calendar_arithmetic::ArithmeticDate;
6use crate::calendar_arithmetic::DateFieldsResolver;
7use crate::calendar_arithmetic::ToExtendedYear;
8use crate::error::{DateError, DateFromFieldsError, EcmaReferenceYearError, UnknownEraError};
9use crate::options::DateFromFieldsOptions;
10use crate::options::{DateAddOptions, DateDifferenceOptions};
11use crate::types::DateFields;
12use crate::{types, Calendar, Date};
13use crate::{AsCalendar, RangeError};
14use calendrical_calculations::islamic::{
15 ISLAMIC_EPOCH_FRIDAY, ISLAMIC_EPOCH_THURSDAY, WELL_BEHAVED_ASTRONOMICAL_RANGE,
16};
17use calendrical_calculations::rata_die::RataDie;
18use core::fmt::Debug;
19use icu_locale_core::preferences::extensions::unicode::keywords::{
20 CalendarAlgorithm, HijriCalendarAlgorithm,
21};
22use icu_provider::prelude::*;
23use tinystr::tinystr;
24
25#[path = "hijri/simulated_mecca_data.rs"]
26mod simulated_mecca_data;
27#[path = "hijri/ummalqura_data.rs"]
28mod ummalqura_data;
29
30#[derive(Clone, Debug, Default, Copy)]
66#[allow(clippy::exhaustive_structs)] pub struct Hijri<S>(pub S);
68
69pub trait Rules: Clone + Debug + crate::cal::scaffold::UnstableSealed {
82 fn year_data(&self, extended_year: i32) -> HijriYearData;
84
85 fn ecma_reference_year(
99 &self,
100 _month_code: (u8, bool),
102 _day: u8,
103 ) -> Result<i32, EcmaReferenceYearError> {
104 Err(EcmaReferenceYearError::Unimplemented)
105 }
106
107 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
109 None
110 }
111
112 fn debug_name(&self) -> &'static str {
114 "Hijri (custom rules)"
115 }
116}
117
118#[derive(Copy, Clone, Debug)]
134pub struct AstronomicalSimulation {
135 pub(crate) location: SimulatedLocation,
136}
137
138#[derive(Clone, Debug, Copy, PartialEq)]
139pub(crate) enum SimulatedLocation {
140 Mecca,
141}
142
143impl crate::cal::scaffold::UnstableSealed for AstronomicalSimulation {}
144impl Rules for AstronomicalSimulation {
145 fn debug_name(&self) -> &'static str {
146 match self.location {
147 SimulatedLocation::Mecca => "Hijri (simulated, Mecca)",
148 }
149 }
150
151 fn year_data(&self, extended_year: i32) -> HijriYearData {
152 if let Some(data) = HijriYearData::lookup(
153 extended_year,
154 simulated_mecca_data::STARTING_YEAR,
155 simulated_mecca_data::DATA,
156 ) {
157 return data;
158 }
159
160 let location = match self.location {
161 SimulatedLocation::Mecca => calendrical_calculations::islamic::MECCA,
162 };
163
164 let start_day = calendrical_calculations::islamic::fixed_from_observational_islamic(
165 extended_year,
166 1,
167 1,
168 location,
169 );
170 let next_start_day = calendrical_calculations::islamic::fixed_from_observational_islamic(
171 extended_year + 1,
172 1,
173 1,
174 location,
175 );
176 match (next_start_day - start_day) as u16 {
177 355 | 354 => (),
178 353 => {
179 icu_provider::log::trace!(
180 "({}) Found year {extended_year} AH with length {}. See <https://github.com/unicode-org/icu4x/issues/4930>",
181 self.debug_name(),
182 next_start_day - start_day
183 );
184 }
185 other => {
186 debug_assert!(
187 !WELL_BEHAVED_ASTRONOMICAL_RANGE.contains(&start_day),
188 "({}) Found year {extended_year} AH with length {}!",
189 self.debug_name(),
190 other
191 )
192 }
193 }
194
195 let month_lengths = {
196 let mut excess_days = 0;
197 let mut month_lengths = core::array::from_fn(|month_idx| {
198 let days_in_month =
199 calendrical_calculations::islamic::observational_islamic_month_days(
200 extended_year,
201 month_idx as u8 + 1,
202 location,
203 );
204 match days_in_month {
205 29 => false,
206 30 => true,
207 31 => {
208 icu_provider::log::trace!(
209 "({}) Found year {extended_year} AH with month length {days_in_month} for month {}.",
210 self.debug_name(),
211 month_idx + 1
212 );
213 excess_days += 1;
214 true
215 }
216 _ => {
217 debug_assert!(
218 !WELL_BEHAVED_ASTRONOMICAL_RANGE.contains(&start_day),
219 "({}) Found year {extended_year} AH with month length {days_in_month} for month {}!",
220 self.debug_name(),
221 month_idx + 1
222 );
223 false
224 }
225 }
226 });
227 if excess_days != 0 {
231 debug_assert!(
232 excess_days == 1 || !WELL_BEHAVED_ASTRONOMICAL_RANGE.contains(&start_day),
233 "({}) Found year {extended_year} AH with more than one excess day!",
234 self.debug_name()
235 );
236 if let Some(l) = month_lengths.iter_mut().find(|l| !(**l)) {
237 *l = true;
238 }
239 }
240 month_lengths
241 };
242 HijriYearData::try_new(extended_year, start_day, month_lengths)
243 .unwrap_or_else(|| UmmAlQura.year_data(extended_year))
244 }
245}
246
247#[derive(Copy, Clone, Debug, Default)]
261#[non_exhaustive]
262pub struct UmmAlQura;
263
264impl crate::cal::scaffold::UnstableSealed for UmmAlQura {}
265impl Rules for UmmAlQura {
266 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
267 Some(CalendarAlgorithm::Hijri(Some(
268 HijriCalendarAlgorithm::Umalqura,
269 )))
270 }
271
272 fn ecma_reference_year(
273 &self,
274 month_code: (u8, bool),
275 day: u8,
276 ) -> Result<i32, EcmaReferenceYearError> {
277 let (ordinal_month, false) = month_code else {
278 return Err(EcmaReferenceYearError::MonthCodeNotInCalendar);
279 };
280
281 let extended_year = match (ordinal_month, day) {
282 (1, _) => 1392,
283 (2, 30..) => 1390,
284 (2, _) => 1392,
285 (3, 30..) => 1391,
286 (3, _) => 1392,
287 (4, _) => 1392,
288 (5, 30..) => 1391,
289 (5, _) => 1392,
290 (6, _) => 1392,
291 (7, 30..) => 1389,
292 (7, _) => 1392,
293 (8, _) => 1392,
294 (9, _) => 1392,
295 (10, 30..) => 1390,
296 (10, _) => 1392,
297 (11, ..=25) => 1392,
298 (11, _) => 1391,
299 (12, 30..) => 1390,
300 (12, _) => 1391,
301 _ => return Err(EcmaReferenceYearError::MonthCodeNotInCalendar),
302 };
303 Ok(extended_year)
304 }
305
306 fn debug_name(&self) -> &'static str {
307 "Hijri (Umm al-Qura)"
308 }
309
310 fn year_data(&self, extended_year: i32) -> HijriYearData {
311 if let Some(data) = HijriYearData::lookup(
312 extended_year,
313 ummalqura_data::STARTING_YEAR,
314 ummalqura_data::DATA,
315 ) {
316 data
317 } else {
318 TabularAlgorithm {
319 leap_years: TabularAlgorithmLeapYears::TypeII,
320 epoch: TabularAlgorithmEpoch::Friday,
321 }
322 .year_data(extended_year)
323 }
324 }
325}
326
327#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
336pub struct TabularAlgorithm {
337 pub(crate) leap_years: TabularAlgorithmLeapYears,
338 pub(crate) epoch: TabularAlgorithmEpoch,
339}
340
341impl TabularAlgorithm {
342 pub const fn new(leap_years: TabularAlgorithmLeapYears, epoch: TabularAlgorithmEpoch) -> Self {
344 Self { epoch, leap_years }
345 }
346}
347
348impl crate::cal::scaffold::UnstableSealed for TabularAlgorithm {}
349impl Rules for TabularAlgorithm {
350 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
351 Some(match (self.epoch, self.leap_years) {
352 (TabularAlgorithmEpoch::Friday, TabularAlgorithmLeapYears::TypeII) => {
353 CalendarAlgorithm::Hijri(Some(HijriCalendarAlgorithm::Civil))
354 }
355 (TabularAlgorithmEpoch::Thursday, TabularAlgorithmLeapYears::TypeII) => {
356 CalendarAlgorithm::Hijri(Some(HijriCalendarAlgorithm::Tbla))
357 }
358 })
359 }
360
361 fn ecma_reference_year(
362 &self,
363 month_code: (u8, bool),
364 day: u8,
365 ) -> Result<i32, EcmaReferenceYearError> {
366 let (ordinal_month, false) = month_code else {
367 return Err(EcmaReferenceYearError::MonthCodeNotInCalendar);
368 };
369
370 Ok(match (ordinal_month, day) {
371 (1, _) => 1392,
372 (2, 30..) => 1389,
373 (2, _) => 1392,
374 (3, _) => 1392,
375 (4, 30..) => 1389,
376 (4, _) => 1392,
377 (5, _) => 1392,
378 (6, 30..) => 1389,
379 (6, _) => 1392,
380 (7, _) => 1392,
381 (8, 30..) => 1389,
382 (8, _) => 1392,
383 (9, _) => 1392,
384 (10, 30..) => 1389,
385 (10, _) => 1392,
386 (11, ..=26) if self.epoch == TabularAlgorithmEpoch::Thursday => 1392,
387 (11, ..=25) if self.epoch == TabularAlgorithmEpoch::Friday => 1392,
388 (11, _) => 1391,
389 (12, 30..) => 1390,
390 (12, _) => 1391,
391 _ => return Err(EcmaReferenceYearError::MonthCodeNotInCalendar),
392 })
393 }
394
395 fn debug_name(&self) -> &'static str {
396 match self.epoch {
397 TabularAlgorithmEpoch::Friday => "Hijri (civil)",
398 TabularAlgorithmEpoch::Thursday => "Hijri (astronomical)",
399 }
400 }
401
402 fn year_data(&self, extended_year: i32) -> HijriYearData {
403 let start_day = calendrical_calculations::islamic::fixed_from_tabular_islamic(
404 extended_year,
405 1,
406 1,
407 self.epoch.rata_die(),
408 );
409 let month_lengths = core::array::from_fn(|m| {
410 m % 2 == 0
411 || m == 11
412 && match self.leap_years {
413 TabularAlgorithmLeapYears::TypeII => {
414 (14 + 11 * extended_year as i64).rem_euclid(30) < 11
415 }
416 }
417 });
418 HijriYearData {
419 packed: PackedHijriYearData::new_unchecked(extended_year, month_lengths, start_day),
422 extended_year,
423 }
424 }
425}
426
427impl Hijri<AstronomicalSimulation> {
428 #[cfg(feature = "compiled_data")]
430 #[deprecated(since = "2.1.0", note = "use `Hijri::new_simulated_mecca`")]
431 pub const fn new_mecca() -> Self {
432 Self::new_simulated_mecca()
433 }
434
435 pub const fn new_simulated_mecca() -> Self {
441 Self(AstronomicalSimulation {
442 location: SimulatedLocation::Mecca,
443 })
444 }
445
446 #[cfg(feature = "serde")]
447 #[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER,Self::new)]
448 #[deprecated(since = "2.1.0", note = "use `Hijri::new_simulated_mecca`")]
449 pub fn try_new_mecca_with_buffer_provider(
450 _provider: &(impl icu_provider::buf::BufferProvider + ?Sized),
451 ) -> Result<Self, DataError> {
452 Ok(Self::new_simulated_mecca())
453 }
454
455 #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new_mecca)]
456 #[deprecated(since = "2.1.0", note = "use `Hijri::new_simulated_mecca`")]
457 pub fn try_new_mecca_unstable<D: ?Sized>(_provider: &D) -> Result<Self, DataError> {
458 Ok(Self::new_simulated_mecca())
459 }
460
461 #[deprecated(since = "2.1.0", note = "use `Hijri::new_simulated_mecca`")]
463 pub const fn new_mecca_always_calculating() -> Self {
464 Self::new_simulated_mecca()
465 }
466}
467
468impl Hijri<UmmAlQura> {
469 #[deprecated(since = "2.1.0", note = "use `Self::new_umm_al_qura`")]
471 pub const fn new() -> Self {
472 Self(UmmAlQura)
473 }
474
475 pub const fn new_umm_al_qura() -> Self {
477 Self(UmmAlQura)
478 }
479}
480
481#[non_exhaustive]
483#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
484pub enum TabularAlgorithmEpoch {
485 Thursday,
487 Friday,
489}
490
491impl TabularAlgorithmEpoch {
492 fn rata_die(self) -> RataDie {
493 match self {
494 Self::Thursday => ISLAMIC_EPOCH_THURSDAY,
495 Self::Friday => ISLAMIC_EPOCH_FRIDAY,
496 }
497 }
498}
499
500#[non_exhaustive]
505#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
506pub enum TabularAlgorithmLeapYears {
507 TypeII,
509}
510
511impl Hijri<TabularAlgorithm> {
512 #[deprecated(since = "2.1.0", note = "use `Hijri::new_tabular`")]
514 pub const fn new(leap_years: TabularAlgorithmLeapYears, epoch: TabularAlgorithmEpoch) -> Self {
515 Hijri::new_tabular(leap_years, epoch)
516 }
517
518 pub const fn new_tabular(
520 leap_years: TabularAlgorithmLeapYears,
521 epoch: TabularAlgorithmEpoch,
522 ) -> Self {
523 Self(TabularAlgorithm::new(leap_years, epoch))
524 }
525}
526
527#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
529pub struct HijriYearData {
530 packed: PackedHijriYearData,
531 extended_year: i32,
532}
533
534impl ToExtendedYear for HijriYearData {
535 fn to_extended_year(&self) -> i32 {
536 self.extended_year
537 }
538}
539
540impl HijriYearData {
541 pub fn try_new(
550 extended_year: i32,
551 start_day: RataDie,
552 month_lengths: [bool; 12],
553 ) -> Option<Self> {
554 Some(Self {
555 packed: PackedHijriYearData::try_new(extended_year, month_lengths, start_day)?,
556 extended_year,
557 })
558 }
559
560 fn lookup(
561 extended_year: i32,
562 starting_year: i32,
563 data: &[PackedHijriYearData],
564 ) -> Option<Self> {
565 Some(extended_year)
566 .and_then(|e| usize::try_from(e.checked_sub(starting_year)?).ok())
567 .and_then(|i| data.get(i))
568 .map(|&packed| Self {
569 extended_year,
570 packed,
571 })
572 }
573
574 fn new_year(self) -> RataDie {
575 self.packed.new_year(self.extended_year)
576 }
577}
578
579#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
601struct PackedHijriYearData(u16);
602
603impl PackedHijriYearData {
604 const fn try_new(
605 extended_year: i32,
606 month_lengths: [bool; 12],
607 start_day: RataDie,
608 ) -> Option<Self> {
609 let start_offset = start_day.since(Self::mean_tabular_start_day(extended_year));
610
611 if !(-8 < start_offset && start_offset < 8
612 || calendrical_calculations::islamic::WELL_BEHAVED_ASTRONOMICAL_RANGE
613 .start
614 .to_i64_date()
615 > start_day.to_i64_date()
616 || calendrical_calculations::islamic::WELL_BEHAVED_ASTRONOMICAL_RANGE
617 .end
618 .to_i64_date()
619 < start_day.to_i64_date())
620 {
621 return None;
622 }
623 let start_offset = start_offset as i8 & 0b1000_0111u8 as i8;
624
625 let mut all = 0u16;
626
627 let mut num_days = 29 * 12;
628
629 let mut i = 0;
630 while i < 12 {
631 #[expect(clippy::indexing_slicing)]
632 if month_lengths[i] {
633 all |= 1 << i;
634 num_days += 1;
635 }
636 i += 1;
637 }
638
639 if !matches!(num_days, 354 | 355) {
640 return None;
641 }
642
643 if start_offset < 0 {
644 all |= 1 << 12;
645 }
646 all |= (start_offset.unsigned_abs() as u16) << 13;
647 Some(Self(all))
648 }
649
650 const fn new_unchecked(
651 extended_year: i32,
652 month_lengths: [bool; 12],
653 start_day: RataDie,
654 ) -> Self {
655 let start_offset = start_day.since(Self::mean_tabular_start_day(extended_year));
656
657 let start_offset = start_offset as i8 & 0b1000_0111u8 as i8;
658
659 let mut all = 0u16;
660
661 let mut i = 0;
662 while i < 12 {
663 #[expect(clippy::indexing_slicing)]
664 if month_lengths[i] {
665 all |= 1 << i;
666 }
667 i += 1;
668 }
669
670 if start_offset < 0 {
671 all |= 1 << 12;
672 }
673 all |= (start_offset.unsigned_abs() as u16) << 13;
674 Self(all)
675 }
676
677 fn new_year(self, extended_year: i32) -> RataDie {
678 let start_offset = if (self.0 & 0b1_0000_0000_0000) != 0 {
679 -((self.0 >> 13) as i64)
680 } else {
681 (self.0 >> 13) as i64
682 };
683 Self::mean_tabular_start_day(extended_year) + start_offset
684 }
685
686 fn month_has_30_days(self, month: u8) -> bool {
687 self.0 & (1 << (month - 1) as u16) != 0
688 }
689
690 fn is_leap(self) -> bool {
691 (self.0 & ((1 << 12) - 1)).count_ones() == 7
692 }
693
694 fn last_day_of_month(self, month: u8) -> u16 {
696 let mut prev_month_lengths = 29 * month as u16;
698 let long_month_bits = self.0 & ((1 << month as u16) - 1);
702 prev_month_lengths += long_month_bits.count_ones().try_into().unwrap_or(0);
703 prev_month_lengths
704 }
705
706 fn days_in_year(self) -> u16 {
707 self.last_day_of_month(12)
708 }
709
710 const fn mean_tabular_start_day(extended_year: i32) -> RataDie {
711 calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY
713 .add((extended_year as i64 - 1) * (354 * 30 + 11) / 30)
714 }
715}
716
717impl<A: AsCalendar<Calendar = Hijri<AstronomicalSimulation>>> Date<A> {
718 #[deprecated(since = "2.1.0", note = "use `Date::try_new_hijri_with_calendar`")]
720 pub fn try_new_simulated_hijri_with_calendar(
721 year: i32,
722 month: u8,
723 day: u8,
724 calendar: A,
725 ) -> Result<Date<A>, RangeError> {
726 Date::try_new_hijri_with_calendar(year, month, day, calendar)
727 }
728}
729
730#[test]
731fn computer_reference_years() {
732 let rules = UmmAlQura;
733
734 fn compute_hijri_reference_year<C>(
735 ordinal_month: u8,
736 day: u8,
737 cal: &C,
738 year_info_from_extended: impl Fn(i32) -> C::YearInfo,
739 ) -> Result<C::YearInfo, DateError>
740 where
741 C: DateFieldsResolver,
742 {
743 let dec_31 = Date::from_rata_die(
744 crate::cal::abstract_gregorian::LAST_DAY_OF_REFERENCE_YEAR,
745 crate::Ref(cal),
746 );
747 debug_assert_eq!(dec_31.month().ordinal, 11);
749 let (y0, y1, y2, y3) =
750 if ordinal_month < 11 || (ordinal_month == 11 && day <= dec_31.day_of_month().0) {
751 (1389, 1390, 1391, 1392)
752 } else {
753 (1388, 1389, 1390, 1391)
754 };
755 let year_info = year_info_from_extended(y3);
756 if day <= C::days_in_provided_month(year_info, ordinal_month) {
757 return Ok(year_info);
758 }
759 let year_info = year_info_from_extended(y2);
760 if day <= C::days_in_provided_month(year_info, ordinal_month) {
761 return Ok(year_info);
762 }
763 let year_info = year_info_from_extended(y1);
764 if day <= C::days_in_provided_month(year_info, ordinal_month) {
765 return Ok(year_info);
766 }
767 let year_info = year_info_from_extended(y0);
768 if day <= 29 {
771 debug_assert!(
772 day <= C::days_in_provided_month(year_info, ordinal_month),
773 "{ordinal_month}/{day}"
774 );
775 }
776 Ok(year_info)
777 }
778 for month in 1..=12 {
779 for day in [30, 29] {
780 let y = compute_hijri_reference_year(month, day, &Hijri(rules), |e| rules.year_data(e))
781 .unwrap()
782 .extended_year;
783
784 if day == 30 {
785 println!("({month}, {day}) => {y},")
786 } else {
787 println!("({month}, _) => {y},")
788 }
789 }
790 }
791}
792
793#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Clone, Debug, Hash)]
795pub struct HijriDateInner<R: Rules>(ArithmeticDate<Hijri<R>>);
797
798impl<R: Rules> Copy for HijriDateInner<R> {}
799impl<R: Rules> PartialEq for HijriDateInner<R> {
800 fn eq(&self, other: &Self) -> bool {
801 self.0 == other.0
802 }
803}
804impl<R: Rules> Eq for HijriDateInner<R> {}
805impl<R: Rules> PartialOrd for HijriDateInner<R> {
806 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
807 Some(self.cmp(other))
808 }
809}
810impl<R: Rules> Ord for HijriDateInner<R> {
811 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
812 self.0.cmp(&other.0)
813 }
814}
815
816impl<R: Rules> DateFieldsResolver for Hijri<R> {
817 type YearInfo = HijriYearData;
818
819 fn days_in_provided_month(year: Self::YearInfo, month: u8) -> u8 {
820 29 + year.packed.month_has_30_days(month) as u8
821 }
822
823 fn months_in_provided_year(_year: Self::YearInfo) -> u8 {
824 12
825 }
826
827 #[inline]
828 fn year_info_from_era(
829 &self,
830 era: &[u8],
831 era_year: i32,
832 ) -> Result<Self::YearInfo, UnknownEraError> {
833 let extended_year = match era {
834 b"ah" => era_year,
835 b"bh" => 1 - era_year,
836 _ => return Err(UnknownEraError),
837 };
838 Ok(self.year_info_from_extended(extended_year))
839 }
840
841 #[inline]
842 fn year_info_from_extended(&self, extended_year: i32) -> Self::YearInfo {
843 self.0.year_data(extended_year)
844 }
845
846 #[inline]
847 fn reference_year_from_month_day(
848 &self,
849 month_code: types::ValidMonthCode,
850 day: u8,
851 ) -> Result<Self::YearInfo, EcmaReferenceYearError> {
852 self.0
853 .ecma_reference_year(month_code.to_tuple(), day)
854 .map(|y| self.0.year_data(y))
855 }
856}
857
858impl<R: Rules> crate::cal::scaffold::UnstableSealed for Hijri<R> {}
859impl<R: Rules> Calendar for Hijri<R> {
860 type DateInner = HijriDateInner<R>;
861 type Year = types::EraYear;
862 type DifferenceError = core::convert::Infallible;
863
864 fn from_codes(
865 &self,
866 era: Option<&str>,
867 year: i32,
868 month_code: types::MonthCode,
869 day: u8,
870 ) -> Result<Self::DateInner, DateError> {
871 ArithmeticDate::from_codes(era, year, month_code, day, self).map(HijriDateInner)
872 }
873
874 #[cfg(feature = "unstable")]
875 fn from_fields(
876 &self,
877 fields: DateFields,
878 options: DateFromFieldsOptions,
879 ) -> Result<Self::DateInner, DateFromFieldsError> {
880 ArithmeticDate::from_fields(fields, options, self).map(HijriDateInner)
881 }
882
883 fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
884 let extended_year = (rd - calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY) * 30
892 / (354 * 30 + 11)
893 + (rd >= calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY) as i64;
894
895 let extended_year = extended_year.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
896
897 let mut year = self.0.year_data(extended_year);
898
899 if rd >= year.new_year() + year.packed.days_in_year() as i64 && extended_year < i32::MAX {
901 year = self.0.year_data(year.extended_year + 1)
902 }
903
904 let rd = rd.clamp(
906 year.new_year(),
907 year.new_year() + year.packed.days_in_year() as i64,
908 );
909
910 let day_of_year = (rd - year.new_year()) as u16;
911
912 let mut month = (day_of_year / 30) as u8 + 1;
915 let mut last_day_of_month = year.packed.last_day_of_month(month);
916 let mut last_day_of_prev_month = year.packed.last_day_of_month(month - 1);
917
918 while day_of_year >= last_day_of_month {
919 month += 1;
920 last_day_of_prev_month = last_day_of_month;
921 last_day_of_month = year.packed.last_day_of_month(month);
922 }
923
924 let day = (day_of_year + 1 - last_day_of_prev_month) as u8;
925
926 HijriDateInner(ArithmeticDate::new_unchecked(year, month, day))
927 }
928
929 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
930 date.0.year.new_year()
931 + date.0.year.packed.last_day_of_month(date.0.month - 1) as i64
932 + (date.0.day - 1) as i64
933 }
934
935 fn has_cheap_iso_conversion(&self) -> bool {
936 false
937 }
938
939 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
940 Self::months_in_provided_year(date.0.year)
941 }
942
943 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
944 date.0.year.packed.days_in_year()
945 }
946
947 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
948 Self::days_in_provided_month(date.0.year, date.0.month)
949 }
950
951 #[cfg(feature = "unstable")]
952 fn add(
953 &self,
954 date: &Self::DateInner,
955 duration: types::DateDuration,
956 options: DateAddOptions,
957 ) -> Result<Self::DateInner, DateError> {
958 date.0.added(duration, self, options).map(HijriDateInner)
959 }
960
961 #[cfg(feature = "unstable")]
962 fn until(
963 &self,
964 date1: &Self::DateInner,
965 date2: &Self::DateInner,
966 options: DateDifferenceOptions,
967 ) -> Result<types::DateDuration, Self::DifferenceError> {
968 Ok(date1.0.until(&date2.0, self, options))
969 }
970
971 fn debug_name(&self) -> &'static str {
972 self.0.debug_name()
973 }
974
975 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
976 let extended_year = date.0.year.extended_year;
977 if extended_year > 0 {
978 types::EraYear {
979 era: tinystr!(16, "ah"),
980 era_index: Some(0),
981 year: extended_year,
982 extended_year,
983 ambiguity: types::YearAmbiguity::CenturyRequired,
984 }
985 } else {
986 types::EraYear {
987 era: tinystr!(16, "bh"),
988 era_index: Some(1),
989 year: 1 - extended_year,
990 extended_year,
991 ambiguity: types::YearAmbiguity::CenturyRequired,
992 }
993 }
994 }
995
996 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
997 date.0.year.packed.is_leap()
998 }
999
1000 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
1001 types::MonthInfo::non_lunisolar(date.0.month)
1002 }
1003
1004 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
1005 types::DayOfMonth(date.0.day)
1006 }
1007
1008 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
1009 types::DayOfYear(date.0.year.packed.last_day_of_month(date.0.month - 1) + date.0.day as u16)
1010 }
1011
1012 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
1013 self.0.calendar_algorithm()
1014 }
1015}
1016
1017impl<A: AsCalendar<Calendar = Hijri<R>>, R: Rules> Date<A> {
1018 pub fn try_new_hijri_with_calendar(
1034 year: i32,
1035 month: u8,
1036 day: u8,
1037 calendar: A,
1038 ) -> Result<Self, RangeError> {
1039 let y = calendar.as_calendar().0.year_data(year);
1040 Ok(Date::from_raw(
1041 HijriDateInner(ArithmeticDate::try_from_ymd(y, month, day)?),
1042 calendar,
1043 ))
1044 }
1045}
1046
1047impl Date<Hijri<UmmAlQura>> {
1048 #[deprecated(since = "2.1.0", note = "use `Date::try_new_hijri_with_calendar")]
1050 pub fn try_new_ummalqura(year: i32, month: u8, day: u8) -> Result<Self, RangeError> {
1051 Date::try_new_hijri_with_calendar(year, month, day, Hijri::new_umm_al_qura())
1052 }
1053}
1054
1055impl<A: AsCalendar<Calendar = Hijri<TabularAlgorithm>>> Date<A> {
1056 #[deprecated(since = "2.1.0", note = "use `Date::try_new_hijri_with_calendar")]
1058 pub fn try_new_hijri_tabular_with_calendar(
1059 year: i32,
1060 month: u8,
1061 day: u8,
1062 calendar: A,
1063 ) -> Result<Date<A>, RangeError> {
1064 Date::try_new_hijri_with_calendar(year, month, day, calendar)
1065 }
1066}
1067
1068#[cfg(test)]
1069mod test {
1070 use types::MonthCode;
1071
1072 use super::*;
1073
1074 const START_YEAR: i32 = -1245;
1075 const END_YEAR: i32 = 1518;
1076
1077 #[derive(Debug)]
1078 struct DateCase {
1079 year: i32,
1080 month: u8,
1081 day: u8,
1082 }
1083
1084 static TEST_RD: [i64; 33] = [
1085 -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
1086 470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
1087 664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
1088 ];
1089
1090 static UMMALQURA_CASES: [DateCase; 33] = [
1091 DateCase {
1092 year: -1245,
1093 month: 12,
1094 day: 9,
1095 },
1096 DateCase {
1097 year: -813,
1098 month: 2,
1099 day: 23,
1100 },
1101 DateCase {
1102 year: -568,
1103 month: 4,
1104 day: 1,
1105 },
1106 DateCase {
1107 year: -501,
1108 month: 4,
1109 day: 6,
1110 },
1111 DateCase {
1112 year: -157,
1113 month: 10,
1114 day: 17,
1115 },
1116 DateCase {
1117 year: -47,
1118 month: 6,
1119 day: 3,
1120 },
1121 DateCase {
1122 year: 75,
1123 month: 7,
1124 day: 13,
1125 },
1126 DateCase {
1127 year: 403,
1128 month: 10,
1129 day: 5,
1130 },
1131 DateCase {
1132 year: 489,
1133 month: 5,
1134 day: 22,
1135 },
1136 DateCase {
1137 year: 586,
1138 month: 2,
1139 day: 7,
1140 },
1141 DateCase {
1142 year: 637,
1143 month: 8,
1144 day: 7,
1145 },
1146 DateCase {
1147 year: 687,
1148 month: 2,
1149 day: 20,
1150 },
1151 DateCase {
1152 year: 697,
1153 month: 7,
1154 day: 7,
1155 },
1156 DateCase {
1157 year: 793,
1158 month: 7,
1159 day: 1,
1160 },
1161 DateCase {
1162 year: 839,
1163 month: 7,
1164 day: 6,
1165 },
1166 DateCase {
1167 year: 897,
1168 month: 6,
1169 day: 1,
1170 },
1171 DateCase {
1172 year: 960,
1173 month: 9,
1174 day: 30,
1175 },
1176 DateCase {
1177 year: 967,
1178 month: 5,
1179 day: 27,
1180 },
1181 DateCase {
1182 year: 1058,
1183 month: 5,
1184 day: 18,
1185 },
1186 DateCase {
1187 year: 1091,
1188 month: 6,
1189 day: 2,
1190 },
1191 DateCase {
1192 year: 1128,
1193 month: 8,
1194 day: 4,
1195 },
1196 DateCase {
1197 year: 1182,
1198 month: 2,
1199 day: 3,
1200 },
1201 DateCase {
1202 year: 1234,
1203 month: 10,
1204 day: 10,
1205 },
1206 DateCase {
1207 year: 1255,
1208 month: 1,
1209 day: 11,
1210 },
1211 DateCase {
1212 year: 1321,
1213 month: 1,
1214 day: 21,
1215 },
1216 DateCase {
1217 year: 1348,
1218 month: 3,
1219 day: 20,
1220 },
1221 DateCase {
1222 year: 1360,
1223 month: 9,
1224 day: 7,
1225 },
1226 DateCase {
1227 year: 1362,
1228 month: 4,
1229 day: 14,
1230 },
1231 DateCase {
1232 year: 1362,
1233 month: 10,
1234 day: 7,
1235 },
1236 DateCase {
1237 year: 1412,
1238 month: 9,
1239 day: 12,
1240 },
1241 DateCase {
1242 year: 1416,
1243 month: 10,
1244 day: 6,
1245 },
1246 DateCase {
1247 year: 1460,
1248 month: 10,
1249 day: 13,
1250 },
1251 DateCase {
1252 year: 1518,
1253 month: 3,
1254 day: 5,
1255 },
1256 ];
1257
1258 static SIMULATED_CASES: [DateCase; 33] = [
1259 DateCase {
1260 year: -1245,
1261 month: 12,
1262 day: 10,
1263 },
1264 DateCase {
1265 year: -813,
1266 month: 2,
1267 day: 25,
1268 },
1269 DateCase {
1270 year: -568,
1271 month: 4,
1272 day: 2,
1273 },
1274 DateCase {
1275 year: -501,
1276 month: 4,
1277 day: 7,
1278 },
1279 DateCase {
1280 year: -157,
1281 month: 10,
1282 day: 18,
1283 },
1284 DateCase {
1285 year: -47,
1286 month: 6,
1287 day: 3,
1288 },
1289 DateCase {
1290 year: 75,
1291 month: 7,
1292 day: 13,
1293 },
1294 DateCase {
1295 year: 403,
1296 month: 10,
1297 day: 5,
1298 },
1299 DateCase {
1300 year: 489,
1301 month: 5,
1302 day: 22,
1303 },
1304 DateCase {
1305 year: 586,
1306 month: 2,
1307 day: 7,
1308 },
1309 DateCase {
1310 year: 637,
1311 month: 8,
1312 day: 7,
1313 },
1314 DateCase {
1315 year: 687,
1316 month: 2,
1317 day: 21,
1318 },
1319 DateCase {
1320 year: 697,
1321 month: 7,
1322 day: 7,
1323 },
1324 DateCase {
1325 year: 793,
1326 month: 6,
1327 day: 29,
1328 },
1329 DateCase {
1330 year: 839,
1331 month: 7,
1332 day: 6,
1333 },
1334 DateCase {
1335 year: 897,
1336 month: 6,
1337 day: 2,
1338 },
1339 DateCase {
1340 year: 960,
1341 month: 9,
1342 day: 30,
1343 },
1344 DateCase {
1345 year: 967,
1346 month: 5,
1347 day: 27,
1348 },
1349 DateCase {
1350 year: 1058,
1351 month: 5,
1352 day: 18,
1353 },
1354 DateCase {
1355 year: 1091,
1356 month: 6,
1357 day: 3,
1358 },
1359 DateCase {
1360 year: 1128,
1361 month: 8,
1362 day: 4,
1363 },
1364 DateCase {
1365 year: 1182,
1366 month: 2,
1367 day: 4,
1368 },
1369 DateCase {
1370 year: 1234,
1371 month: 10,
1372 day: 10,
1373 },
1374 DateCase {
1375 year: 1255,
1376 month: 1,
1377 day: 11,
1378 },
1379 DateCase {
1380 year: 1321,
1381 month: 1,
1382 day: 20,
1383 },
1384 DateCase {
1385 year: 1348,
1386 month: 3,
1387 day: 19,
1388 },
1389 DateCase {
1390 year: 1360,
1391 month: 9,
1392 day: 7,
1393 },
1394 DateCase {
1395 year: 1362,
1396 month: 4,
1397 day: 13,
1398 },
1399 DateCase {
1400 year: 1362,
1401 month: 10,
1402 day: 7,
1403 },
1404 DateCase {
1405 year: 1412,
1406 month: 9,
1407 day: 12,
1408 },
1409 DateCase {
1410 year: 1416,
1411 month: 10,
1412 day: 5,
1413 },
1414 DateCase {
1415 year: 1460,
1416 month: 10,
1417 day: 12,
1418 },
1419 DateCase {
1420 year: 1518,
1421 month: 3,
1422 day: 5,
1423 },
1424 ];
1425
1426 static ARITHMETIC_CASES: [DateCase; 33] = [
1427 DateCase {
1428 year: -1245,
1429 month: 12,
1430 day: 9,
1431 },
1432 DateCase {
1433 year: -813,
1434 month: 2,
1435 day: 23,
1436 },
1437 DateCase {
1438 year: -568,
1439 month: 4,
1440 day: 1,
1441 },
1442 DateCase {
1443 year: -501,
1444 month: 4,
1445 day: 6,
1446 },
1447 DateCase {
1448 year: -157,
1449 month: 10,
1450 day: 17,
1451 },
1452 DateCase {
1453 year: -47,
1454 month: 6,
1455 day: 3,
1456 },
1457 DateCase {
1458 year: 75,
1459 month: 7,
1460 day: 13,
1461 },
1462 DateCase {
1463 year: 403,
1464 month: 10,
1465 day: 5,
1466 },
1467 DateCase {
1468 year: 489,
1469 month: 5,
1470 day: 22,
1471 },
1472 DateCase {
1473 year: 586,
1474 month: 2,
1475 day: 7,
1476 },
1477 DateCase {
1478 year: 637,
1479 month: 8,
1480 day: 7,
1481 },
1482 DateCase {
1483 year: 687,
1484 month: 2,
1485 day: 20,
1486 },
1487 DateCase {
1488 year: 697,
1489 month: 7,
1490 day: 7,
1491 },
1492 DateCase {
1493 year: 793,
1494 month: 7,
1495 day: 1,
1496 },
1497 DateCase {
1498 year: 839,
1499 month: 7,
1500 day: 6,
1501 },
1502 DateCase {
1503 year: 897,
1504 month: 6,
1505 day: 1,
1506 },
1507 DateCase {
1508 year: 960,
1509 month: 9,
1510 day: 30,
1511 },
1512 DateCase {
1513 year: 967,
1514 month: 5,
1515 day: 27,
1516 },
1517 DateCase {
1518 year: 1058,
1519 month: 5,
1520 day: 18,
1521 },
1522 DateCase {
1523 year: 1091,
1524 month: 6,
1525 day: 2,
1526 },
1527 DateCase {
1528 year: 1128,
1529 month: 8,
1530 day: 4,
1531 },
1532 DateCase {
1533 year: 1182,
1534 month: 2,
1535 day: 3,
1536 },
1537 DateCase {
1538 year: 1234,
1539 month: 10,
1540 day: 10,
1541 },
1542 DateCase {
1543 year: 1255,
1544 month: 1,
1545 day: 11,
1546 },
1547 DateCase {
1548 year: 1321,
1549 month: 1,
1550 day: 21,
1551 },
1552 DateCase {
1553 year: 1348,
1554 month: 3,
1555 day: 19,
1556 },
1557 DateCase {
1558 year: 1360,
1559 month: 9,
1560 day: 8,
1561 },
1562 DateCase {
1563 year: 1362,
1564 month: 4,
1565 day: 13,
1566 },
1567 DateCase {
1568 year: 1362,
1569 month: 10,
1570 day: 7,
1571 },
1572 DateCase {
1573 year: 1412,
1574 month: 9,
1575 day: 13,
1576 },
1577 DateCase {
1578 year: 1416,
1579 month: 10,
1580 day: 5,
1581 },
1582 DateCase {
1583 year: 1460,
1584 month: 10,
1585 day: 12,
1586 },
1587 DateCase {
1588 year: 1518,
1589 month: 3,
1590 day: 5,
1591 },
1592 ];
1593
1594 static ASTRONOMICAL_CASES: [DateCase; 33] = [
1595 DateCase {
1596 year: -1245,
1597 month: 12,
1598 day: 10,
1599 },
1600 DateCase {
1601 year: -813,
1602 month: 2,
1603 day: 24,
1604 },
1605 DateCase {
1606 year: -568,
1607 month: 4,
1608 day: 2,
1609 },
1610 DateCase {
1611 year: -501,
1612 month: 4,
1613 day: 7,
1614 },
1615 DateCase {
1616 year: -157,
1617 month: 10,
1618 day: 18,
1619 },
1620 DateCase {
1621 year: -47,
1622 month: 6,
1623 day: 4,
1624 },
1625 DateCase {
1626 year: 75,
1627 month: 7,
1628 day: 14,
1629 },
1630 DateCase {
1631 year: 403,
1632 month: 10,
1633 day: 6,
1634 },
1635 DateCase {
1636 year: 489,
1637 month: 5,
1638 day: 23,
1639 },
1640 DateCase {
1641 year: 586,
1642 month: 2,
1643 day: 8,
1644 },
1645 DateCase {
1646 year: 637,
1647 month: 8,
1648 day: 8,
1649 },
1650 DateCase {
1651 year: 687,
1652 month: 2,
1653 day: 21,
1654 },
1655 DateCase {
1656 year: 697,
1657 month: 7,
1658 day: 8,
1659 },
1660 DateCase {
1661 year: 793,
1662 month: 7,
1663 day: 2,
1664 },
1665 DateCase {
1666 year: 839,
1667 month: 7,
1668 day: 7,
1669 },
1670 DateCase {
1671 year: 897,
1672 month: 6,
1673 day: 2,
1674 },
1675 DateCase {
1676 year: 960,
1677 month: 10,
1678 day: 1,
1679 },
1680 DateCase {
1681 year: 967,
1682 month: 5,
1683 day: 28,
1684 },
1685 DateCase {
1686 year: 1058,
1687 month: 5,
1688 day: 19,
1689 },
1690 DateCase {
1691 year: 1091,
1692 month: 6,
1693 day: 3,
1694 },
1695 DateCase {
1696 year: 1128,
1697 month: 8,
1698 day: 5,
1699 },
1700 DateCase {
1701 year: 1182,
1702 month: 2,
1703 day: 4,
1704 },
1705 DateCase {
1706 year: 1234,
1707 month: 10,
1708 day: 11,
1709 },
1710 DateCase {
1711 year: 1255,
1712 month: 1,
1713 day: 12,
1714 },
1715 DateCase {
1716 year: 1321,
1717 month: 1,
1718 day: 22,
1719 },
1720 DateCase {
1721 year: 1348,
1722 month: 3,
1723 day: 20,
1724 },
1725 DateCase {
1726 year: 1360,
1727 month: 9,
1728 day: 9,
1729 },
1730 DateCase {
1731 year: 1362,
1732 month: 4,
1733 day: 14,
1734 },
1735 DateCase {
1736 year: 1362,
1737 month: 10,
1738 day: 8,
1739 },
1740 DateCase {
1741 year: 1412,
1742 month: 9,
1743 day: 14,
1744 },
1745 DateCase {
1746 year: 1416,
1747 month: 10,
1748 day: 6,
1749 },
1750 DateCase {
1751 year: 1460,
1752 month: 10,
1753 day: 13,
1754 },
1755 DateCase {
1756 year: 1518,
1757 month: 3,
1758 day: 6,
1759 },
1760 ];
1761
1762 #[test]
1763 fn test_simulated_hijri_from_rd() {
1764 let calendar = Hijri::new_simulated_mecca();
1765 for (case, f_date) in SIMULATED_CASES.iter().zip(TEST_RD.iter()) {
1766 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1767 .unwrap();
1768 let iso = Date::from_rata_die(RataDie::new(*f_date), crate::Iso);
1769
1770 assert_eq!(iso.to_calendar(calendar).inner, date.inner, "{case:?}");
1771 }
1772 }
1773
1774 #[test]
1775 fn test_rd_from_simulated_hijri() {
1776 let calendar = Hijri::new_simulated_mecca();
1777 for (case, f_date) in SIMULATED_CASES.iter().zip(TEST_RD.iter()) {
1778 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1779 .unwrap();
1780 assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
1781 }
1782 }
1783
1784 #[test]
1785 fn test_rd_from_hijri() {
1786 let calendar = Hijri::new_tabular(
1787 TabularAlgorithmLeapYears::TypeII,
1788 TabularAlgorithmEpoch::Friday,
1789 );
1790 for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) {
1791 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1792 .unwrap();
1793 assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
1794 }
1795 }
1796
1797 #[test]
1798 fn test_hijri_from_rd() {
1799 let calendar = Hijri::new_tabular(
1800 TabularAlgorithmLeapYears::TypeII,
1801 TabularAlgorithmEpoch::Friday,
1802 );
1803 for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) {
1804 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1805 .unwrap();
1806 let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
1807
1808 assert_eq!(date, date_rd, "{case:?}");
1809 }
1810 }
1811
1812 #[test]
1813 fn test_rd_from_hijri_tbla() {
1814 let calendar = Hijri::new_tabular(
1815 TabularAlgorithmLeapYears::TypeII,
1816 TabularAlgorithmEpoch::Thursday,
1817 );
1818 for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) {
1819 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1820 .unwrap();
1821 assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
1822 }
1823 }
1824
1825 #[test]
1826 fn test_hijri_tbla_from_rd() {
1827 let calendar = Hijri::new_tabular(
1828 TabularAlgorithmLeapYears::TypeII,
1829 TabularAlgorithmEpoch::Thursday,
1830 );
1831 for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) {
1832 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1833 .unwrap();
1834 let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
1835
1836 assert_eq!(date, date_rd, "{case:?}");
1837 }
1838 }
1839
1840 #[test]
1841 fn test_saudi_hijri_from_rd() {
1842 let calendar = Hijri::new_umm_al_qura();
1843 for (case, f_date) in UMMALQURA_CASES.iter().zip(TEST_RD.iter()) {
1844 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1845 .unwrap();
1846 let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
1847
1848 assert_eq!(date, date_rd, "{case:?}");
1849 }
1850 }
1851
1852 #[test]
1853 fn test_rd_from_saudi_hijri() {
1854 let calendar = Hijri::new_umm_al_qura();
1855 for (case, f_date) in UMMALQURA_CASES.iter().zip(TEST_RD.iter()) {
1856 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1857 .unwrap();
1858 assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
1859 }
1860 }
1861
1862 #[ignore] #[test]
1864 fn test_days_in_provided_year_simulated() {
1865 let calendar = Hijri::new_simulated_mecca();
1866 let sum_days_in_year: i64 = (START_YEAR..END_YEAR)
1869 .map(|year| {
1870 Hijri::new_simulated_mecca()
1871 .0
1872 .year_data(year)
1873 .packed
1874 .days_in_year() as i64
1875 })
1876 .sum();
1877 let expected_number_of_days = Date::try_new_hijri_with_calendar(END_YEAR, 1, 1, calendar)
1878 .unwrap()
1879 .to_rata_die()
1880 - Date::try_new_hijri_with_calendar(START_YEAR, 1, 1, calendar)
1881 .unwrap()
1882 .to_rata_die(); let tolerance = 1; assert!(
1886 (sum_days_in_year - expected_number_of_days).abs() <= tolerance,
1887 "Difference between sum_days_in_year and expected_number_of_days is more than the tolerance"
1888 );
1889 }
1890
1891 #[ignore] #[test]
1893 fn test_days_in_provided_year_ummalqura() {
1894 let calendar = Hijri::new_umm_al_qura();
1895 let sum_days_in_year: i64 = (START_YEAR..END_YEAR)
1898 .map(|year| calendar.0.year_data(year).packed.days_in_year() as i64)
1899 .sum();
1900 let expected_number_of_days = Date::try_new_hijri_with_calendar(END_YEAR, 1, 1, calendar)
1901 .unwrap()
1902 .to_rata_die()
1903 - (Date::try_new_hijri_with_calendar(START_YEAR, 1, 1, calendar).unwrap())
1904 .to_rata_die(); assert_eq!(sum_days_in_year, expected_number_of_days);
1907 }
1908
1909 #[test]
1910 fn test_regression_3868() {
1911 let iso = Date::try_new_iso(2011, 4, 4).unwrap();
1913 let hijri = iso.to_calendar(Hijri::new_umm_al_qura());
1914 assert_eq!(hijri.day_of_month().0, 30);
1916 assert_eq!(hijri.month().ordinal, 4);
1917 assert_eq!(hijri.era_year().year, 1432);
1918 }
1919
1920 #[test]
1921 fn test_regression_4914() {
1922 let dt = Hijri::new_umm_al_qura()
1924 .from_codes(Some("bh"), 6824, MonthCode::new_normal(1).unwrap(), 1)
1925 .unwrap();
1926 assert_eq!(dt.0.day, 1);
1927 assert_eq!(dt.0.month, 1);
1928 assert_eq!(dt.0.year.extended_year, -6823);
1929 }
1930
1931 #[test]
1932 fn test_regression_7056() {
1933 let calendar = Hijri::new_tabular(
1935 TabularAlgorithmLeapYears::TypeII,
1936 TabularAlgorithmEpoch::Friday,
1937 );
1938 let iso = Date::try_new_iso(-62971, 3, 19).unwrap();
1939 let _dt = iso.to_calendar(calendar);
1940 let _dt = iso.to_calendar(Hijri::new_umm_al_qura());
1941 }
1942
1943 #[test]
1944 fn test_regression_5069_uaq() {
1945 let calendar = Hijri::new_umm_al_qura();
1946
1947 let dt = Date::try_new_hijri_with_calendar(1391, 1, 29, calendar).unwrap();
1948
1949 assert_eq!(dt.to_iso().to_calendar(calendar), dt);
1950 }
1951
1952 #[test]
1953 fn test_regression_5069_obs() {
1954 let cal = Hijri::new_simulated_mecca();
1955
1956 let dt = Date::try_new_hijri_with_calendar(1390, 1, 30, cal).unwrap();
1957
1958 assert_eq!(dt.to_iso().to_calendar(cal), dt);
1959
1960 let dt = Date::try_new_iso(2000, 5, 5).unwrap();
1961
1962 assert!(dt.to_calendar(cal).day_of_month().0 > 0);
1963 }
1964
1965 #[test]
1966 fn test_regression_6197() {
1967 let calendar = Hijri::new_umm_al_qura();
1968
1969 let iso = Date::try_new_iso(2025, 2, 26).unwrap();
1970
1971 let date = iso.to_calendar(calendar);
1972
1973 assert_eq!(
1975 (
1976 date.day_of_month().0,
1977 date.month().ordinal,
1978 date.era_year().year
1979 ),
1980 (27, 8, 1446)
1981 );
1982 }
1983
1984 #[test]
1985 fn test_hijri_packed_roundtrip() {
1986 fn single_roundtrip(month_lengths: [bool; 12], start_day: RataDie) -> Option<()> {
1987 let packed = PackedHijriYearData::try_new(1600, month_lengths, start_day)?;
1988 for i in 0..12 {
1989 assert_eq!(packed.month_has_30_days(i + 1), month_lengths[i as usize]);
1990 }
1991 assert_eq!(packed.new_year(1600), start_day);
1992 Some(())
1993 }
1994
1995 let l = true;
1996 let s = false;
1997 let all_short = [s; 12];
1998 let all_long = [l; 12];
1999 let mixed1 = [l, s, l, s, l, s, l, s, l, s, l, s];
2000 let mixed2 = [s, s, l, l, l, s, l, s, s, s, l, l];
2001
2002 let start_1600 = PackedHijriYearData::mean_tabular_start_day(1600);
2003 assert_eq!(single_roundtrip(all_short, start_1600), None);
2004 assert_eq!(single_roundtrip(all_long, start_1600), None);
2005 single_roundtrip(mixed1, start_1600).unwrap();
2006 single_roundtrip(mixed2, start_1600).unwrap();
2007
2008 single_roundtrip(mixed1, start_1600 - 7).unwrap();
2009 single_roundtrip(mixed2, start_1600 + 7).unwrap();
2010 single_roundtrip(mixed2, start_1600 + 4).unwrap();
2011 single_roundtrip(mixed2, start_1600 + 1).unwrap();
2012 single_roundtrip(mixed2, start_1600 - 1).unwrap();
2013 single_roundtrip(mixed2, start_1600 - 4).unwrap();
2014 }
2015}