Coverage Report

Created: 2025-07-12 07:16

/rust/registry/src/index.crates.io-6f17d22bba15001f/icu_calendar-1.5.2/src/persian.rs
Line
Count
Source (jump to first uncovered line)
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 Persian calendar.
6
//!
7
//! ```rust
8
//! use icu::calendar::{Date, DateTime};
9
//!
10
//! // `Date` type
11
//! let persian_date = Date::try_new_persian_date(1348, 10, 11)
12
//!     .expect("Failed to initialize Persian Date instance.");
13
//!
14
//! // `DateTime` type
15
//! let persian_datetime =
16
//!     DateTime::try_new_persian_datetime(1348, 10, 11, 13, 1, 0)
17
//!         .expect("Failed to initialize Persian DateTime instance.");
18
//!
19
//! // `Date` checks
20
//! assert_eq!(persian_date.year().number, 1348);
21
//! assert_eq!(persian_date.month().ordinal, 10);
22
//! assert_eq!(persian_date.day_of_month().0, 11);
23
//!
24
//! // `DateTime` checks
25
//! assert_eq!(persian_datetime.date.year().number, 1348);
26
//! assert_eq!(persian_datetime.date.month().ordinal, 10);
27
//! assert_eq!(persian_datetime.date.day_of_month().0, 11);
28
//! assert_eq!(persian_datetime.time.hour.number(), 13);
29
//! assert_eq!(persian_datetime.time.minute.number(), 1);
30
//! assert_eq!(persian_datetime.time.second.number(), 0);
31
//! ```
32
33
use crate::any_calendar::AnyCalendarKind;
34
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
35
use crate::iso::Iso;
36
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time};
37
use ::tinystr::tinystr;
38
use calendrical_calculations::helpers::I32CastError;
39
use calendrical_calculations::rata_die::RataDie;
40
41
/// The Persian Calendar
42
///
43
/// The [Persian Calendar] is a solar calendar used officially by the countries of Iran and Afghanistan and many Persian-speaking regions.
44
/// It has 12 months and other similarities to the Gregorian Calendar
45
///
46
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
47
///
48
/// [Persian Calendar]: https://en.wikipedia.org/wiki/Solar_Hijri_calendar
49
///
50
/// # Era codes
51
///
52
/// This calendar supports only one era code, which starts from the year of the Hijra, designated as "ah".
53
///
54
/// # Month codes
55
///
56
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
57
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
58
#[allow(clippy::exhaustive_structs)]
59
pub struct Persian;
60
61
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
62
63
/// The inner date type used for representing [`Date`]s of [`Persian`]. See [`Date`] and [`Persian`] for more details.
64
pub struct PersianDateInner(ArithmeticDate<Persian>);
65
66
impl CalendarArithmetic for Persian {
67
    type YearInfo = ();
68
69
0
    fn month_days(year: i32, month: u8, _data: ()) -> u8 {
70
0
        match month {
71
0
            1..=6 => 31,
72
0
            7..=11 => 30,
73
0
            12 if Self::is_leap_year(year, ()) => 30,
74
0
            12 => 29,
75
0
            _ => 0,
76
        }
77
0
    }
78
79
0
    fn months_for_every_year(_: i32, _data: ()) -> u8 {
80
0
        12
81
0
    }
82
83
0
    fn is_leap_year(p_year: i32, _data: ()) -> bool {
84
0
        calendrical_calculations::persian::is_leap_year(p_year, _data)
85
0
    }
86
87
0
    fn days_in_provided_year(year: i32, _data: ()) -> u16 {
88
0
        if Self::is_leap_year(year, ()) {
89
0
            366
90
        } else {
91
0
            365
92
        }
93
0
    }
94
95
0
    fn last_month_day_in_year(year: i32, _data: ()) -> (u8, u8) {
96
0
        if Self::is_leap_year(year, ()) {
97
0
            (12, 30)
98
        } else {
99
0
            (12, 29)
100
        }
101
0
    }
102
}
103
104
impl Calendar for Persian {
105
    type DateInner = PersianDateInner;
106
0
    fn date_from_codes(
107
0
        &self,
108
0
        era: types::Era,
109
0
        year: i32,
110
0
        month_code: types::MonthCode,
111
0
        day: u8,
112
0
    ) -> Result<Self::DateInner, CalendarError> {
113
0
        let year = if era.0 == tinystr!(16, "ah") || era.0 == tinystr!(16, "persian") {
114
0
            year
115
        } else {
116
0
            return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
117
        };
118
119
0
        ArithmeticDate::new_from_codes(self, year, month_code, day).map(PersianDateInner)
120
0
    }
121
122
0
    fn date_from_iso(&self, iso: Date<Iso>) -> PersianDateInner {
123
0
        let fixed_iso = Iso::fixed_from_iso(*iso.inner());
124
0
        Self::fast_persian_from_fixed(fixed_iso)
125
0
    }
126
127
0
    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
128
0
        let fixed_persian = Persian::fixed_from_fast_persian(*date);
129
0
        Iso::iso_from_fixed(fixed_persian)
130
0
    }
