use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::ArithmeticDate;
use crate::iso::{Iso, IsoDateInner};
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use tinystr::tinystr;
#[derive(Copy, Clone, Debug, Default)]
#[allow(clippy::exhaustive_structs)] pub struct Gregorian;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct GregorianDateInner(IsoDateInner);
impl Calendar for Gregorian {
type DateInner = GregorianDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
let year = if era.0 == tinystr!(16, "ce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
year
} else if era.0 == tinystr!(16, "bce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
1 - year
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
ArithmeticDate::new_from_solar(self, year, month_code, day)
.map(IsoDateInner)
.map(GregorianDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> GregorianDateInner {
GregorianDateInner(*iso.inner())
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
Date::from_raw(date.0, Iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
Iso.months_in_year(&date.0)
}
fn days_in_year(&self, date: &Self::DateInner) -> u32 {
Iso.days_in_year(&date.0)
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
Iso.days_in_month(&date.0)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
Iso.offset_date(&mut date.0, offset.cast_unit())
}
#[allow(clippy::field_reassign_with_default)] fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
Iso.until(&date1.0, &date2.0, &Iso, largest_unit, smallest_unit)
.cast_unit()
}
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
year_as_gregorian(date.0 .0.year)
}
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
Iso.month(&date.0)
}
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
Iso.day_of_month(&date.0)
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0 .0.year - 1;
let next_year = date.0 .0.year + 1;
types::DayOfYearInfo {
day_of_year: Iso::day_of_year(date.0),
days_in_year: Iso::days_in_year_direct(date.0 .0.year),
prev_year: year_as_gregorian(prev_year),
days_in_prev_year: Iso::days_in_year_direct(prev_year),
next_year: year_as_gregorian(next_year),
}
}
fn debug_name(&self) -> &'static str {
"Gregorian"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Gregorian)
}
}
impl Date<Gregorian> {
pub fn try_new_gregorian_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Gregorian>, CalendarError> {
Date::try_new_iso_date(year, month, day).map(|d| Date::new_from_iso(d, Gregorian))
}
}
impl DateTime<Gregorian> {
pub fn try_new_gregorian_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Gregorian>, CalendarError> {
Ok(DateTime {
date: Date::try_new_gregorian_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
pub(crate) fn year_as_gregorian(year: i32) -> types::FormattableYear {
if year > 0 {
types::FormattableYear {
era: types::Era(tinystr!(16, "ce")),
number: year,
related_iso: None,
}
} else {
types::FormattableYear {
era: types::Era(tinystr!(16, "bce")),
number: 1 - year,
related_iso: None,
}
}
}