icu_calendar/cal/
persian.rs

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