use crate::cal::abstract_gregorian::{
impl_with_abstract_gregorian, AbstractGregorian, GregorianYears,
};
use crate::calendar_arithmetic::ArithmeticDate;
use crate::error::UnknownEraError;
use crate::preferences::CalendarAlgorithm;
use crate::{types, Date, RangeError};
use tinystr::tinystr;
impl_with_abstract_gregorian!(Gregorian, GregorianDateInner, CeBce, _x, CeBce);
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct CeBce;
impl GregorianYears for CeBce {
fn extended_from_era_year(
&self,
era: Option<&[u8]>,
year: i32,
) -> Result<i32, UnknownEraError> {
match era {
None => Ok(year),
Some(b"ad" | b"ce") => Ok(year),
Some(b"bce" | b"bc") => Ok(1 - year),
Some(_) => Err(UnknownEraError),
}
}
fn era_year_from_extended(&self, extended_year: i32, _month: u8, _day: u8) -> types::EraYear {
if extended_year > 0 {
types::EraYear {
era: tinystr!(16, "ce"),
era_index: Some(1),
year: extended_year,
extended_year,
ambiguity: match extended_year {
..=999 => types::YearAmbiguity::EraAndCenturyRequired,
1000..=1949 => types::YearAmbiguity::CenturyRequired,
1950..=2049 => types::YearAmbiguity::Unambiguous,
2050.. => types::YearAmbiguity::CenturyRequired,
},
}
} else {
types::EraYear {
era: tinystr!(16, "bce"),
era_index: Some(0),
year: 1 - extended_year,
extended_year,
ambiguity: types::YearAmbiguity::EraAndCenturyRequired,
}
}
}
fn debug_name(&self) -> &'static str {
"Gregorian"
}
fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
Some(CalendarAlgorithm::Gregory)
}
}
#[derive(Copy, Clone, Debug, Default)]
#[allow(clippy::exhaustive_structs)] pub struct Gregorian;
impl Date<Gregorian> {
pub fn try_new_gregorian(year: i32, month: u8, day: u8) -> Result<Date<Gregorian>, RangeError> {
ArithmeticDate::from_year_month_day(year, month, day, &AbstractGregorian(CeBce))
.map(ArithmeticDate::cast)
.map(GregorianDateInner)
.map(|i| Date::from_raw(i, Gregorian))
}
}
impl Gregorian {
pub fn easter(year: i32) -> Date<Self> {
Date::from_rata_die(calendrical_calculations::gregorian::easter(year), Self)
}
}
#[cfg(test)]
mod test {
use calendrical_calculations::rata_die::RataDie;
use super::*;
#[derive(Debug)]
struct TestCase {
rd: RataDie,
extended_year: i32,
month: u8,
day: u8,
era_year: i32,
era: &'static str,
}
fn check_test_case(case: TestCase) {
let date = Date::from_rata_die(case.rd, Gregorian);
assert_eq!(date.to_rata_die(), case.rd, "{case:?}");
assert_eq!(date.era_year().year, case.era_year, "{case:?}");
assert_eq!(
date.era_year().extended_year,
case.extended_year,
"{case:?}"
);
assert_eq!(date.era_year().era, case.era, "{case:?}");
assert_eq!(date.month().ordinal, case.month, "{case:?}");
assert_eq!(date.day_of_month().0, case.day, "{case:?}");
assert_eq!(
Date::try_new_gregorian(
date.era_year().extended_year,
date.month().ordinal,
date.day_of_month().0
),
Ok(date),
"{case:?}"
);
}
#[test]
fn test_gregorian_ce() {
let cases = [
TestCase {
rd: RataDie::new(1),
extended_year: 1,
month: 1,
day: 1,
era_year: 1,
era: "ce",
},
TestCase {
rd: RataDie::new(181),
extended_year: 1,
month: 6,
day: 30,
era_year: 1,
era: "ce",
},
TestCase {
rd: RataDie::new(1155),
extended_year: 4,
month: 2,
day: 29,
era_year: 4,
era: "ce",
},
TestCase {
rd: RataDie::new(1344),
extended_year: 4,
month: 9,
day: 5,
era_year: 4,
era: "ce",
},
TestCase {
rd: RataDie::new(36219),
extended_year: 100,
month: 3,
day: 1,
era_year: 100,
era: "ce",
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn test_gregorian_bce() {
let cases = [
TestCase {
rd: RataDie::new(0),
extended_year: 0,
month: 12,
day: 31,
era_year: 1,
era: "bce",
},
TestCase {
rd: RataDie::new(-365), extended_year: 0,
month: 1,
day: 1,
era_year: 1,
era: "bce",
},
TestCase {
rd: RataDie::new(-366),
extended_year: -1,
month: 12,
day: 31,
era_year: 2,
era: "bce",
},
TestCase {
rd: RataDie::new(-1461),
extended_year: -4,
month: 12,
day: 31,
era_year: 5,
era: "bce",
},
TestCase {
rd: RataDie::new(-1826),
extended_year: -4,
month: 1,
day: 1,
era_year: 5,
era: "bce",
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn check_gregorian_directionality() {
for i in -100..100 {
for j in -100..100 {
let greg_i = Date::from_rata_die(RataDie::new(i), Gregorian);
let greg_j = Date::from_rata_die(RataDie::new(j), Gregorian);
assert_eq!(
i.cmp(&j),
greg_i.cmp(&greg_j),
"Gregorian directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
}