1use crate::calendar_arithmetic::PrecomputedDataSource;
40use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
41use crate::provider::islamic::{
42 IslamicCacheV1, IslamicObservationalCacheV1Marker, IslamicUmmAlQuraCacheV1Marker,
43 PackedIslamicYearInfo,
44};
45use crate::AnyCalendarKind;
46use crate::AsCalendar;
47use crate::Iso;
48use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time};
49use calendrical_calculations::islamic::{
50 IslamicBasedMarker, ObservationalIslamicMarker, SaudiIslamicMarker,
51};
52use calendrical_calculations::rata_die::RataDie;
53use core::marker::PhantomData;
54use icu_provider::prelude::*;
55use tinystr::tinystr;
56
57#[derive(Clone, Debug, Default)]
68pub struct IslamicObservational {
69 data: Option<DataPayload<IslamicObservationalCacheV1Marker>>,
70}
71
72#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
83#[allow(clippy::exhaustive_structs)] pub struct IslamicCivil;
85
86#[derive(Clone, Debug, Default)]
97pub struct IslamicUmmAlQura {
98 data: Option<DataPayload<IslamicUmmAlQuraCacheV1Marker>>,
99}
100
101#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
112#[allow(clippy::exhaustive_structs)] pub struct IslamicTabular;
114
115impl IslamicObservational {
116 #[cfg(feature = "compiled_data")]
122 pub const fn new() -> Self {
123 Self {
124 data: Some(DataPayload::from_static_ref(
125 crate::provider::Baked::SINGLETON_CALENDAR_ISLAMICOBSERVATIONALCACHE_V1,
126 )),
127 }
128 }
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 pub fn try_new_unstable<D: DataProvider<IslamicObservationalCacheV1Marker> + ?Sized>(
142 provider: &D,
143 ) -> Result<Self, CalendarError> {
144 Ok(Self {
145 data: Some(provider.load(Default::default())?.take_payload()?),
146 })
147 }
148
149 pub fn new_always_calculating() -> Self {
151 Self { data: None }
152 }
153}
154
155impl IslamicCivil {
156 pub fn new() -> Self {
158 Self
159 }
160
161 #[deprecated = "Precomputation not needed for this calendar"]
163 pub fn new_always_calculating() -> Self {
164 Self
165 }
166}
167
168impl IslamicUmmAlQura {
169 #[cfg(feature = "compiled_data")]
175 pub const fn new() -> Self {
176 Self {
177 data: Some(DataPayload::from_static_ref(
178 crate::provider::Baked::SINGLETON_CALENDAR_ISLAMICUMMALQURACACHE_V1,
179 )),
180 }
181 }
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 pub fn try_new_unstable<D: DataProvider<IslamicUmmAlQuraCacheV1Marker> + ?Sized>(
195 provider: &D,
196 ) -> Result<Self, CalendarError> {
197 Ok(Self {
198 data: Some(provider.load(Default::default())?.take_payload()?),
199 })
200 }
201
202 pub fn new_always_calculating() -> Self {
204 Self { data: None }
205 }
206}
207
208impl IslamicTabular {
209 pub fn new() -> Self {
211 Self
212 }
213
214 #[deprecated = "Precomputation not needed for this calendar"]
216 pub fn new_always_calculating() -> Self {
217 Self
218 }
219}
220
221#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
223enum IslamicYearLength {
224 L355,
226 L354,
228 L353,
233}
234
235impl Default for IslamicYearLength {
236 fn default() -> Self {
237 Self::L354
238 }
239}
240
241impl IslamicYearLength {
242 fn try_from_int(value: i64) -> Option<Self> {
243 match value {
244 355 => Some(Self::L355),
245 354 => Some(Self::L354),
246 353 => Some(Self::L353),
247 _ => None,
248 }
249 }
250 fn to_int(self) -> u16 {
251 match self {
252 Self::L355 => 355,
253 Self::L354 => 354,
254 Self::L353 => 353,
255 }
256 }
257}
258
259#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
260pub(crate) struct IslamicYearInfo {
261 packed_data: PackedIslamicYearInfo,
262 prev_year_length: IslamicYearLength,
263}
264
265impl IslamicYearInfo {
266 pub(crate) const LONG_YEAR_LEN: u16 = 355;
267 const SHORT_YEAR_LEN: u16 = 354;
268 pub(crate) fn new(
269 prev_packed: PackedIslamicYearInfo,
270 this_packed: PackedIslamicYearInfo,
271 extended_year: i32,
272 ) -> (Self, i32) {
273 let days_in_year = prev_packed.days_in_year();
274 let days_in_year = match IslamicYearLength::try_from_int(days_in_year as i64) {
275 Some(x) => x,
276 None => {
277 debug_assert!(false, "Found wrong year length for Islamic year {extended_year}: Expected 355, 354, or 353, got {days_in_year}");
278 Default::default()
279 }
280 };
281 let year_info = Self {
282 prev_year_length: days_in_year,
283 packed_data: this_packed,
284 };
285 (year_info, extended_year)
286 }
287
288 fn compute<IB: IslamicBasedMarker>(extended_year: i32) -> Self {
289 let ny = IB::fixed_from_islamic(extended_year, 1, 1);
290 let packed_data = PackedIslamicYearInfo::compute_with_ny::<IB>(extended_year, ny);
291 let prev_ny = IB::fixed_from_islamic(extended_year - 1, 1, 1);
292 let rd_diff = ny - prev_ny;
293 let rd_diff = match IslamicYearLength::try_from_int(rd_diff) {
294 Some(x) => x,
295 None => {
296 debug_assert!(false, "({}) Found wrong year length for Islamic year {extended_year}: Expected 355, 354, or 353, got {rd_diff}", IB::DEBUG_NAME);
297 Default::default()
298 }
299 };
300 Self {
301 prev_year_length: rd_diff,
302 packed_data,
303 }
304 }
305 fn new_year<IB: IslamicBasedMarker>(self, extended_year: i32) -> RataDie {
307 IB::mean_synodic_ny(extended_year) + i64::from(self.packed_data.ny_offset())
308 }
309
310 fn rd_for<IB: IslamicBasedMarker>(self, extended_year: i32, month: u8, day: u8) -> RataDie {
312 let ny = self.new_year::<IB>(extended_year);
313 let month_offset = if month == 1 {
314 0
315 } else {
316 self.packed_data.last_day_of_month(month - 1)
317 };
318 ny - 1 + month_offset.into() + day.into()
320 }
321
322 #[inline]
323 fn days_in_prev_year(self) -> u16 {
324 self.prev_year_length.to_int()
325 }
326}
327
328#[derive(Default)]
331pub(crate) struct IslamicPrecomputedData<'a, IB: IslamicBasedMarker> {
332 data: Option<&'a IslamicCacheV1<'a>>,
333 _ib: PhantomData<IB>,
334}
335
336impl<'b, IB: IslamicBasedMarker> PrecomputedDataSource<IslamicYearInfo>
337 for IslamicPrecomputedData<'b, IB>
338{
339 fn load_or_compute_info(&self, extended_year: i32) -> IslamicYearInfo {
340 self.data
341 .and_then(|d| d.get_for_extended_year(extended_year))
342 .unwrap_or_else(|| IslamicYearInfo::compute::<IB>(extended_year))
343 }
344}
345
346fn compute_month_day(info: IslamicYearInfo, mut possible_month: u8, day_of_year: u16) -> (u8, u8) {
349 let mut last_day_of_month = info.packed_data.last_day_of_month(possible_month);
350 let mut last_day_of_prev_month = if possible_month == 1 {
351 0
352 } else {
353 info.packed_data.last_day_of_month(possible_month - 1)
354 };
355
356 while day_of_year > last_day_of_month && possible_month <= 12 {
357 possible_month += 1;
358 last_day_of_prev_month = last_day_of_month;
359 last_day_of_month = info.packed_data.last_day_of_month(possible_month);
360 }
361 let day = u8::try_from(day_of_year - last_day_of_prev_month);
362 debug_assert!(
363 day.is_ok(),
364 "Found day {} that doesn't fit in month!",
365 day_of_year - last_day_of_prev_month
366 );
367 (possible_month, day.unwrap_or(29))
368}
369impl<'b, IB: IslamicBasedMarker> IslamicPrecomputedData<'b, IB> {
370 pub(crate) fn new(data: Option<&'b IslamicCacheV1<'b>>) -> Self {
371 Self {
372 data,
373 _ib: PhantomData,
374 }
375 }
376 fn load_or_compute_info_for_iso(&self, fixed: RataDie) -> (IslamicYearInfo, i32, u8, u8) {
379 let cached = self.data.and_then(|d| d.get_for_fixed::<IB>(fixed));
380 if let Some((cached, year)) = cached {
381 let ny = cached.packed_data.ny::<IB>(year);
382 let day_of_year = (fixed - ny) as u16 + 1;
383 debug_assert!(day_of_year < 360);
384 let possible_month = u8::try_from(1 + (day_of_year / 30)).unwrap_or(1);
388 let (m, d) = compute_month_day(cached, possible_month, day_of_year);
389 return (cached, year, m, d);
390 };
391 let (y, m, d) = IB::islamic_from_fixed(fixed);
394 let info = IslamicYearInfo::compute::<IB>(y);
395 let ny = info.packed_data.ny::<IB>(y);
396 let day_of_year = (fixed - ny) as u16 + 1;
397 let (m, d) = if m > 1 {
400 compute_month_day(info, m - 1, day_of_year)
401 } else {
402 (m, d)
403 };
404 (info, y, m, d)
405 }
406}
407
408#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
411pub struct IslamicDateInner(ArithmeticDate<IslamicObservational>);
412
413impl CalendarArithmetic for IslamicObservational {
414 type YearInfo = IslamicYearInfo;
415
416 fn month_days(_year: i32, month: u8, year_info: IslamicYearInfo) -> u8 {
417 year_info.packed_data.days_in_month(month)
418 }
419
420 fn months_for_every_year(_year: i32, _year_info: IslamicYearInfo) -> u8 {
421 12
422 }
423
424 fn days_in_provided_year(_year: i32, year_info: IslamicYearInfo) -> u16 {
425 year_info.packed_data.days_in_year()
426 }
427
428 fn is_leap_year(_year: i32, year_info: IslamicYearInfo) -> bool {
430 year_info.packed_data.days_in_year() != IslamicYearInfo::SHORT_YEAR_LEN
431 }
432
433 fn last_month_day_in_year(year: i32, year_info: IslamicYearInfo) -> (u8, u8) {
434 let days = Self::month_days(year, 12, year_info);
435
436 (12, days)
437 }
438}
439
440impl Calendar for IslamicObservational {
441 type DateInner = IslamicDateInner;
442 fn date_from_codes(
443 &self,
444 era: types::Era,
445 year: i32,
446 month_code: types::MonthCode,
447 day: u8,
448 ) -> Result<Self::DateInner, CalendarError> {
449 let year = if era.0 == tinystr!(16, "islamic") || era.0 == tinystr!(16, "ah") {
450 year
451 } else {
452 return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
453 };
454 let month = if let Some((ordinal, false)) = month_code.parsed() {
455 ordinal
456 } else {
457 return Err(CalendarError::UnknownMonthCode(
458 month_code.0,
459 self.debug_name(),
460 ));
461 };
462 ArithmeticDate::new_from_ordinals_with_info(
463 year,
464 month,
465 day,
466 self.precomputed_data().load_or_compute_info(year),
467 )
468 .map(IslamicDateInner)
469 }
470
471 fn date_from_iso(&self, iso: Date<crate::Iso>) -> Self::DateInner {
472 let fixed_iso = Iso::fixed_from_iso(*iso.inner());
473
474 let (year_info, y, m, d) = self
475 .precomputed_data()
476 .load_or_compute_info_for_iso(fixed_iso);
477 IslamicDateInner(ArithmeticDate::new_unchecked_with_info(y, m, d, year_info))
478 }
479
480 fn date_to_iso(&self, date: &Self::DateInner) -> Date<crate::Iso> {
481 let fixed = date.0.year_info.rd_for::<ObservationalIslamicMarker>(
482 date.0.year,
483 date.0.month,
484 date.0.day,
485 );
486 Iso::iso_from_fixed(fixed)
487 }
488
489 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
490 date.0.months_in_year()
491 }
492
493 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
494 date.0.days_in_year()
495 }
496
497 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
498 date.0.days_in_month()
499 }
500
501 fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
502 Iso.day_of_week(self.date_to_iso(date).inner())
503 }
504
505 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
506 date.0.offset_date(offset, &self.precomputed_data())
507 }
508
509 fn until(
510 &self,
511 date1: &Self::DateInner,
512 date2: &Self::DateInner,
513 _calendar2: &Self,
514 _largest_unit: DateDurationUnit,
515 _smallest_unit: DateDurationUnit,
516 ) -> DateDuration<Self> {
517 date1.0.until(date2.0, _largest_unit, _smallest_unit)
518 }
519
520 fn debug_name(&self) -> &'static str {
521 Self::DEBUG_NAME
522 }
523
524 fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
525 Self::year_as_islamic(date.0.year)
526 }
527
528 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
529 Self::is_leap_year(date.0.year, date.0.year_info)
530 }
531
532 fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
533 date.0.month()
534 }
535
536 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
537 date.0.day_of_month()
538 }
539
540 fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
541 let prev_year = date.0.year.saturating_sub(1);
542 let next_year = date.0.year.saturating_add(1);
543 types::DayOfYearInfo {
544 day_of_year: date.0.day_of_year(),
545 days_in_year: date.0.days_in_year(),
546 prev_year: Self::year_as_islamic(prev_year),
547 days_in_prev_year: date.0.year_info.days_in_prev_year(),
548 next_year: Self::year_as_islamic(next_year),
549 }
550 }
551
552 fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
553 Some(AnyCalendarKind::IslamicObservational)
554 }
555}
556
557impl IslamicObservational {
558 fn precomputed_data(&self) -> IslamicPrecomputedData<ObservationalIslamicMarker> {
559 IslamicPrecomputedData::new(self.data.as_ref().map(|x| x.get()))
560 }
561
562 fn year_as_islamic(year: i32) -> types::FormattableYear {
563 types::FormattableYear {
564 era: types::Era(tinystr!(16, "islamic")),
565 number: year,
566 cyclic: None,
567 related_iso: None,
568 }
569 }
570 pub(crate) const DEBUG_NAME: &'static str = "Islamic (observational)";
571}
572
573impl<A: AsCalendar<Calendar = IslamicObservational>> Date<A> {
574 pub fn try_new_observational_islamic_date(
593 year: i32,
594 month: u8,
595 day: u8,
596 calendar: A,
597 ) -> Result<Date<A>, CalendarError> {
598 let year_info = calendar
599 .as_calendar()
600 .precomputed_data()
601 .load_or_compute_info(year);
602 ArithmeticDate::new_from_ordinals_with_info(year, month, day, year_info)
603 .map(IslamicDateInner)
604 .map(|inner| Date::from_raw(inner, calendar))
605 }
606}
607
608impl<A: AsCalendar<Calendar = IslamicObservational>> DateTime<A> {
609 pub fn try_new_observational_islamic_datetime(
630 year: i32,
631 month: u8,
632 day: u8,
633 hour: u8,
634 minute: u8,
635 second: u8,
636 calendar: A,
637 ) -> Result<DateTime<A>, CalendarError> {
638 Ok(DateTime {
639 date: Date::try_new_observational_islamic_date(year, month, day, calendar)?,
640 time: Time::try_new(hour, minute, second, 0)?,
641 })
642 }
643}
644
645#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
646pub struct IslamicUmmAlQuraDateInner(ArithmeticDate<IslamicUmmAlQura>);
648
649impl CalendarArithmetic for IslamicUmmAlQura {
650 type YearInfo = IslamicYearInfo;
651
652 fn month_days(_year: i32, month: u8, year_info: IslamicYearInfo) -> u8 {
653 year_info.packed_data.days_in_month(month)
654 }
655
656 fn months_for_every_year(_year: i32, _year_info: IslamicYearInfo) -> u8 {
657 12
658 }
659
660 fn days_in_provided_year(_year: i32, year_info: IslamicYearInfo) -> u16 {
661 year_info.packed_data.days_in_year()
662 }
663
664 fn is_leap_year(_year: i32, year_info: IslamicYearInfo) -> bool {
666 year_info.packed_data.days_in_year() != IslamicYearInfo::SHORT_YEAR_LEN
667 }
668
669 fn last_month_day_in_year(year: i32, year_info: IslamicYearInfo) -> (u8, u8) {
670 let days = Self::month_days(year, 12, year_info);
671
672 (12, days)
673 }
674}
675
676impl Calendar for IslamicUmmAlQura {
677 type DateInner = IslamicUmmAlQuraDateInner;
678 fn date_from_codes(
679 &self,
680 era: types::Era,
681 year: i32,
682 month_code: types::MonthCode,
683 day: u8,
684 ) -> Result<Self::DateInner, CalendarError> {
685 let year = if era.0 == tinystr!(16, "islamic-umalqura")
686 || era.0 == tinystr!(16, "islamic")
687 || era.0 == tinystr!(16, "ah")
688 {
689 year
690 } else {
691 return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
692 };
693
694 let month = if let Some((ordinal, false)) = month_code.parsed() {
695 ordinal
696 } else {
697 return Err(CalendarError::UnknownMonthCode(
698 month_code.0,
699 self.debug_name(),
700 ));
701 };
702 ArithmeticDate::new_from_ordinals_with_info(
703 year,
704 month,
705 day,
706 self.precomputed_data().load_or_compute_info(year),
707 )
708 .map(IslamicUmmAlQuraDateInner)
709 }
710
711 fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
712 let fixed_iso = Iso::fixed_from_iso(*iso.inner());
713
714 let (year_info, y, m, d) = self
715 .precomputed_data()
716 .load_or_compute_info_for_iso(fixed_iso);
717 IslamicUmmAlQuraDateInner(ArithmeticDate::new_unchecked_with_info(y, m, d, year_info))
718 }
719
720 fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
721 let fixed =
722 date.0
723 .year_info
724 .rd_for::<SaudiIslamicMarker>(date.0.year, date.0.month, date.0.day);
725 Iso::iso_from_fixed(fixed)
726 }
727
728 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
729 date.0.months_in_year()
730 }
731
732 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
733 date.0.days_in_year()
734 }
735
736 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
737 date.0.days_in_month()
738 }
739
740 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
741 date.0.offset_date(offset, &self.precomputed_data())
742 }
743
744 fn until(
745 &self,
746 date1: &Self::DateInner,
747 date2: &Self::DateInner,
748 _calendar2: &Self,
749 _largest_unit: DateDurationUnit,
750 _smallest_unit: DateDurationUnit,
751 ) -> DateDuration<Self> {
752 date1.0.until(date2.0, _largest_unit, _smallest_unit)
753 }
754
755 fn debug_name(&self) -> &'static str {
756 Self::DEBUG_NAME
757 }
758
759 fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
760 Self::year_as_islamic(date.0.year)
761 }
762
763 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
764 Self::is_leap_year(date.0.year, date.0.year_info)
765 }
766
767 fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
768 date.0.month()
769 }
770
771 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
772 date.0.day_of_month()
773 }
774
775 fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
776 let prev_year = date.0.year.saturating_sub(1);
777 let next_year = date.0.year.saturating_add(1);
778 types::DayOfYearInfo {
779 day_of_year: date.0.day_of_year(),
780 days_in_year: date.0.days_in_year(),
781 prev_year: Self::year_as_islamic(prev_year),
782 days_in_prev_year: date.0.year_info.days_in_prev_year(),
783 next_year: Self::year_as_islamic(next_year),
784 }
785 }
786
787 fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
788 Some(AnyCalendarKind::IslamicUmmAlQura)
789 }
790}
791
792impl IslamicUmmAlQura {
793 fn precomputed_data(&self) -> IslamicPrecomputedData<SaudiIslamicMarker> {
794 IslamicPrecomputedData::new(self.data.as_ref().map(|x| x.get()))
795 }
796
797 fn year_as_islamic(year: i32) -> types::FormattableYear {
798 types::FormattableYear {
799 era: types::Era(tinystr!(16, "islamic")),
800 number: year,
801 cyclic: None,
802 related_iso: None,
803 }
804 }
805 pub(crate) const DEBUG_NAME: &'static str = "Islamic (Umm al-Qura)";
806}
807
808impl<A: AsCalendar<Calendar = IslamicUmmAlQura>> Date<A> {
809 pub fn try_new_ummalqura_date(
827 year: i32,
828 month: u8,
829 day: u8,
830 calendar: A,
831 ) -> Result<Date<A>, CalendarError> {
832 let year_info = calendar
833 .as_calendar()
834 .precomputed_data()
835 .load_or_compute_info(year);
836 ArithmeticDate::new_from_ordinals_with_info(year, month, day, year_info)
837 .map(IslamicUmmAlQuraDateInner)
838 .map(|inner| Date::from_raw(inner, calendar))
839 }
840}
841
842impl<A: AsCalendar<Calendar = IslamicUmmAlQura>> DateTime<A> {
843 pub fn try_new_ummalqura_datetime(
863 year: i32,
864 month: u8,
865 day: u8,
866 hour: u8,
867 minute: u8,
868 second: u8,
869 calendar: A,
870 ) -> Result<DateTime<A>, CalendarError> {
871 Ok(DateTime {
872 date: Date::try_new_ummalqura_date(year, month, day, calendar)?,
873 time: Time::try_new(hour, minute, second, 0)?,
874 })
875 }
876}
877
878#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
881pub struct IslamicCivilDateInner(ArithmeticDate<IslamicCivil>);
882
883impl CalendarArithmetic for IslamicCivil {
884 type YearInfo = ();
885
886 fn month_days(year: i32, month: u8, _data: ()) -> u8 {
887 match month {
888 1 | 3 | 5 | 7 | 9 | 11 => 30,
889 2 | 4 | 6 | 8 | 10 => 29,
890 12 if Self::is_leap_year(year, ()) => 30,
891 12 => 29,
892 _ => 0,
893 }
894 }
895
896 fn months_for_every_year(_year: i32, _data: ()) -> u8 {
897 12
898 }
899
900 fn days_in_provided_year(year: i32, _data: ()) -> u16 {
901 if Self::is_leap_year(year, ()) {
902 IslamicYearInfo::LONG_YEAR_LEN
903 } else {
904 IslamicYearInfo::SHORT_YEAR_LEN
905 }
906 }
907
908 fn is_leap_year(year: i32, _data: ()) -> bool {
909 (14 + 11 * year).rem_euclid(30) < 11
910 }
911
912 fn last_month_day_in_year(year: i32, _data: ()) -> (u8, u8) {
913 if Self::is_leap_year(year, ()) {
914 (12, 30)
915 } else {
916 (12, 29)
917 }
918 }
919}
920
921impl Calendar for IslamicCivil {
922 type DateInner = IslamicCivilDateInner;
923
924 fn date_from_codes(
925 &self,
926 era: types::Era,
927 year: i32,
928 month_code: types::MonthCode,
929 day: u8,
930 ) -> Result<Self::DateInner, CalendarError> {
931 let year = if era.0 == tinystr!(16, "islamic-civil")
932 || era.0 == tinystr!(16, "islamicc")
933 || era.0 == tinystr!(16, "islamic")
934 || era.0 == tinystr!(16, "ah")
935 {
936 year
938 } else {
939 return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
940 };
941
942 ArithmeticDate::new_from_codes(self, year, month_code, day).map(IslamicCivilDateInner)
943 }
944
945 fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
946 let fixed_iso = Iso::fixed_from_iso(*iso.inner());
947 Self::islamic_from_fixed(fixed_iso).inner
948 }
949
950 fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
951 let fixed_islamic = Self::fixed_from_islamic(*date);
952 Iso::iso_from_fixed(fixed_islamic)
953 }
954
955 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
956 date.0.months_in_year()
957 }
958
959 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
960 date.0.days_in_year()
961 }
962
963 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
964 date.0.days_in_month()
965 }
966
967 fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
968 Iso.day_of_week(self.date_to_iso(date).inner())
969 }
970
971 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
972 date.0.offset_date(offset, &())
973 }
974
975 fn until(
976 &self,
977 date1: &Self::DateInner,
978 date2: &Self::DateInner,
979 _calendar2: &Self,
980 _largest_unit: DateDurationUnit,
981 _smallest_unit: DateDurationUnit,
982 ) -> DateDuration<Self> {
983 date1.0.until(date2.0, _largest_unit, _smallest_unit)
984 }
985
986 fn debug_name(&self) -> &'static str {
987 "Islamic (civil)"
988 }
989
990 fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
991 Self::year_as_islamic(date.0.year)
992 }
993
994 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
995 Self::is_leap_year(date.0.year, ())
996 }
997
998 fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
999 date.0.month()
1000 }
1001
1002 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
1003 date.0.day_of_month()
1004 }
1005
1006 fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
1007 let prev_year = date.0.year.saturating_sub(1);
1008 let next_year = date.0.year.saturating_add(1);
1009 types::DayOfYearInfo {
1010 day_of_year: date.0.day_of_year(),
1011 days_in_year: date.0.days_in_year(),
1012 prev_year: Self::year_as_islamic(prev_year),
1013 days_in_prev_year: Self::days_in_provided_year(prev_year, ()),
1014 next_year: Self::year_as_islamic(next_year),
1015 }
1016 }
1017 fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
1018 Some(AnyCalendarKind::IslamicCivil)
1019 }
1020}
1021
1022impl IslamicCivil {
1023 fn fixed_from_islamic(i_date: IslamicCivilDateInner) -> RataDie {
1024 calendrical_calculations::islamic::fixed_from_islamic_civil(
1025 i_date.0.year,
1026 i_date.0.month,
1027 i_date.0.day,
1028 )
1029 }
1030
1031 fn islamic_from_fixed(date: RataDie) -> Date<IslamicCivil> {
1032 let (y, m, d) = calendrical_calculations::islamic::islamic_civil_from_fixed(date);
1033
1034 debug_assert!(
1035 Date::try_new_islamic_civil_date_with_calendar(y, m, d, IslamicCivil).is_ok()
1036 );
1037 Date::from_raw(
1038 IslamicCivilDateInner(ArithmeticDate::new_unchecked(y, m, d)),
1039 IslamicCivil,
1040 )
1041 }
1042
1043 fn year_as_islamic(year: i32) -> types::FormattableYear {
1044 types::FormattableYear {
1045 era: types::Era(tinystr!(16, "islamic")),
1046 number: year,
1047 cyclic: None,
1048 related_iso: None,
1049 }
1050 }
1051}
1052
1053impl<A: AsCalendar<Calendar = IslamicCivil>> Date<A> {
1054 pub fn try_new_islamic_civil_date_with_calendar(
1073 year: i32,
1074 month: u8,
1075 day: u8,
1076 calendar: A,
1077 ) -> Result<Date<A>, CalendarError> {
1078 ArithmeticDate::new_from_ordinals(year, month, day)
1079 .map(IslamicCivilDateInner)
1080 .map(|inner| Date::from_raw(inner, calendar))
1081 }
1082}
1083
1084impl<A: AsCalendar<Calendar = IslamicCivil>> DateTime<A> {
1085 pub fn try_new_islamic_civil_datetime_with_calendar(
1107 year: i32,
1108 month: u8,
1109 day: u8,
1110 hour: u8,
1111 minute: u8,
1112 second: u8,
1113 calendar: A,
1114 ) -> Result<DateTime<A>, CalendarError> {
1115 Ok(DateTime {
1116 date: Date::try_new_islamic_civil_date_with_calendar(year, month, day, calendar)?,
1117 time: Time::try_new(hour, minute, second, 0)?,
1118 })
1119 }
1120}
1121
1122#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
1125pub struct IslamicTabularDateInner(ArithmeticDate<IslamicTabular>);
1126
1127impl CalendarArithmetic for IslamicTabular {
1128 type YearInfo = ();
1129
1130 fn month_days(year: i32, month: u8, _data: ()) -> u8 {
1131 match month {
1132 1 | 3 | 5 | 7 | 9 | 11 => 30,
1133 2 | 4 | 6 | 8 | 10 => 29,
1134 12 if Self::is_leap_year(year, ()) => 30,
1135 12 => 29,
1136 _ => 0,
1137 }
1138 }
1139
1140 fn months_for_every_year(_year: i32, _data: ()) -> u8 {
1141 12
1142 }
1143
1144 fn days_in_provided_year(year: i32, _data: ()) -> u16 {
1145 if Self::is_leap_year(year, ()) {
1146 IslamicYearInfo::LONG_YEAR_LEN
1147 } else {
1148 IslamicYearInfo::SHORT_YEAR_LEN
1149 }
1150 }
1151
1152 fn is_leap_year(year: i32, _data: ()) -> bool {
1153 (14 + 11 * year).rem_euclid(30) < 11
1154 }
1155
1156 fn last_month_day_in_year(year: i32, _data: ()) -> (u8, u8) {
1157 if Self::is_leap_year(year, ()) {
1158 (12, 30)
1159 } else {
1160 (12, 29)
1161 }
1162 }
1163}
1164
1165impl Calendar for IslamicTabular {
1166 type DateInner = IslamicTabularDateInner;
1167
1168 fn date_from_codes(
1169 &self,
1170 era: types::Era,
1171 year: i32,
1172 month_code: types::MonthCode,
1173 day: u8,
1174 ) -> Result<Self::DateInner, CalendarError> {
1175 let year = if era.0 == tinystr!(16, "islamic-tbla")
1176 || era.0 == tinystr!(16, "islamic")
1177 || era.0 == tinystr!(16, "ah")
1178 {
1179 year
1180 } else {
1181 return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
1182 };
1183
1184 ArithmeticDate::new_from_codes(self, year, month_code, day).map(IslamicTabularDateInner)
1185 }
1186
1187 fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
1188 let fixed_iso = Iso::fixed_from_iso(*iso.inner());
1189 Self::islamic_from_fixed(fixed_iso).inner
1190 }
1191
1192 fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
1193 let fixed_islamic = Self::fixed_from_islamic(*date);
1194 Iso::iso_from_fixed(fixed_islamic)
1195 }
1196
1197 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
1198 date.0.months_in_year()
1199 }
1200
1201 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
1202 date.0.days_in_year()
1203 }
1204
1205 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
1206 date.0.days_in_month()
1207 }
1208
1209 fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
1210 Iso.day_of_week(self.date_to_iso(date).inner())
1211 }
1212
1213 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
1214 date.0.offset_date(offset, &())
1215 }
1216
1217 fn until(
1218 &self,
1219 date1: &Self::DateInner,
1220 date2: &Self::DateInner,
1221 _calendar2: &Self,
1222 _largest_unit: DateDurationUnit,
1223 _smallest_unit: DateDurationUnit,
1224 ) -> DateDuration<Self> {
1225 date1.0.until(date2.0, _largest_unit, _smallest_unit)
1226 }
1227
1228 fn debug_name(&self) -> &'static str {
1229 "Islamic (tabular)"
1230 }
1231
1232 fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
1233 Self::year_as_islamic(date.0.year)
1234 }
1235
1236 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
1237 Self::is_leap_year(date.0.year, ())
1238 }
1239
1240 fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
1241 date.0.month()
1242 }
1243
1244 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
1245 date.0.day_of_month()
1246 }
1247
1248 fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
1249 let prev_year = date.0.year.saturating_sub(1);
1250 let next_year = date.0.year.saturating_add(1);
1251 types::DayOfYearInfo {
1252 day_of_year: date.0.day_of_year(),
1253 days_in_year: date.0.days_in_year(),
1254 prev_year: Self::year_as_islamic(prev_year),
1255 days_in_prev_year: Self::days_in_provided_year(prev_year, ()),
1256 next_year: Self::year_as_islamic(next_year),
1257 }
1258 }
1259 fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
1260 Some(AnyCalendarKind::IslamicTabular)
1261 }
1262}
1263
1264impl IslamicTabular {
1265 fn fixed_from_islamic(i_date: IslamicTabularDateInner) -> RataDie {
1266 calendrical_calculations::islamic::fixed_from_islamic_tabular(
1267 i_date.0.year,
1268 i_date.0.month,
1269 i_date.0.day,
1270 )
1271 }
1272
1273 fn islamic_from_fixed(date: RataDie) -> Date<IslamicTabular> {
1274 let (y, m, d) = calendrical_calculations::islamic::islamic_tabular_from_fixed(date);
1275
1276 debug_assert!(
1277 Date::try_new_islamic_civil_date_with_calendar(y, m, d, IslamicCivil).is_ok()
1278 );
1279 Date::from_raw(
1280 IslamicTabularDateInner(ArithmeticDate::new_unchecked(y, m, d)),
1281 IslamicTabular,
1282 )
1283 }
1284
1285 fn year_as_islamic(year: i32) -> types::FormattableYear {
1286 types::FormattableYear {
1287 era: types::Era(tinystr!(16, "islamic")),
1288 number: year,
1289 cyclic: None,
1290 related_iso: None,
1291 }
1292 }
1293}
1294
1295impl<A: AsCalendar<Calendar = IslamicTabular>> Date<A> {
1296 pub fn try_new_islamic_tabular_date_with_calendar(
1315 year: i32,
1316 month: u8,
1317 day: u8,
1318 calendar: A,
1319 ) -> Result<Date<A>, CalendarError> {
1320 ArithmeticDate::new_from_ordinals(year, month, day)
1321 .map(IslamicTabularDateInner)
1322 .map(|inner| Date::from_raw(inner, calendar))
1323 }
1324}
1325
1326impl<A: AsCalendar<Calendar = IslamicTabular>> DateTime<A> {
1327 pub fn try_new_islamic_tabular_datetime_with_calendar(
1349 year: i32,
1350 month: u8,
1351 day: u8,
1352 hour: u8,
1353 minute: u8,
1354 second: u8,
1355 calendar: A,
1356 ) -> Result<DateTime<A>, CalendarError> {
1357 Ok(DateTime {
1358 date: Date::try_new_islamic_tabular_date_with_calendar(year, month, day, calendar)?,
1359 time: Time::try_new(hour, minute, second, 0)?,
1360 })
1361 }
1362}
1363
1364#[cfg(test)]
1365mod 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 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 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(); let tolerance = 1; 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 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(); assert_eq!(sum_days_in_year, expected_number_of_days);
2218 }
2219
2220 #[test]
2221 fn test_regression_3868() {
2222 let iso = Date::try_new_iso_date(2011, 4, 4).unwrap();
2224 let islamic = iso.to_calendar(IslamicUmmAlQura::new());
2225 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 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}