use crate::calendar::gregorian::Gregorian;
use crate::calendar::gregorian::GregorianMonth;
use crate::calendar::prelude::CommonDate;
use crate::calendar::prelude::CommonWeekOfYear;
use crate::calendar::prelude::GuaranteedMonth;
use crate::calendar::prelude::HasLeapYears;
use crate::calendar::prelude::Quarter;
use crate::calendar::prelude::ToFromCommonDate;
use crate::calendar::AllowYearZero;
use crate::calendar::CalendarMoment;
use crate::calendar::OrdinalDate;
use crate::calendar::ToFromOrdinalDate;
use crate::common::error::CalendarError;
use crate::day_count::CalculatedBounds;
use crate::day_count::Epoch;
use crate::day_count::Fixed;
use crate::day_count::FromFixed;
use crate::day_count::ToFixed;
use std::num::NonZero;
const HOLOCENE_YEAR_OFFSET: i32 = -10000;
pub type HoloceneMonth = GregorianMonth;
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub struct Holocene(CommonDate);
impl AllowYearZero for Holocene {}
impl ToFromOrdinalDate for Holocene {
fn valid_ordinal(ord: OrdinalDate) -> Result<(), CalendarError> {
Gregorian::valid_ordinal(ord)
}
fn ordinal_from_fixed(fixed_date: Fixed) -> OrdinalDate {
let g_ord = Gregorian::ordinal_from_fixed(fixed_date);
OrdinalDate {
year: (g_ord.year - HOLOCENE_YEAR_OFFSET),
day_of_year: g_ord.day_of_year,
}
}
fn to_ordinal(self) -> OrdinalDate {
let g = Gregorian::try_from_common_date(self.to_common_date())
.expect("Same month/day validity");
g.to_ordinal()
}
fn from_ordinal_unchecked(ord: OrdinalDate) -> Self {
let e = Gregorian::from_ordinal_unchecked(ord);
Holocene::try_from_common_date(e.to_common_date()).expect("Same month/day validity")
}
}
impl HasLeapYears for Holocene {
fn is_leap(h_year: i32) -> bool {
Gregorian::is_leap(h_year) }
}
impl CalculatedBounds for Holocene {}
impl Epoch for Holocene {
fn epoch() -> Fixed {
Gregorian::try_year_start(HOLOCENE_YEAR_OFFSET + 1)
.expect("Year known to be valid")
.to_fixed()
}
}
impl FromFixed for Holocene {
fn from_fixed(date: Fixed) -> Holocene {
let result = Gregorian::from_fixed(date).to_common_date();
Holocene(CommonDate::new(
result.year - (HOLOCENE_YEAR_OFFSET as i32),
result.month,
result.day,
))
}
}
impl ToFixed for Holocene {
fn to_fixed(self) -> Fixed {
let g = Gregorian::try_from_common_date(CommonDate::new(
self.0.year + (HOLOCENE_YEAR_OFFSET as i32),
self.0.month,
self.0.day,
))
.expect("Same month/day rules");
g.to_fixed()
}
}
impl ToFromCommonDate<HoloceneMonth> for Holocene {
fn to_common_date(self) -> CommonDate {
self.0
}
fn from_common_date_unchecked(date: CommonDate) -> Self {
debug_assert!(Self::valid_ymd(date).is_ok());
Self(date)
}
fn valid_ymd(date: CommonDate) -> Result<(), CalendarError> {
Gregorian::valid_ymd(date)
}
fn year_end_date(year: i32) -> CommonDate {
Gregorian::year_end_date(year)
}
fn month_length(year: i32, month: HoloceneMonth) -> u8 {
Gregorian::month_length(year, month)
}
}
impl Quarter for Holocene {
fn quarter(self) -> NonZero<u8> {
NonZero::new(((self.to_common_date().month - 1) / 3) + 1).expect("(m-1)/3 > -1")
}
}
impl GuaranteedMonth<HoloceneMonth> for Holocene {}
impl CommonWeekOfYear<HoloceneMonth> for Holocene {}
pub type HoloceneMoment = CalendarMoment<Holocene>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn h_epoch() {
let dh = CommonDate {
year: 1,
month: 1,
day: 1,
};
let dg = CommonDate {
year: -9999,
month: 1,
day: 1,
};
let fh = Holocene::try_from_common_date(dh).unwrap().to_fixed();
let fg = Gregorian::try_from_common_date(dg).unwrap().to_fixed();
assert_eq!(fh, fg);
assert_eq!(fh, Holocene::epoch());
}
#[test]
fn g_epoch() {
let dh = CommonDate {
year: 10001,
month: 1,
day: 1,
};
let dg = CommonDate {
year: 1,
month: 1,
day: 1,
};
let fh = Holocene::try_from_common_date(dh).unwrap().to_fixed();
let fg = Gregorian::try_from_common_date(dg).unwrap().to_fixed();
assert_eq!(fh, fg);
assert_eq!(fh, Gregorian::epoch());
}
#[test]
fn date_of_proposal() {
let dh = CommonDate {
year: 11993,
month: 12,
day: 30,
};
let dg = CommonDate {
year: 1993,
month: 12,
day: 30,
};
let fh = Holocene::try_from_common_date(dh).unwrap().to_fixed();
let fg = Gregorian::try_from_common_date(dg).unwrap().to_fixed();
assert_eq!(fh, fg);
}
}