131
132
0
    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
133
0
        date.0.months_in_year()
134
0
    }
135
136
0
    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
137
0
        date.0.days_in_year()
138
0
    }
139
140
0
    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
141
0
        date.0.days_in_month()
142
0
    }
143
144
0
    fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
145
0
        Iso.day_of_week(self.date_to_iso(date).inner())
146
0
    }
147
148
0
    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
149
0
        date.0.offset_date(offset, &())
150
0
    }
151
152
    #[allow(clippy::field_reassign_with_default)]
153
0
    fn until(
154
0
        &self,
155
0
        date1: &Self::DateInner,
156
0
        date2: &Self::DateInner,
157
0
        _calendar2: &Self,
158
0
        _largest_unit: DateDurationUnit,
159
0
        _smallest_unit: DateDurationUnit,
160
0
    ) -> DateDuration<Self> {
161
0
        date1.0.until(date2.0, _largest_unit, _smallest_unit)
162
0
    }
163
164
0
    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
165
0
        Self::year_as_persian(date.0.year)
166
0
    }
167
168
0
    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
169
0
        Self::is_leap_year(date.0.year, ())
170
0
    }
171
172
0
    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
173
0
        date.0.month()
174
0
    }
175
176
0
    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
177
0
        date.0.day_of_month()
178
0
    }
179
180
0
    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
181
0
        let prev_year = date.0.year.saturating_sub(1);
182
0
        let next_year = date.0.year.saturating_add(1);
183
0
        types::DayOfYearInfo {
184
0
            day_of_year: date.0.day_of_year(),
185
0
            days_in_year: date.0.days_in_year(),
186
0
            prev_year: Persian::year_as_persian(prev_year),
187
0
            days_in_prev_year: Persian::days_in_provided_year(prev_year, ()),
188
0
            next_year: Persian::year_as_persian(next_year),
189
0
        }
190
0
    }
191
192
0
    fn debug_name(&self) -> &'static str {
193
0
        "Persian"
194
0
    }
195
    // Missing any_calendar persian tests, the rest is completed
196
0
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
197
0
        Some(AnyCalendarKind::Persian)
198
0
    }
