use std::fmt::{Debug, Display};
use chrono::{Datelike, NaiveDate};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ArvelieDate {
pub start_year: i32,
pub year: i32,
pub sub_year: SubYear,
}
impl ArvelieDate {
pub fn day_of_year(&self) -> u16 {
match self.sub_year {
SubYear::LeapDay => 366,
SubYear::YearDay => 365,
SubYear::NormalSubYear(sub_year) => {
sub_year.day as u16 + sub_year.month as u16 * 14 + 1
}
}
}
pub fn gregorian_year(&self) -> i32 {
self.start_year + self.year
}
}
impl Display for ArvelieDate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:0>2}", self.year)?;
write!(f, "{}", self.sub_year)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SubYear {
NormalSubYear(NormalSubYear),
YearDay,
LeapDay,
}
impl SubYear {
pub fn month(&self) -> u8 {
match self {
SubYear::NormalSubYear(normal_sub_year) => normal_sub_year.month,
SubYear::YearDay | SubYear::LeapDay => 26,
}
}
pub fn format_month(&self) -> String {
match self {
SubYear::NormalSubYear(normal_sub_year) => normal_sub_year.format_month(),
SubYear::YearDay | SubYear::LeapDay => "+".to_string(),
}
}
pub fn day(&self) -> u8 {
match self {
SubYear::NormalSubYear(normal_sub_year) => normal_sub_year.day,
SubYear::YearDay => 0,
SubYear::LeapDay => 1,
}
}
pub fn format_day(&self) -> String {
format!("{:0>2}", self.day())
}
}
impl Default for SubYear {
fn default() -> Self {
SubYear::NormalSubYear(NormalSubYear::default())
}
}
impl Display for SubYear {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SubYear::NormalSubYear(normal_sub_year) => write!(f, "{normal_sub_year}"),
SubYear::YearDay => f.write_str("+00"),
SubYear::LeapDay => f.write_str("+01"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct NormalSubYear {
pub month: u8,
pub day: u8,
}
impl NormalSubYear {
pub fn format_month(&self) -> String {
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars().collect::<Vec<char>>();
let letter = chars.get(self.month as usize).unwrap();
letter.to_string()
}
}
impl Display for NormalSubYear {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{:0>2}", self.format_month(), self.day)
}
}
pub struct NaiveDateAndStartYear {
pub date: NaiveDate,
pub start_year: i32,
}
impl From<NaiveDateAndStartYear> for ArvelieDate {
fn from(value: NaiveDateAndStartYear) -> Self {
let NaiveDateAndStartYear { date, start_year } = value;
let year = date.year() - start_year;
let day_of_year = date.ordinal0() as i32;
let sub_year = match day_of_year {
365 => SubYear::LeapDay,
364 => SubYear::YearDay,
_ => {
let month = day_of_year / 14;
let day = day_of_year - month * 14;
SubYear::NormalSubYear(NormalSubYear {
month: month as u8,
day: day as u8,
})
}
};
ArvelieDate {
start_year,
year,
sub_year,
}
}
}
impl From<ArvelieDate> for NaiveDate {
fn from(value: ArvelieDate) -> Self {
NaiveDate::from_yo_opt(value.gregorian_year(), value.day_of_year() as u32).unwrap()
}
}