diff --git a/components/calendar/src/persian.rs b/components/calendar/src/persian.rs index 546712b0efb..2abb2a26487 100644 --- a/components/calendar/src/persian.rs +++ b/components/calendar/src/persian.rs @@ -79,17 +79,11 @@ impl CalendarArithmetic for Persian { fn months_for_every_year(_: i32, _data: ()) -> u8 { 12 } - // Lisp code reference: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L4789 - fn is_leap_year(p_year: i32, _data: ()) -> bool { - let mut p_year = p_year as i64; - if 0 < p_year { - p_year -= 474; - } else { - p_year -= 473; - }; - let year = p_year.rem_euclid(2820) + 474; - ((year + 38) * 31).rem_euclid(128) < 31 + // Calculated using the 33-year rule + fn is_leap_year(p_year: i32, _data: ()) -> bool { + let p_year = p_year as i64; + (25 * p_year + 11).rem_euclid(33) < 8 } fn days_in_provided_year(year: i32, _data: ()) -> u16 { @@ -129,11 +123,11 @@ impl Calendar for Persian { fn date_from_iso(&self, iso: Date) -> PersianDateInner { let fixed_iso = Iso::fixed_from_iso(*iso.inner()); - Self::arithmetic_persian_from_fixed(fixed_iso) + Self::fast_persian_from_fixed(fixed_iso) } fn date_to_iso(&self, date: &Self::DateInner) -> Date { - let fixed_persian = Persian::fixed_from_arithmetic_persian(*date); + let fixed_persian = Persian::fixed_from_fast_persian(*date); Iso::iso_from_fixed(fixed_persian) } @@ -212,16 +206,16 @@ impl Persian { Self } - fn fixed_from_arithmetic_persian(p_date: PersianDateInner) -> RataDie { - calendrical_calculations::persian::fixed_from_arithmetic_persian( + fn fixed_from_fast_persian(p_date: PersianDateInner) -> RataDie { + calendrical_calculations::persian::fixed_from_fast_persian( p_date.0.year, p_date.0.month, p_date.0.day, ) } - fn arithmetic_persian_from_fixed(date: RataDie) -> PersianDateInner { + fn fast_persian_from_fixed(date: RataDie) -> PersianDateInner { let (year, month, day) = - match calendrical_calculations::persian::arithmetic_persian_from_fixed(date) { + match calendrical_calculations::persian::fast_persian_from_fixed(date) { Err(I32CastError::BelowMin) => return PersianDateInner(ArithmeticDate::min_date()), Err(I32CastError::AboveMax) => return PersianDateInner(ArithmeticDate::max_date()), Ok(ymd) => ymd, @@ -308,122 +302,20 @@ mod tests { day: u8, } - static TEST_FIXED_DATE: [i64; 33] = [ - -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605, - 470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554, - 664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652, + static TEST_FIXED_DATE: [i64; 18] = [ + 656786, 664224, 671401, 694799, 702806, 704424, 708842, 709409, 709580, 727274, 728714, + 739330, 739331, 744313, 763436, 763437, 764652, 775123, ]; - static CASES: [DateCase; 33] = [ - DateCase { - year: -1208, - month: 5, - day: 1, - }, - DateCase { - year: -790, - month: 9, - day: 14, - }, - DateCase { - year: -552, - month: 7, - day: 2, - }, - DateCase { - year: -487, - month: 7, - day: 9, - }, - DateCase { - year: -153, - month: 10, - day: 18, - }, - DateCase { - year: -46, - month: 2, - day: 30, - }, - DateCase { - year: 73, - month: 8, - day: 19, - }, + // Test data are provided for the range 1178-1501 AP, for which + // we know the 33-year rule matches the astronomical calculations + // based on the 52.5 degrees east meridian. + static CASES: [DateCase; 18] = [ + // First year for which 33-year rule matches the astronomical calculation DateCase { - year: 392, - month: 2, - day: 5, - }, - DateCase { - year: 475, - month: 3, - day: 3, - }, - DateCase { - year: 569, + year: 1178, month: 1, - day: 3, - }, - DateCase { - year: 618, - month: 12, - day: 20, - }, - DateCase { - year: 667, - month: 1, - day: 14, - }, - DateCase { - year: 677, - month: 2, - day: 8, - }, - DateCase { - year: 770, - month: 3, - day: 22, - }, - DateCase { - year: 814, - month: 11, - day: 13, - }, - DateCase { - year: 871, - month: 1, - day: 21, - }, - DateCase { - year: 932, - month: 6, - day: 28, - }, - DateCase { - year: 938, - month: 12, - day: 14, - }, - DateCase { - year: 1027, - month: 3, - day: 21, - }, - DateCase { - year: 1059, - month: 4, - day: 10, - }, - DateCase { - year: 1095, - month: 5, - day: 2, - }, - DateCase { - year: 1147, - month: 3, - day: 30, + day: 1, }, DateCase { year: 1198, @@ -440,6 +332,12 @@ mod tests { month: 1, day: 29, }, + // The beginning of the year the calendar was adopted + DateCase { + year: 1304, + month: 1, + day: 1, + }, DateCase { year: 1308, month: 6, @@ -470,24 +368,53 @@ mod tests { month: 12, day: 6, }, + // First day that the 2820-year rule fails + DateCase { + year: 1403, + month: 12, + day: 30, + }, + // First Nowruz that the 2820-year rule fails + DateCase { + year: 1404, + month: 1, + day: 1, + }, DateCase { year: 1417, month: 8, day: 19, }, + // First day the unmodified astronomical algorithm fails + DateCase { + year: 1469, + month: 12, + day: 30, + }, + // First Nowruz the unmodified astronomical algorithm fails + DateCase { + year: 1470, + month: 1, + day: 1, + }, DateCase { year: 1473, month: 4, day: 28, }, + // Last year the 33-year rule matches the modified astronomical calculation + DateCase { + year: 1501, + month: 12, + day: 29, + }, ]; fn days_in_provided_year_core(year: i32) -> u16 { let fixed_year = - calendrical_calculations::persian::fixed_from_arithmetic_persian(year, 1, 1) - .to_i64_date(); + calendrical_calculations::persian::fixed_from_fast_persian(year, 1, 1).to_i64_date(); let next_fixed_year = - calendrical_calculations::persian::fixed_from_arithmetic_persian(year + 1, 1, 1) + calendrical_calculations::persian::fixed_from_fast_persian(year + 1, 1, 1) .to_i64_date(); (next_fixed_year - fixed_year) as u16 @@ -495,26 +422,13 @@ mod tests { #[test] fn test_persian_leap_year() { - let mut leap_years: [i32; 33] = [0; 33]; + let mut leap_years: [i32; 18] = [0; 18]; // These values were computed from the "Calendrical Calculations" reference code output let expected_values = [ - false, true, false, false, false, false, false, true, false, true, false, false, true, - false, false, true, false, false, false, false, false, false, false, true, false, - false, false, false, false, true, false, false, false, + false, false, true, false, true, false, false, false, false, true, false, true, false, + false, true, false, false, false, ]; - let mut leap_year_results: Vec = Vec::new(); - let canonical_leap_year_cycle_start = 474; - let canonical_leap_year_cycle_end = 3293; - for year in canonical_leap_year_cycle_start..=canonical_leap_year_cycle_end { - let r = Persian::is_leap_year(year, ()); - if r { - leap_year_results.push(r); - } - } - // 683 is the amount of leap years in the 2820 Persian year cycle - assert_eq!(leap_year_results.len(), 683); - for (index, case) in CASES.iter().enumerate() { leap_years[index] = case.year; } @@ -539,7 +453,7 @@ mod tests { let date = Date::try_new_persian_date(case.year, case.month, case.day).unwrap(); assert_eq!( - Persian::fixed_from_arithmetic_persian(*date.inner()).to_i64_date(), + Persian::fixed_from_fast_persian(*date.inner()).to_i64_date(), *f_date, "{case:?}" ); @@ -550,7 +464,7 @@ mod tests { for (case, f_date) in CASES.iter().zip(TEST_FIXED_DATE.iter()) { let date = Date::try_new_persian_date(case.year, case.month, case.day).unwrap(); assert_eq!( - Persian::arithmetic_persian_from_fixed(RataDie::new(*f_date)), + Persian::fast_persian_from_fixed(RataDie::new(*f_date)), date.inner, "{case:?}" ); @@ -597,4 +511,314 @@ mod tests { assert_eq!(info.next_year.number, case.expected_next, "{:?}", case); } } + + // From https://calendar.ut.ac.ir/Fa/News/Data/Doc/KabiseShamsi1206-1498-new.pdf + // Plain text version at https://github.com/roozbehp/persiancalendar/blob/main/kabise.txt + static CALENDAR_UT_AC_IR_TEST_DATA: [(i32, bool, i32, u32, u32); 293] = [ + (1206, false, 1827, 3, 22), + (1207, false, 1828, 3, 21), + (1208, false, 1829, 3, 21), + (1209, false, 1830, 3, 21), + (1210, true, 1831, 3, 21), + (1211, false, 1832, 3, 21), + (1212, false, 1833, 3, 21), + (1213, false, 1834, 3, 21), + (1214, true, 1835, 3, 21), + (1215, false, 1836, 3, 21), + (1216, false, 1837, 3, 21), + (1217, false, 1838, 3, 21), + (1218, true, 1839, 3, 21), + (1219, false, 1840, 3, 21), + (1220, false, 1841, 3, 21), + (1221, false, 1842, 3, 21), + (1222, true, 1843, 3, 21), + (1223, false, 1844, 3, 21), + (1224, false, 1845, 3, 21), + (1225, false, 1846, 3, 21), + (1226, true, 1847, 3, 21), + (1227, false, 1848, 3, 21), + (1228, false, 1849, 3, 21), + (1229, false, 1850, 3, 21), + (1230, true, 1851, 3, 21), + (1231, false, 1852, 3, 21), + (1232, false, 1853, 3, 21), + (1233, false, 1854, 3, 21), + (1234, true, 1855, 3, 21), + (1235, false, 1856, 3, 21), + (1236, false, 1857, 3, 21), + (1237, false, 1858, 3, 21), + (1238, true, 1859, 3, 21), + (1239, false, 1860, 3, 21), + (1240, false, 1861, 3, 21), + (1241, false, 1862, 3, 21), + (1242, false, 1863, 3, 21), + (1243, true, 1864, 3, 20), + (1244, false, 1865, 3, 21), + (1245, false, 1866, 3, 21), + (1246, false, 1867, 3, 21), + (1247, true, 1868, 3, 20), + (1248, false, 1869, 3, 21), + (1249, false, 1870, 3, 21), + (1250, false, 1871, 3, 21), + (1251, true, 1872, 3, 20), + (1252, false, 1873, 3, 21), + (1253, false, 1874, 3, 21), + (1254, false, 1875, 3, 21), + (1255, true, 1876, 3, 20), + (1256, false, 1877, 3, 21), + (1257, false, 1878, 3, 21), + (1258, false, 1879, 3, 21), + (1259, true, 1880, 3, 20), + (1260, false, 1881, 3, 21), + (1261, false, 1882, 3, 21), + (1262, false, 1883, 3, 21), + (1263, true, 1884, 3, 20), + (1264, false, 1885, 3, 21), + (1265, false, 1886, 3, 21), + (1266, false, 1887, 3, 21), + (1267, true, 1888, 3, 20), + (1268, false, 1889, 3, 21), + (1269, false, 1890, 3, 21), + (1270, false, 1891, 3, 21), + (1271, true, 1892, 3, 20), + (1272, false, 1893, 3, 21), + (1273, false, 1894, 3, 21), + (1274, false, 1895, 3, 21), + (1275, false, 1896, 3, 20), + (1276, true, 1897, 3, 20), + (1277, false, 1898, 3, 21), + (1278, false, 1899, 3, 21), + (1279, false, 1900, 3, 21), + (1280, true, 1901, 3, 21), + (1281, false, 1902, 3, 22), + (1282, false, 1903, 3, 22), + (1283, false, 1904, 3, 21), + (1284, true, 1905, 3, 21), + (1285, false, 1906, 3, 22), + (1286, false, 1907, 3, 22), + (1287, false, 1908, 3, 21), + (1288, true, 1909, 3, 21), + (1289, false, 1910, 3, 22), + (1290, false, 1911, 3, 22), + (1291, false, 1912, 3, 21), + (1292, true, 1913, 3, 21), + (1293, false, 1914, 3, 22), + (1294, false, 1915, 3, 22), + (1295, false, 1916, 3, 21), + (1296, true, 1917, 3, 21), + (1297, false, 1918, 3, 22), + (1298, false, 1919, 3, 22), + (1299, false, 1920, 3, 21), + (1300, true, 1921, 3, 21), + (1301, false, 1922, 3, 22), + (1302, false, 1923, 3, 22), + (1303, false, 1924, 3, 21), + (1304, true, 1925, 3, 21), + (1305, false, 1926, 3, 22), + (1306, false, 1927, 3, 22), + (1307, false, 1928, 3, 21), + (1308, false, 1929, 3, 21), + (1309, true, 1930, 3, 21), + (1310, false, 1931, 3, 22), + (1311, false, 1932, 3, 21), + (1312, false, 1933, 3, 21), + (1313, true, 1934, 3, 21), + (1314, false, 1935, 3, 22), + (1315, false, 1936, 3, 21), + (1316, false, 1937, 3, 21), + (1317, true, 1938, 3, 21), + (1318, false, 1939, 3, 22), + (1319, false, 1940, 3, 21), + (1320, false, 1941, 3, 21), + (1321, true, 1942, 3, 21), + (1322, false, 1943, 3, 22), + (1323, false, 1944, 3, 21), + (1324, false, 1945, 3, 21), + (1325, true, 1946, 3, 21), + (1326, false, 1947, 3, 22), + (1327, false, 1948, 3, 21), + (1328, false, 1949, 3, 21), + (1329, true, 1950, 3, 21), + (1330, false, 1951, 3, 22), + (1331, false, 1952, 3, 21), + (1332, false, 1953, 3, 21), + (1333, true, 1954, 3, 21), + (1334, false, 1955, 3, 22), + (1335, false, 1956, 3, 21), + (1336, false, 1957, 3, 21), + (1337, true, 1958, 3, 21), + (1338, false, 1959, 3, 22), + (1339, false, 1960, 3, 21), + (1340, false, 1961, 3, 21), + (1341, false, 1962, 3, 21), + (1342, true, 1963, 3, 21), + (1343, false, 1964, 3, 21), + (1344, false, 1965, 3, 21), + (1345, false, 1966, 3, 21), + (1346, true, 1967, 3, 21), + (1347, false, 1968, 3, 21), + (1348, false, 1969, 3, 21), + (1349, false, 1970, 3, 21), + (1350, true, 1971, 3, 21), + (1351, false, 1972, 3, 21), + (1352, false, 1973, 3, 21), + (1353, false, 1974, 3, 21), + (1354, true, 1975, 3, 21), + (1355, false, 1976, 3, 21), + (1356, false, 1977, 3, 21), + (1357, false, 1978, 3, 21), + (1358, true, 1979, 3, 21), + (1359, false, 1980, 3, 21), + (1360, false, 1981, 3, 21), + (1361, false, 1982, 3, 21), + (1362, true, 1983, 3, 21), + (1363, false, 1984, 3, 21), + (1364, false, 1985, 3, 21), + (1365, false, 1986, 3, 21), + (1366, true, 1987, 3, 21), + (1367, false, 1988, 3, 21), + (1368, false, 1989, 3, 21), + (1369, false, 1990, 3, 21), + (1370, true, 1991, 3, 21), + (1371, false, 1992, 3, 21), + (1372, false, 1993, 3, 21), + (1373, false, 1994, 3, 21), + (1374, false, 1995, 3, 21), + (1375, true, 1996, 3, 20), + (1376, false, 1997, 3, 21), + (1377, false, 1998, 3, 21), + (1378, false, 1999, 3, 21), + (1379, true, 2000, 3, 20), + (1380, false, 2001, 3, 21), + (1381, false, 2002, 3, 21), + (1382, false, 2003, 3, 21), + (1383, true, 2004, 3, 20), + (1384, false, 2005, 3, 21), + (1385, false, 2006, 3, 21), + (1386, false, 2007, 3, 21), + (1387, true, 2008, 3, 20), + (1388, false, 2009, 3, 21), + (1389, false, 2010, 3, 21), + (1390, false, 2011, 3, 21), + (1391, true, 2012, 3, 20), + (1392, false, 2013, 3, 21), + (1393, false, 2014, 3, 21), + (1394, false, 2015, 3, 21), + (1395, true, 2016, 3, 20), + (1396, false, 2017, 3, 21), + (1397, false, 2018, 3, 21), + (1398, false, 2019, 3, 21), + (1399, true, 2020, 3, 20), + (1400, false, 2021, 3, 21), + (1401, false, 2022, 3, 21), + (1402, false, 2023, 3, 21), + (1403, true, 2024, 3, 20), + (1404, false, 2025, 3, 21), + (1405, false, 2026, 3, 21), + (1406, false, 2027, 3, 21), + (1407, false, 2028, 3, 20), + (1408, true, 2029, 3, 20), + (1409, false, 2030, 3, 21), + (1410, false, 2031, 3, 21), + (1411, false, 2032, 3, 20), + (1412, true, 2033, 3, 20), + (1413, false, 2034, 3, 21), + (1414, false, 2035, 3, 21), + (1415, false, 2036, 3, 20), + (1416, true, 2037, 3, 20), + (1417, false, 2038, 3, 21), + (1418, false, 2039, 3, 21), + (1419, false, 2040, 3, 20), + (1420, true, 2041, 3, 20), + (1421, false, 2042, 3, 21), + (1422, false, 2043, 3, 21), + (1423, false, 2044, 3, 20), + (1424, true, 2045, 3, 20), + (1425, false, 2046, 3, 21), + (1426, false, 2047, 3, 21), + (1427, false, 2048, 3, 20), + (1428, true, 2049, 3, 20), + (1429, false, 2050, 3, 21), + (1430, false, 2051, 3, 21), + (1431, false, 2052, 3, 20), + (1432, true, 2053, 3, 20), + (1433, false, 2054, 3, 21), + (1434, false, 2055, 3, 21), + (1435, false, 2056, 3, 20), + (1436, true, 2057, 3, 20), + (1437, false, 2058, 3, 21), + (1438, false, 2059, 3, 21), + (1439, false, 2060, 3, 20), + (1440, false, 2061, 3, 20), + (1441, true, 2062, 3, 20), + (1442, false, 2063, 3, 21), + (1443, false, 2064, 3, 20), + (1444, false, 2065, 3, 20), + (1445, true, 2066, 3, 20), + (1446, false, 2067, 3, 21), + (1447, false, 2068, 3, 20), + (1448, false, 2069, 3, 20), + (1449, true, 2070, 3, 20), + (1450, false, 2071, 3, 21), + (1451, false, 2072, 3, 20), + (1452, false, 2073, 3, 20), + (1453, true, 2074, 3, 20), + (1454, false, 2075, 3, 21), + (1455, false, 2076, 3, 20), + (1456, false, 2077, 3, 20), + (1457, true, 2078, 3, 20), + (1458, false, 2079, 3, 21), + (1459, false, 2080, 3, 20), + (1460, false, 2081, 3, 20), + (1461, true, 2082, 3, 20), + (1462, false, 2083, 3, 21), + (1463, false, 2084, 3, 20), + (1464, false, 2085, 3, 20), + (1465, true, 2086, 3, 20), + (1466, false, 2087, 3, 21), + (1467, false, 2088, 3, 20), + (1468, false, 2089, 3, 20), + (1469, true, 2090, 3, 20), + (1470, false, 2091, 3, 21), + (1471, false, 2092, 3, 20), + (1472, false, 2093, 3, 20), + (1473, false, 2094, 3, 20), + (1474, true, 2095, 3, 20), + (1475, false, 2096, 3, 20), + (1476, false, 2097, 3, 20), + (1477, false, 2098, 3, 20), + (1478, true, 2099, 3, 20), + (1479, false, 2100, 3, 21), + (1480, false, 2101, 3, 21), + (1481, false, 2102, 3, 21), + (1482, true, 2103, 3, 21), + (1483, false, 2104, 3, 21), + (1484, false, 2105, 3, 21), + (1485, false, 2106, 3, 21), + (1486, true, 2107, 3, 21), + (1487, false, 2108, 3, 21), + (1488, false, 2109, 3, 21), + (1489, false, 2110, 3, 21), + (1490, true, 2111, 3, 21), + (1491, false, 2112, 3, 21), + (1492, false, 2113, 3, 21), + (1493, false, 2114, 3, 21), + (1494, true, 2115, 3, 21), + (1495, false, 2116, 3, 21), + (1496, false, 2117, 3, 21), + (1497, false, 2118, 3, 21), + (1498, true, 2119, 3, 21), + ]; + + #[test] + fn test_calendar_ut_ac_ir_data() { + for (p_year, leap, iso_year, iso_month, iso_day) in CALENDAR_UT_AC_IR_TEST_DATA.iter() { + assert_eq!(Persian::is_leap_year(*p_year, ()), *leap); + let persian_date = Date::try_new_persian_date(*p_year, 1, 1).unwrap(); + let iso_date = persian_date.to_calendar(Iso); + assert_eq!(iso_date.year().number, *iso_year); + assert_eq!(iso_date.month().ordinal, *iso_month); + assert_eq!(iso_date.day_of_month().0, *iso_day); + } + } } diff --git a/utils/calendrical_calculations/src/persian.rs b/utils/calendrical_calculations/src/persian.rs index 39e8ae9441e..b5bcab6e342 100644 --- a/utils/calendrical_calculations/src/persian.rs +++ b/utils/calendrical_calculations/src/persian.rs @@ -14,6 +14,7 @@ use crate::rata_die::RataDie; const FIXED_PERSIAN_EPOCH: RataDie = crate::julian::fixed_from_julian(622, 3, 19); /// Lisp code reference: +/// Not used, but kept for comparative purposes pub fn fixed_from_arithmetic_persian(year: i32, month: u8, day: u8) -> RataDie { let p_year = i64::from(year); let month = i64::from(month); @@ -39,7 +40,28 @@ pub fn fixed_from_arithmetic_persian(year: i32, month: u8, day: u8) -> RataDie { ) } +/// fixed_from_arithmetic_persian, modified to use the more correct 33-year rule +pub fn fixed_from_fast_persian(year: i32, month: u8, day: u8) -> RataDie { + let p_year = i64::from(year); + let month = i64::from(month); + let day = i64::from(day); + let new_year = FIXED_PERSIAN_EPOCH.to_i64_date() - 1 + + 365 * (p_year - 1) + + (8 * p_year + 21).div_euclid(33); + + RataDie::new( + new_year - 1 + + if month <= 7 { + 31 * (month - 1) + } else { + 30 * (month - 1) + 6 + } + + day, + ) +} + /// Lisp code reference: +/// Not used, but kept for comparative purposes pub fn arithmetic_persian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> { let year = arithmetic_persian_year_from_fixed(date); let year = i64_to_i32(year)?; @@ -55,7 +77,23 @@ pub fn arithmetic_persian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32 Ok((year, month, day)) } +/// arithmetic_persian_from_fixed, modified to use the 33-year rule method +pub fn fast_persian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> { + let year = fast_persian_year_from_fixed(date); + let year = i64_to_i32(year)?; + let day_of_year = 1_i64 + (date - fixed_from_fast_persian(year, 1, 1)); + #[allow(unstable_name_collisions)] // div_ceil is unstable and polyfilled + let month = if day_of_year <= 186 { + day_of_year.div_ceil(31) as u8 + } else { + (day_of_year - 6).div_ceil(30) as u8 + }; + let day = (date - fixed_from_fast_persian(year, month, 1) + 1) as u8; + Ok((year, month, day)) +} + /// Lisp code reference: +/// Not used, but kept for comparative purposes fn arithmetic_persian_year_from_fixed(date: RataDie) -> i64 { let d0 = date - fixed_from_arithmetic_persian(475, 1, 1); let n2820 = d0.div_euclid(1029983); @@ -73,6 +111,27 @@ fn arithmetic_persian_year_from_fixed(date: RataDie) -> i64 { } } +/// arithmetic_persian_year_from_fixed modified for the 33-year rule +fn fast_persian_year_from_fixed(date: RataDie) -> i64 { + let days_since_epoch = date - FIXED_PERSIAN_EPOCH + 1; + 1 + (33 * days_since_epoch + 3).div_euclid(12053) +} + +/// Lisp code reference: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L4789 +/// Not used, but kept for comparative purposes +#[allow(dead_code)] +fn is_arithmetic_leap_year(p_year: i32, _data: ()) -> bool { + let mut p_year = p_year as i64; + if 0 < p_year { + p_year -= 474; + } else { + p_year -= 473; + }; + let year = p_year.rem_euclid(2820) + 474; + + ((year + 38) * 31).rem_euclid(128) < 31 +} + #[cfg(test)] mod tests { use super::*; @@ -94,13 +153,11 @@ mod tests { } else { persian_year }; - fixed_from_arithmetic_persian(year, 1, 1) + fixed_from_fast_persian(year, 1, 1) } #[test] fn test_nowruz() { - let fixed_date = nowruz(622).to_i64_date(); - assert_eq!(fixed_date, FIXED_PERSIAN_EPOCH.to_i64_date()); // These values are used as test data in appendix C of the "Calendrical Calculations" book let nowruz_test_year_start = 2000; let nowruz_test_year_end = 2103; @@ -108,9 +165,9 @@ mod tests { for year in nowruz_test_year_start..=nowruz_test_year_end { let two_thousand_eight_to_fixed = nowruz(year).to_i64_date(); let iso_date = crate::iso::fixed_from_iso(year, 3, 21); - let (persian_year, _m, _d) = arithmetic_persian_from_fixed(iso_date).unwrap(); + let (persian_year, _m, _d) = fast_persian_from_fixed(iso_date).unwrap(); assert_eq!( - arithmetic_persian_from_fixed(RataDie::new(two_thousand_eight_to_fixed)) + fast_persian_from_fixed(RataDie::new(two_thousand_eight_to_fixed)) .unwrap() .0, persian_year