199
}
200
201
impl Persian {
202
    /// Constructs a new Persian Calendar
203
0
    pub fn new() -> Self {
204
0
        Self
205
0
    }
206
207
0
    fn fixed_from_fast_persian(p_date: PersianDateInner) -> RataDie {
208
0
        calendrical_calculations::persian::fixed_from_fast_persian(
209
0
            p_date.0.year,
210
0
            p_date.0.month,
211
0
            p_date.0.day,
212
0
        )
213
0
    }
214
0
    fn fast_persian_from_fixed(date: RataDie) -> PersianDateInner {
215
0
        let (year, month, day) =
216
0
            match calendrical_calculations::persian::fast_persian_from_fixed(date) {
217
0
                Err(I32CastError::BelowMin) => return PersianDateInner(ArithmeticDate::min_date()),
218
0
                Err(I32CastError::AboveMax) => return PersianDateInner(ArithmeticDate::max_date()),
219
0
                Ok(ymd) => ymd,
220
0
            };
221
0
222
0
        PersianDateInner(ArithmeticDate::new_unchecked(year, month, day))
223
0
    }
224
225
0
    fn year_as_persian(year: i32) -> types::FormattableYear {
226
0
        types::FormattableYear {
227
0
            era: types::Era(tinystr!(16, "ah")),
228
0
            number: year,
229
0
            cyclic: None,
230
0
            related_iso: None,
231
0
        }
232
0
    }
233
}
234
235
impl Date<Persian> {
236
    /// Construct new Persian Date.
237
    ///
238
    /// Has no negative years, only era is the AH/AP.
239
    ///
240
    /// ```rust
241
    /// use icu::calendar::Date;
242
    ///
243
    /// let date_persian = Date::try_new_persian_date(1392, 4, 25)
244
    ///     .expect("Failed to initialize Persian Date instance.");
245
    ///
246
    /// assert_eq!(date_persian.year().number, 1392);
247
    /// assert_eq!(date_persian.month().ordinal, 4);
248
    /// assert_eq!(date_persian.day_of_month().0, 25);
249
    /// ```
250
0
    pub fn try_new_persian_date(
251
0
        year: i32,
252
0
        month: u8,
253
0
        day: u8,
254
0
    ) -> Result<Date<Persian>, CalendarError> {
255
0
        ArithmeticDate::new_from_ordinals(year, month, day)
256
0
            .map(PersianDateInner)
257
0
            .map(|inner| Date::from_raw(inner, Persian))
258
0
    }
259
}
260
261
impl DateTime<Persian> {
262
    /// Construct a new Persian datetime from integers.
263
    ///
264
    /// ```rust
265
    /// use icu::calendar::DateTime;
266
    ///
267
    /// let datetime_persian =
268
    ///     DateTime::try_new_persian_datetime(474, 10, 11, 13, 1, 0)
269
    ///         .expect("Failed to initialize Persian DateTime instance.");
270
    ///
271
    /// assert_eq!(datetime_persian.date.year().number, 474);
272
    /// assert_eq!(datetime_persian.date.month().ordinal, 10);
273
    /// assert_eq!(datetime_persian.date.day_of_month().0, 11);
274
    /// assert_eq!(datetime_persian.time.hour.number(), 13);
275
    /// assert_eq!(datetime_persian.time.minute.number(), 1);
276
    /// assert_eq!(datetime_persian.time.second.number(), 0);
277
    /// ```
278
0
    pub fn try_new_persian_datetime(
279
0
        year: i32,
280
0
        month: u8,
281
0
        day: u8,
282
0
        hour: u8,
283
0
        minute: u8,
284
0
        second: u8,
285
0
    ) -> Result<DateTime<Persian>, CalendarError> {
286
0
        Ok(DateTime {
287
0
            date: Date::try_new_persian_date(year, month, day)?,
288
0
            time: Time::try_new(hour, minute, second, 0)?,
289
        })
290
0
    }
291
}
292
293
#[cfg(test)]
294
mod tests {
295
    use super::*;
296
    #[derive(Debug)]
297
    struct DateCase {
298
        year: i32,
299
        month: u8,
300
        day: u8,
301
    }
302
303
    static TEST_FIXED_DATE: [i64; 21] = [
304
        656786, 664224, 671401, 694799, 702806, 704424, 708842, 709409, 709580, 727274, 728714,
305
        739330, 739331, 744313, 763436, 763437, 764652, 775123, 775488, 775489, 1317874,
306
    ];
307
308
    // Test data are provided for the range 1178-3000 AP, for which
309
    // we know the 33-year rule, with the override table, matches the
310
    // astronomical calculations based on the 52.5 degrees east meridian.
311
    static CASES: [DateCase; 21] = [
312
        // First year for which 33-year rule matches the astronomical calculation
313
        DateCase {
314
            year: 1178,
315
            month: 1,
316
            day: 1,
317
        },
318
        DateCase {
319
            year: 1198,
320
            month: 5,
321
            day: 10,
322
        },
323
        DateCase {
324
            year: 1218,
325
            month: 1,
326
            day: 7,
327
        },
328
        DateCase {
329
            year: 1282,
330
            month: 1,
331
            day: 29,
332
        },
333
        // The beginning of the year the calendar was adopted
334
        DateCase {
335
            year: 1304,
336
            month: 1,
337
            day: 1,
338
        },
339
        DateCase {
340
            year: 1308,
341
            month: 6,
342
            day: 3,
343
        },
344
        DateCase {
345
            year: 1320,
346
            month: 7,
347
            day: 7,
348
        },
349
        DateCase {
350
            year: 1322,
351
            month: 1,
352
            day: 29,
353
        },
354
        DateCase {
355
            year: 1322,
356
            month: 7,
357
            day: 14,
358
        },
359
        DateCase {
360
            year: 1370,
361
            month: 12,
362
            day: 27,
363
        },
364
        DateCase {
365
            year: 1374,
366
            month: 12,
367
            day: 6,
368
        },
369
        // First day that the 2820-year rule fails
370
        DateCase {
371
            year: 1403,
372
            month: 12,
373
            day: 30,
374
        },
375
        // First Nowruz that the 2820-year rule fails
376
        DateCase {
377
            year: 1404,
378
            month: 1,
379
            day: 1,
380
        },
381
        DateCase {
382
            year: 1417,
383
            month: 8,
384
            day: 19,
385
        },
386
        // First day the unmodified astronomical algorithm fails
387
        DateCase {
388
            year: 1469,
389
            month: 12,
390
            day: 30,
391
        },
392
        // First Nowruz the unmodified astronomical algorithm fails
393
        DateCase {
394
            year: 1470,
395
            month: 1,
396
            day: 1,
397
        },
398
        DateCase {
399
            year: 1473,
400
            month: 4,
401
            day: 28,
402
        },
403
        // Last year the 33-year rule matches the modified astronomical calculation
404
        DateCase {
405
            year: 1501,
406
            month: 12,
407
            day: 29,
408
        },
409
        DateCase {
410
            year: 1502,
411
            month: 12,
412
            day: 29,
413
        },
414
        DateCase {
415
            year: 1503,
416
            month: 1,
417
            day: 1,
418
        },
419
        DateCase {
420
            year: 2988,
421
            month: 1,
422
            day: 1,
423
        },
424
    ];
425
426
    fn days_in_provided_year_core(year: i32) -> u16 {
427
        let fixed_year =
428
            calendrical_calculations::persian::fixed_from_fast_persian(year, 1, 1).to_i64_date();
429
        let next_fixed_year =
430
            calendrical_calculations::persian::fixed_from_fast_persian(year + 1, 1, 1)
431
                .to_i64_date();
432
433
        (next_fixed_year - fixed_year) as u16
434
    }
435
436
    #[test]
437
    fn test_persian_leap_year() {
438
        let mut leap_years: [i32; 21] = [0; 21];
439
        // These values were computed from the "Calendrical Calculations" reference code output
440
        let expected_values = [
441
            false, false, true, false, true, false, false, false, false, true, false, true, false,
442
            false, true, false, false, false, false, true, true,
443
        ];
444
445
        for (index, case) in CASES.iter().enumerate() {
446
            leap_years[index] = case.year;
447
        }
448
        for (year, bool) in leap_years.iter().zip(expected_values.iter()) {
449
            assert_eq!(Persian::is_leap_year(*year, ()), *bool);
450
        }
451
    }
452
453
    #[test]
454
    fn days_in_provided_year_test() {
455
        for case in CASES.iter() {
456
            assert_eq!(
457
                days_in_provided_year_core(case.year),
458
                Persian::days_in_provided_year(case.year, ())
459
            );
460
        }
461
    }
462
463
    #[test]
464
    fn test_fixed_from_persian() {
465
        for (case, f_date) in CASES.iter().zip(TEST_FIXED_DATE.iter()) {
466
            let date = Date::try_new_persian_date(case.year, case.month, case.day).unwrap();
467
468
            assert_eq!(
469
                Persian::fixed_from_fast_persian(*date.inner()).to_i64_date(),
470
                *f_date,
471
                "{case:?}"
472
            );
473
        }
474
    }
475
    #[test]
476
    fn test_persian_from_fixed() {
477
        for (case, f_date) in CASES.iter().zip(TEST_FIXED_DATE.iter()) {
478
            let date = Date::try_new_persian_date(case.year, case.month, case.day).unwrap();
479
            assert_eq!(
480
                Persian::fast_persian_from_fixed(RataDie::new(*f_date)),
481
                date.inner,
482
                "{case:?}"
483
            );
484
        }
485
    }
486
487
    #[test]
488
    fn test_day_of_year_info() {
489
        #[derive(Debug)]
490
        struct TestCase {
491
            input: i32,
492
            expected_prev: i32,
493
            expected_next: i32,
494
        }
495
496
        let test_cases = [
497
            TestCase {
498
                input: 0,
499
                expected_prev: -1,
500
                expected_next: 1,
501
            },
502
            TestCase {
503
                input: i32::MAX,
504
                expected_prev: i32::MAX - 1,
505
                expected_next: i32::MAX, // can't go above i32::MAX
506
            },
507
            TestCase {
508
                input: i32::MIN + 1,
509
                expected_prev: i32::MIN,
510
                expected_next: i32::MIN + 2,
511
            },
512
            TestCase {
513
                input: i32::MIN,
514
                expected_prev: i32::MIN, // can't go below i32::MIN
515
                expected_next: i32::MIN + 1,
516
            },
517
        ];
518
519
        for case in test_cases {
520
            let date = Date::try_new_persian_date(case.input, 1, 1).unwrap();
521
            let info = Persian::day_of_year_info(&Persian, date.inner());
522
523
            assert_eq!(info.prev_year.number, case.expected_prev, "{:?}", case);
524
            assert_eq!(info.next_year.number, case.expected_next, "{:?}", case);
525
        }
526
    }
527
528
    // From https://calendar.ut.ac.ir/Fa/News/Data/Doc/KabiseShamsi1206-1498-new.pdf
529
    // Plain text version at https://github.com/roozbehp/persiancalendar/blob/main/kabise.txt
530
    static CALENDAR_UT_AC_IR_TEST_DATA: [(i32, bool, i32, u32, u32); 293] = [
531
        (1206, false, 1827, 3, 22),
532
        (1207, false, 1828, 3, 21),
533
        (1208, false, 1829, 3, 21),
534
        (1209, false, 1830, 3, 21),
535
        (1210, true, 1831, 3, 21),
536
        (1211, false, 1832, 3, 21),
537
        (1212, false, 1833, 3, 21),
538
        (1213, false, 1834, 3, 21),
539
        (1214, true, 1835, 3, 21),
540
        (1215, false, 1836, 3, 21),
541
        (1216, false, 1837, 3, 21),
542
        (1217, false, 1838, 3, 21),
543
        (1218, true, 1839, 3, 21),
544
        (1219, false, 1840, 3, 21),
545
        (1220, false, 1841, 3, 21),
546
        (1221, false, 1842, 3, 21),
547
        (1222, true, 1843, 3, 21),
548
        (1223, false, 1844, 3, 21),
549
        (1224, false, 1845, 3, 21),
550
        (1225, false, 1846, 3, 21),
551
        (1226, true, 1847, 3, 21),
552
        (1227, false, 1848, 3, 21),
553
        (1228, false, 1849, 3, 21),
554
        (1229, false, 1850, 3, 21),
555
        (1230, true, 1851, 3, 21),
556
        (1231, false, 1852, 3, 21),
557
        (1232, false, 1853, 3, 21),
558
        (1233, false, 1854, 3, 21),
559
        (1234, true, 1855, 3, 21),
560
        (1235, false, 1856, 3, 21),
561
        (1236, false, 1857, 3, 21),
562
        (1237, false, 1858, 3, 21),
563
        (1238, true, 1859, 3, 21),
564
        (1239, false, 1860, 3, 21),
565
        (1240, false, 1861, 3, 21),
566
        (1241, false, 1862, 3, 21),
567
        (1242, false, 1863, 3, 21),
568
        (1243, true, 1864, 3, 20),
569
        (1244, false, 1865, 3, 21),
570
        (1245, false, 1866, 3, 21),
571
        (1246, false, 1867, 3, 21),
572
        (1247, true, 1868, 3, 20),
573
        (1248, false, 1869, 3, 21),
574
        (1249, false, 1870, 3, 21),
575
        (1250, false, 1871, 3, 21),
576
        (1251, true, 1872, 3, 20),
577
        (1252, false, 1873, 3, 21),
578
        (1253, false, 1874, 3, 21),
579
        (1254, false, 1875, 3, 21),
580
        (1255, true, 1876, 3, 20),
581
        (1256, false, 1877, 3, 21),
582
        (1257, false, 1878, 3, 21),
583
        (1258, false, 1879, 3, 21),
584
        (1259, true, 1880, 3, 20),
585
        (1260, false, 1881, 3, 21),
586
        (1261, false, 1882, 3, 21),
587
        (1262, false, 1883, 3, 21),
588
        (1263, true, 1884, 3, 20),
589
        (1264, false, 1885, 3, 21),
590
        (1265, false, 1886, 3, 21),
591
        (1266, false, 1887, 3, 21),
592
        (1267, true, 1888, 3, 20),
593
        (1268, false, 1889, 3, 21),
594
        (1269, false, 1890, 3, 21),
595
        (1270, false, 1891, 3, 21),
596
        (1271, true, 1892, 3, 20),
597
        (1272, false, 1893, 3, 21),
598
        (1273, false, 1894, 3, 21),
599
        (1274, false, 1895, 3, 21),
600
        (1275, false, 1896, 3, 20),
601
        (1276, true, 1897, 3, 20),
602
        (1277, false, 1898, 3, 21),
603
        (1278, false, 1899, 3, 21),
604
        (1279, false, 1900, 3, 21),
605
        (1280, true, 1901, 3, 21),
606
        (1281, false, 1902, 3, 22),
607
        (1282, false, 1903, 3, 22),
608
        (1283, false, 1904, 3, 21),
609
        (1284, true, 1905, 3, 21),
610
        (1285, false, 1906, 3, 22),
611
        (1286, false, 1907, 3, 22),
612
        (1287, false, 1908, 3, 21),
613
        (1288, true, 1909, 3, 21),
614
        (1289, false, 1910, 3, 22),
615
        (1290, false, 1911, 3, 22),
616
        (1291, false, 1912, 3, 21),
617
        (1292, true, 1913, 3, 21),
618
        (1293, false, 1914, 3, 22),
619
        (1294, false, 1915, 3, 22),
620
        (1295, false, 1916, 3, 21),
621
        (1296, true, 1917, 3, 21),
622
        (1297, false, 1918, 3, 22),
623
        (1298, false, 1919, 3, 22),
624
        (1299, false, 1920, 3, 21),
625
        (1300, true, 1921, 3, 21),
626
        (1301, false, 1922, 3, 22),
627
        (1302, false, 1923, 3, 22),
628
        (1303, false, 1924, 3, 21),
629
        (1304, true, 1925, 3, 21),
630
        (1305, false, 1926, 3, 22),
631
        (1306, false, 1927, 3, 22),
632
        (1307, false, 1928, 3, 21),
633
        (1308, false, 1929, 3, 21),
634
        (1309, true, 1930, 3, 21),
635
        (1310, false, 1931, 3, 22),
636
        (1311, false, 1932, 3, 21),
637
        (1312, false, 1933, 3, 21),
638
        (1313, true, 1934, 3, 21),
639
        (1314, false, 1935, 3, 22),
640
        (1315, false, 1936, 3, 21),
641
        (1316, false, 1937, 3, 21),
642
        (1317, true, 1938, 3, 21),
643
        (1318, false, 1939, 3, 22),
644
        (1319, false, 1940, 3, 21),
645
        (1320, false, 1941, 3, 21),
646
        (1321, true, 1942, 3, 21),
647
        (1322, false, 1943, 3, 22),
648
        (1323, false, 1944, 3, 21),
649
        (1324, false, 1945, 3, 21),
650
        (1325, true, 1946, 3, 21),
651
        (1326, false, 1947, 3, 22),
652
        (1327, false, 1948, 3, 21),
653
        (1328, false, 1949, 3, 21),
654
        (1329, true, 1950, 3, 21),
655
        (1330, false, 1951, 3, 22),
656
        (1331, false, 1952, 3, 21),
657
        (1332, false, 1953, 3, 21),
658
        (1333, true, 1954, 3, 21),
659
        (1334, false, 1955, 3, 22),
660
        (1335, false, 1956, 3, 21),
661
        (1336, false, 1957, 3, 21),
662
        (1337, true, 1958, 3, 21),
663
        (1338, false, 1959, 3, 22),
664
        (1339, false, 1960, 3, 21),
665
        (1340, false, 1961, 3, 21),
666
        (1341, false, 1962, 3, 21),
667
        (1342, true, 1963, 3, 21),
668
        (1343, false, 1964, 3, 21),
669
        (1344, false, 1965, 3, 21),
670
        (1345, false, 1966, 3, 21),
671
        (1346, true, 1967, 3, 21),
672
        (1347, false, 1968, 3, 21),
673
        (1348, false, 1969, 3, 21),
674
        (1349, false, 1970, 3, 21),
675
        (1350, true, 1971, 3, 21),
676
        (1351, false, 1972, 3, 21),
677
        (1352, false, 1973, 3, 21),
678
        (1353, false, 1974, 3, 21),
679
        (1354, true, 1975, 3, 21),
680
        (1355, false, 1976, 3, 21),
681
        (1356, false, 1977, 3, 21),
682
        (1357, false, 1978, 3, 21),
683
        (1358, true, 1979, 3, 21),
684
        (1359, false, 1980, 3, 21),
685
        (1360, false, 1981, 3, 21),
686
        (1361, false, 1982, 3, 21),
687
        (1362, true, 1983, 3, 21),
688
        (1363, false, 1984, 3, 21),
689
        (1364, false, 1985, 3, 21),
690
        (1365, false, 1986, 3, 21),
691
        (1366, true, 1987, 3, 21),
692
        (1367, false, 1988, 3, 21),
693
        (1368, false, 1989, 3, 21),
694
        (1369, false, 1990, 3, 21),
695
        (1370, true, 1991, 3, 21),
696
        (1371, false, 1992, 3, 21),
697
        (1372, false, 1993, 3, 21),
698
        (1373, false, 1994, 3, 21),
699
        (1374, false, 1995, 3, 21),
700
        (1375, true, 1996, 3, 20),
701
        (1376, false, 1997, 3, 21),
702
        (1377, false, 1998, 3, 21),
703
        (1378, false, 1999, 3, 21),
704
        (1379, true, 2000, 3, 20),
705
        (1380, false, 2001, 3, 21),
706
        (1381, false, 2002, 3, 21),
707
        (1382, false, 2003, 3, 21),
708
        (1383, true, 2004, 3, 20),
709
        (1384, false, 2005, 3, 21),
710
        (1385, false, 2006, 3, 21),
711
        (1386, false, 2007, 3, 21),
712
        (1387, true, 2008, 3, 20),
713
        (1388, false, 2009, 3, 21),
714
        (1389, false, 2010, 3, 21),
715
        (1390, false, 2011, 3, 21),
716
        (1391, true, 2012, 3, 20),
717
        (1392, false, 2013, 3, 21),
718
        (1393, false, 2014, 3, 21),
719
        (1394, false, 2015, 3, 21),
720
        (1395, true, 2016, 3, 20),
721
        (1396, false, 2017, 3, 21),
722
        (1397, false, 2018, 3, 21),
723
        (1398, false, 2019, 3, 21),
724
        (1399, true, 2020, 3, 20),
725
        (1400, false, 2021, 3, 21),
726
        (1401, false, 2022, 3, 21),
727
        (1402, false, 2023, 3, 21),
728
        (1403, true, 2024, 3, 20),
729
        (1404, false, 2025, 3, 21),
730
        (1405, false, 2026, 3, 21),
731
        (1406, false, 2027, 3, 21),
732
        (1407, false, 2028, 3, 20),
733
        (1408, true, 2029, 3, 20),
734
        (1409, false, 2030, 3, 21),
735
        (1410, false, 2031, 3, 21),
736
        (1411, false, 2032, 3, 20),
737
        (1412, true, 2033, 3, 20),
738
        (1413, false, 2034, 3, 21),
739
        (1414, false, 2035, 3, 21),
740
        (1415, false, 2036, 3, 20),
741
        (1416, true, 2037, 3, 20),
742
        (1417, false, 2038, 3, 21),
743
        (1418, false, 2039, 3, 21),
744
        (1419, false, 2040, 3, 20),
745
        (1420, true, 2041, 3, 20),
746
        (1421, false, 2042, 3, 21),
747
        (1422, false, 2043, 3, 21),
748
        (1423, false, 2044, 3, 20),
749
        (1424, true, 2045, 3, 20),
750
        (1425, false, 2046, 3, 21),
751
        (1426, false, 2047, 3, 21),
752
        (1427, false, 2048, 3, 20),
753
        (1428, true, 2049, 3, 20),
754
        (1429, false, 2050, 3, 21),
755
        (1430, false, 2051, 3, 21),
756
        (1431, false, 2052, 3, 20),
757
        (1432, true, 2053, 3, 20),
758
        (1433, false, 2054, 3, 21),
759
        (1434, false, 2055, 3, 21),
760
        (1435, false, 2056, 3, 20),
761
        (1436, true, 2057, 3, 20),
762
        (1437, false, 2058, 3, 21),
763
        (1438, false, 2059, 3, 21),
764
        (1439, false, 2060, 3, 20),
765
        (1440, false, 2061, 3, 20),
766
        (1441, true, 2062, 3, 20),
767
        (1442, false, 2063, 3, 21),
768
        (1443, false, 2064, 3, 20),
769
        (1444, false, 2065, 3, 20),
770
        (1445, true, 2066, 3, 20),
771
        (1446, false, 2067, 3, 21),
772
        (1447, false, 2068, 3, 20),
773
        (1448, false, 2069, 3, 20),
774
        (1449, true, 2070, 3, 20),
775
        (1450, false, 2071, 3, 21),
776
        (1451, false, 2072, 3, 20),
777
        (1452, false, 2073, 3, 20),
778
        (1453, true, 2074, 3, 20),
779
        (1454, false, 2075, 3, 21),
780
        (1455, false, 2076, 3, 20),
781
        (1456, false, 2077, 3, 20),
782
        (1457, true, 2078, 3, 20),
783
        (1458, false, 2079, 3, 21),
784
        (1459, false, 2080, 3, 20),
785
        (1460, false, 2081, 3, 20),
786
        (1461, true, 2082, 3, 20),
787
        (1462, false, 2083, 3, 21),
788
        (1463, false, 2084, 3, 20),
789
        (1464, false, 2085, 3, 20),
790
        (1465, true, 2086, 3, 20),
791
        (1466, false, 2087, 3, 21),
792
        (1467, false, 2088, 3, 20),
793
        (1468, false, 2089, 3, 20),
794
        (1469, true, 2090, 3, 20),
795
        (1470, false, 2091, 3, 21),
796
        (1471, false, 2092, 3, 20),
797
        (1472, false, 2093, 3, 20),
798
        (1473, false, 2094, 3, 20),
799
        (1474, true, 2095, 3, 20),
800
        (1475, false, 2096, 3, 20),
801
        (1476, false, 2097, 3, 20),
802
        (1477, false, 2098, 3, 20),
803
        (1478, true, 2099, 3, 20),
804
        (1479, false, 2100, 3, 21),
805
        (1480, false, 2101, 3, 21),
806
        (1481, false, 2102, 3, 21),
807
        (1482, true, 2103, 3, 21),
808
        (1483, false, 2104, 3, 21),
809
        (1484, false, 2105, 3, 21),
810
        (1485, false, 2106, 3, 21),
811
        (1486, true, 2107, 3, 21),
812
        (1487, false, 2108, 3, 21),
813
        (1488, false, 2109, 3, 21),
814
        (1489, false, 2110, 3, 21),
815
        (1490, true, 2111, 3, 21),
816
        (1491, false, 2112, 3, 21),
817
        (1492, false, 2113, 3, 21),
818
        (1493, false, 2114, 3, 21),
819
        (1494, true, 2115, 3, 21),
820
        (1495, false, 2116, 3, 21),
821
        (1496, false, 2117, 3, 21),
822
        (1497, false, 2118, 3, 21),
823
        (1498, true, 2119, 3, 21),
824
    ];
825
826
    #[test]
827
    fn test_calendar_ut_ac_ir_data() {
828
        for (p_year, leap, iso_year, iso_month, iso_day) in CALENDAR_UT_AC_IR_TEST_DATA.iter() {
829
            assert_eq!(Persian::is_leap_year(*p_year, ()), *leap);
830
            let persian_date = Date::try_new_persian_date(*p_year, 1, 1).unwrap();
831
            let iso_date = persian_date.to_calendar(Iso);
832
            assert_eq!(iso_date.year().number, *iso_year);
833
            assert_eq!(iso_date.month().ordinal, *iso_month);
834
            assert_eq!(iso_date.day_of_month().0, *iso_day);
835
        }
836
    }
837
}