1use std::fmt::{Debug, Display};
2
3use chrono::{Datelike, NaiveDate};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
7pub struct ArvelieDate {
8 pub start_year: i32,
10 pub year: i32,
12 pub sub_year: SubYear,
14}
15impl ArvelieDate {
16 pub fn day_of_year(&self) -> u16 {
18 match self.sub_year {
19 SubYear::LeapDay => 366,
20 SubYear::YearDay => 365,
21 SubYear::NormalSubYear(sub_year) => {
22 sub_year.day as u16 + sub_year.month as u16 * 14 + 1
23 }
24 }
25 }
26 pub fn gregorian_year(&self) -> i32 {
28 self.start_year + self.year
29 }
30}
31impl Display for ArvelieDate {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(f, "{:0>2}", self.year)?;
34 write!(f, "{}", self.sub_year)
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub enum SubYear {
41 NormalSubYear(NormalSubYear),
43 YearDay,
45 LeapDay,
47}
48impl SubYear {
49 pub fn month(&self) -> u8 {
51 match self {
52 SubYear::NormalSubYear(normal_sub_year) => normal_sub_year.month,
53 SubYear::YearDay | SubYear::LeapDay => 26,
54 }
55 }
56 pub fn format_month(&self) -> String {
58 match self {
59 SubYear::NormalSubYear(normal_sub_year) => normal_sub_year.format_month(),
60 SubYear::YearDay | SubYear::LeapDay => "+".to_string(),
61 }
62 }
63 pub fn day(&self) -> u8 {
65 match self {
66 SubYear::NormalSubYear(normal_sub_year) => normal_sub_year.day,
67 SubYear::YearDay => 0,
68 SubYear::LeapDay => 1,
69 }
70 }
71 pub fn format_day(&self) -> String {
73 format!("{:0>2}", self.day())
74 }
75}
76impl Default for SubYear {
77 fn default() -> Self {
78 SubYear::NormalSubYear(NormalSubYear::default())
79 }
80}
81impl Display for SubYear {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 SubYear::NormalSubYear(normal_sub_year) => write!(f, "{normal_sub_year}"),
85 SubYear::YearDay => f.write_str("+00"),
86 SubYear::LeapDay => f.write_str("+01"),
87 }
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
93pub struct NormalSubYear {
94 pub month: u8,
96 pub day: u8,
98}
99impl NormalSubYear {
100 pub fn format_month(&self) -> String {
102 let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars().collect::<Vec<char>>();
103 let letter = chars.get(self.month as usize).unwrap();
104 letter.to_string()
105 }
106}
107impl Display for NormalSubYear {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 write!(f, "{}{:0>2}", self.format_month(), self.day)
110 }
111}
112
113pub struct NaiveDateAndStartYear {
115 pub date: NaiveDate,
117 pub start_year: i32,
119}
120
121impl From<NaiveDateAndStartYear> for ArvelieDate {
122 fn from(value: NaiveDateAndStartYear) -> Self {
123 let NaiveDateAndStartYear { date, start_year } = value;
124 let year = date.year() - start_year;
125 let day_of_year = date.ordinal0() as i32;
126 let sub_year = match day_of_year {
127 365 => SubYear::LeapDay,
128 364 => SubYear::YearDay,
129 _ => {
130 let month = day_of_year / 14;
131 let day = day_of_year - month * 14;
132 SubYear::NormalSubYear(NormalSubYear {
133 month: month as u8,
134 day: day as u8,
135 })
136 }
137 };
138
139 ArvelieDate {
140 start_year,
141 year,
142 sub_year,
143 }
144 }
145}
146
147impl From<ArvelieDate> for NaiveDate {
148 fn from(value: ArvelieDate) -> Self {
149 NaiveDate::from_yo_opt(value.gregorian_year(), value.day_of_year() as u32).unwrap()
150 }
151}