1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub struct Date {
5 year0: i64,
6 month0: i64,
7 day0: i64,
8}
9
10impl Date {
11 pub fn from_timestamp(timestamp: Timestamp) -> Self {
12 let year0 = ts_to_year0(timestamp.seconds);
13 let seconds_in_year = timestamp.seconds - get_year_start0(year0);
14
15 let month0 = seconds_in_year.div_euclid(SECONDS_PER_MONTH);
16 let seconds_in_month = seconds_in_year.rem_euclid(SECONDS_PER_MONTH);
17
18 let day0 = seconds_in_month.div_euclid(SECONDS_PER_DAY);
19
20 Self {
21 year0,
22 month0,
23 day0,
24 }
25 }
26
27 pub fn from_ymd(year: i64, month: i64, day: i64) -> Self {
34 let year0 = match year.cmp(&0) {
35 std::cmp::Ordering::Greater => year - 1,
36 std::cmp::Ordering::Less => year,
37 std::cmp::Ordering::Equal => panic!("year cannot be 0"),
38 };
39 assert!((1..=13).contains(&month), "month must be in [1, 13]");
40 assert!((1..=30).contains(&day), "day must be in [1, 30]");
41 let month0 = month - 1;
42 let day0 = day - 1;
43 Self {
44 year0,
45 month0,
46 day0,
47 }
48 }
49
50 pub fn from_ymd0(year0: i64, month0: i64, day0: i64) -> Self {
56 assert!((0..=12).contains(&month0), "month0 must be in [0, 12]");
57 assert!((0..=29).contains(&day0), "day0 must be in [0, 29]");
58 Self {
59 year0,
60 month0,
61 day0,
62 }
63 }
64
65 pub fn franciade0(&self) -> i64 {
71 ((self.year0 + 2) / 4) - 1
72 }
73
74 pub fn franciade(&self) -> i64 {
80 let franciade0 = self.franciade0();
81 if franciade0 >= 0 {
82 franciade0 + 1
83 } else {
84 franciade0
85 }
86 }
87
88 pub fn year0(&self) -> i64 {
90 self.year0
91 }
92
93 pub fn year(&self) -> i64 {
95 if self.year0 >= 0 {
96 self.year0 + 1
97 } else {
98 self.year0
99 }
100 }
101
102 pub fn num_month0(&self) -> i64 {
105 self.month0
106 }
107
108 pub fn num_month(&self) -> i64 {
111 self.num_month0() + 1
112 }
113
114 pub fn month(&self) -> Month {
116 Month::from_num0(self.month0)
117 }
118
119 pub fn day0(&self) -> i64 {
121 self.day0
122 }
123
124 pub fn day(&self) -> i64 {
126 self.day0() + 1
127 }
128
129 pub fn day_name(&self) -> &'static str {
131 day_name(self.month(), self.day())
132 }
133
134 pub fn day_name_with_article(&self) -> &'static str {
136 day_name_with_article(self.month(), self.day())
137 }
138
139 pub fn decade0(&self) -> i64 {
141 self.day0().div_euclid(DAYS_PER_DECADE)
142 }
143
144 pub fn decade(&self) -> i64 {
146 self.decade0() + 1
147 }
148
149 pub fn num_decade_day0(&self) -> i64 {
151 self.day0().rem_euclid(DAYS_PER_DECADE)
152 }
153
154 pub fn num_decade_day(&self) -> i64 {
156 self.num_decade_day0() + 1
157 }
158
159 pub fn decade_day(&self) -> Day {
160 if self.month0 == 12 {
161 Day::Sansculottide(SansculottideDay::from_num0(self.num_decade_day0()))
162 } else {
163 Day::Regular(RegularDay::from_num0(self.num_decade_day0()))
164 }
165 }
166
167 pub fn timestamp(&self) -> Timestamp {
169 Timestamp {
170 seconds: get_year_start0(self.year0)
171 + self.month0 * SECONDS_PER_MONTH
172 + self.day0 * SECONDS_PER_DAY,
173 }
174 }
175
176 fn fmt_default(&self, f: &mut impl std::io::Write) -> std::io::Result<()> {
177 write!(
178 f,
179 "{} {} {} {}",
180 self.decade_day(),
181 self.day(),
182 self.month(),
183 self.year(),
184 )
185 }
186
187 fn fmt_traditional(&self, f: &mut impl std::io::Write) -> std::io::Result<()> {
188 let mut remaining_years = self.year();
189 let thousand_years = remaining_years.div_euclid(1000);
190 remaining_years -= thousand_years * 1000;
191 let thousand_years = "M".repeat(thousand_years as usize);
192 let five_hundred_years = remaining_years.div_euclid(500);
193 remaining_years -= five_hundred_years * 500;
194 let five_hundred_years = match five_hundred_years {
195 0 => "",
196 1 => "D",
197 _ => unreachable!(),
198 };
199 let hundred_years = remaining_years.div_euclid(100);
200 remaining_years -= hundred_years * 100;
201 let hundred_years = match hundred_years {
202 0 => "",
203 1 => "C",
204 2 => "CC",
205 3 => "CCC",
206 4 => "CD",
207 _ => unreachable!(),
208 };
209 let fifty_years = remaining_years.div_euclid(50);
210 remaining_years -= fifty_years * 50;
211 let fifty_years = match fifty_years {
212 0 => "",
213 1 => "L",
214 _ => unreachable!(),
215 };
216 let ten_years = remaining_years.div_euclid(10);
217 remaining_years -= ten_years * 10;
218 let ten_years = match ten_years {
219 0 => "",
220 1 => "X",
221 2 => "XX",
222 3 => "XXX",
223 4 => "XL",
224 _ => unreachable!(),
225 };
226 let five_years = remaining_years.div_euclid(5);
227 remaining_years -= five_years * 5;
228 let five_years = match five_years {
229 0 => "",
230 1 => "V",
231 _ => unreachable!(),
232 };
233 let one_year = remaining_years;
234 let one_year = match one_year {
235 0 => "",
236 1 => "I",
237 2 => "II",
238 3 => "III",
239 4 => "IV",
240 _ => unreachable!(),
241 };
242
243 write!(
244 f,
245 "{} {} {} an {}{}{}{}{}{}{}",
246 self.decade_day(),
247 self.day(),
248 self.month(),
249 thousand_years,
250 five_hundred_years,
251 hundred_years,
252 fifty_years,
253 ten_years,
254 five_years,
255 one_year
256 )
257 }
258
259 pub fn to_string_default(&self) -> String {
260 let mut s = Vec::new();
261 self.fmt_default(&mut s).unwrap();
262 String::from_utf8(s).unwrap()
263 }
264
265 pub fn to_string_traditional(&self) -> String {
266 let mut s = Vec::new();
267 self.fmt_traditional(&mut s).unwrap();
268 String::from_utf8(s).unwrap()
269 }
270}