use crate::helpers::{i64_to_i32, I32CastError, IntegerRoundings};
use crate::rata_die::RataDie;
const FIXED_PERSIAN_EPOCH: RataDie = crate::julian::fixed_from_julian(622, 3, 19);
pub fn fixed_from_arithmetic_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 y = if p_year > 0 {
p_year - 474
} else {
p_year - 473
};
let year = y.rem_euclid(2820) + 474;
RataDie::new(
FIXED_PERSIAN_EPOCH.to_i64_date() - 1
+ 1029983 * y.div_euclid(2820)
+ 365 * (year - 1)
+ (31 * year - 5).div_euclid(128)
+ if month <= 7 {
31 * (month - 1)
} else {
30 * (month - 1) + 6
}
+ day,
)
}
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)?;
#[allow(clippy::unwrap_used)] let day_of_year = 1_i64 + (date - fixed_from_arithmetic_persian(year, 1, 1));
#[allow(unstable_name_collisions)] 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_arithmetic_persian(year, month, 1) + 1) as u8;
Ok((year, month, day))
}
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);
let d1 = d0.rem_euclid(1029983);
let y2820 = if d1 == 1029982 {
2820
} else {
(128 * d1 + 46878).div_euclid(46751)
};
let year = 474 + n2820 * 2820 + y2820;
if year > 0 {
year
} else {
year - 1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_persian_epoch() {
let epoch = FIXED_PERSIAN_EPOCH.to_i64_date();
let epoch_year_from_fixed = crate::iso::iso_year_from_fixed(RataDie::new(epoch));
assert_eq!(epoch_year_from_fixed, 622);
}
fn nowruz(g_year: i32) -> RataDie {
let (y, _m, _d) = crate::iso::iso_from_fixed(FIXED_PERSIAN_EPOCH).unwrap();
let persian_year = g_year - y + 1;
let year = if persian_year <= 0 {
persian_year - 1
} else {
persian_year
};
fixed_from_arithmetic_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());
let nowruz_test_year_start = 2000;
let nowruz_test_year_end = 2103;
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();
assert_eq!(
arithmetic_persian_from_fixed(RataDie::new(two_thousand_eight_to_fixed))
.unwrap()
.0,
persian_year
);
}
}
}