use chrono::{Datelike, NaiveDate};
use std::error;
use std::fmt;
#[derive(Debug, Clone)]
pub struct EasterError {
y: i32,
}
impl EasterError {
fn description_string(&self) -> String {
let mut msg = format!("Couldn't parse easter date for year {}.", self.y);
if self.y < 1582 {
msg.push_str(" Current algorithm only works after year 1582.");
}
msg
}
}
impl fmt::Display for EasterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description_string())
}
}
impl error::Error for EasterError {}
pub fn easter_num_days_from_ce(y: i32) -> Result<i32, EasterError> {
if y < 1582 {
return Err(EasterError { y });
}
let c = (y / 100) + 1;
let mut se = (14 + 11 * (y % 19) - 3 * c / 4 + (5 + 8 * c) / 25) % 30;
if (se == 0) || ((se == 1) && (10 < (y % 19))) {
se += 1;
}
let p = NaiveDate::from_ymd_opt(y, 4, 19)
.ok_or_else(|| EasterError { y })?
.num_days_from_ce()
- se;
Ok(p + 7 - (p % 7))
}
pub fn easter_naive_date(y: i32) -> Result<NaiveDate, EasterError> {
let rata = easter_num_days_from_ce(y)?;
match NaiveDate::from_num_days_from_ce_opt(rata) {
Some(result) => Ok(result),
None => Err(EasterError { y }),
}
}