1use crate::cal::iso::{Iso, IsoDateInner};
20use crate::calendar_arithmetic::ArithmeticDate;
21use crate::error::{year_check, DateError};
22use crate::{types, Calendar, Date, DateDuration, DateDurationUnit, RangeError};
23use calendrical_calculations::rata_die::RataDie;
24use tinystr::tinystr;
25
26#[derive(Copy, Clone, Debug, Default)]
40#[allow(clippy::exhaustive_structs)] pub struct Gregorian;
42
43#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
44pub struct GregorianDateInner(pub(crate) IsoDateInner);
46
47impl crate::cal::scaffold::UnstableSealed for Gregorian {}
48impl Calendar for Gregorian {
49 type DateInner = GregorianDateInner;
50 type Year = types::EraYear;
51 fn from_codes(
52 &self,
53 era: Option<&str>,
54 year: i32,
55 month_code: types::MonthCode,
56 day: u8,
57 ) -> Result<Self::DateInner, DateError> {
58 let year = match era {
59 Some("bce" | "bc") | None => 1 - year_check(year, 1..)?,
60 Some("ad" | "ce") => year_check(year, 1..)?,
61 Some(_) => return Err(DateError::UnknownEra),
62 };
63
64 ArithmeticDate::new_from_codes(self, year, month_code, day)
65 .map(IsoDateInner)
66 .map(GregorianDateInner)
67 }
68
69 fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
70 GregorianDateInner(Iso.from_rata_die(rd))
71 }
72
73 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
74 Iso.to_rata_die(&date.0)
75 }
76
77 fn from_iso(&self, iso: IsoDateInner) -> GregorianDateInner {
78 GregorianDateInner(iso)
79 }
80
81 fn to_iso(&self, date: &Self::DateInner) -> IsoDateInner {
82 date.0
83 }
84
85 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
86 Iso.months_in_year(&date.0)
87 }
88
89 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
90 Iso.days_in_year(&date.0)
91 }
92
93 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
94 Iso.days_in_month(&date.0)
95 }
96
97 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
98 Iso.offset_date(&mut date.0, offset.cast_unit())
99 }
100
101 #[allow(clippy::field_reassign_with_default)] fn until(
103 &self,
104 date1: &Self::DateInner,
105 date2: &Self::DateInner,
106 _calendar2: &Self,
107 largest_unit: DateDurationUnit,
108 smallest_unit: DateDurationUnit,
109 ) -> DateDuration<Self> {
110 Iso.until(&date1.0, &date2.0, &Iso, largest_unit, smallest_unit)
111 .cast_unit()
112 }
113 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
115 let extended_year = self.extended_year(date);
116 if extended_year > 0 {
117 types::EraYear {
118 era: tinystr!(16, "ce"),
119 era_index: Some(1),
120 year: extended_year,
121 ambiguity: match extended_year {
122 ..=999 => types::YearAmbiguity::EraAndCenturyRequired,
123 1000..=1949 => types::YearAmbiguity::CenturyRequired,
124 1950..=2049 => types::YearAmbiguity::Unambiguous,
125 2050.. => types::YearAmbiguity::CenturyRequired,
126 },
127 }
128 } else {
129 types::EraYear {
130 era: tinystr!(16, "bce"),
131 era_index: Some(0),
132 year: 1_i32.saturating_sub(extended_year),
133 ambiguity: types::YearAmbiguity::EraAndCenturyRequired,
134 }
135 }
136 }
137
138 fn extended_year(&self, date: &Self::DateInner) -> i32 {
139 Iso.extended_year(&date.0)
140 }
141
142 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
143 Iso.is_in_leap_year(&date.0)
144 }
145
146 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
148 Iso.month(&date.0)
149 }
150
151 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
153 Iso.day_of_month(&date.0)
154 }
155
156 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
158 date.0 .0.day_of_year()
159 }
160
161 fn debug_name(&self) -> &'static str {
162 "Gregorian"
163 }
164
165 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
166 Some(crate::preferences::CalendarAlgorithm::Gregory)
167 }
168}
169
170impl Date<Gregorian> {
171 pub fn try_new_gregorian(year: i32, month: u8, day: u8) -> Result<Date<Gregorian>, RangeError> {
187 Date::try_new_iso(year, month, day).map(|d| Date::new_from_iso(d, Gregorian))
188 }
189}
190
191#[cfg(test)]
192mod test {
193 use calendrical_calculations::rata_die::RataDie;
194
195 use super::*;
196
197 #[derive(Debug)]
198 struct TestCase {
199 rd: RataDie,
200 iso_year: i32,
201 iso_month: u8,
202 iso_day: u8,
203 expected_year: i32,
204 expected_era: &'static str,
205 expected_month: u8,
206 expected_day: u8,
207 }
208
209 fn check_test_case(case: TestCase) {
210 let iso_from_rd = Date::from_rata_die(case.rd, Iso);
211 let greg_date_from_rd = Date::from_rata_die(case.rd, Gregorian);
212 assert_eq!(greg_date_from_rd.era_year().year, case.expected_year,
213 "Failed year check from RD: {case:?}\nISO: {iso_from_rd:?}\nGreg: {greg_date_from_rd:?}");
214 assert_eq!(
215 greg_date_from_rd.era_year().era,
216 case.expected_era,
217 "Failed era check from RD: {case:?}\nISO: {iso_from_rd:?}\nGreg: {greg_date_from_rd:?}"
218 );
219 assert_eq!(greg_date_from_rd.month().ordinal, case.expected_month,
220 "Failed month check from RD: {case:?}\nISO: {iso_from_rd:?}\nGreg: {greg_date_from_rd:?}");
221 assert_eq!(
222 greg_date_from_rd.day_of_month().0,
223 case.expected_day,
224 "Failed day check from RD: {case:?}\nISO: {iso_from_rd:?}\nGreg: {greg_date_from_rd:?}"
225 );
226
227 let iso_date_man: Date<Iso> =
228 Date::try_new_iso(case.iso_year, case.iso_month, case.iso_day)
229 .expect("Failed to initialize ISO date for {case:?}");
230 let greg_date_man: Date<Gregorian> = Date::new_from_iso(iso_date_man, Gregorian);
231 assert_eq!(iso_from_rd, iso_date_man,
232 "ISO from RD not equal to ISO generated from manually-input ymd\nCase: {case:?}\nRD: {iso_from_rd:?}\nMan: {iso_date_man:?}");
233 assert_eq!(greg_date_from_rd, greg_date_man,
234 "Greg. date from RD not equal to Greg. generated from manually-input ymd\nCase: {case:?}\nRD: {greg_date_from_rd:?}\nMan: {greg_date_man:?}");
235 }
236
237 #[test]
238 fn test_gregorian_ce() {
239 let cases = [
243 TestCase {
244 rd: RataDie::new(1),
245 iso_year: 1,
246 iso_month: 1,
247 iso_day: 1,
248 expected_year: 1,
249 expected_era: "ce",
250 expected_month: 1,
251 expected_day: 1,
252 },
253 TestCase {
254 rd: RataDie::new(181),
255 iso_year: 1,
256 iso_month: 6,
257 iso_day: 30,
258 expected_year: 1,
259 expected_era: "ce",
260 expected_month: 6,
261 expected_day: 30,
262 },
263 TestCase {
264 rd: RataDie::new(1155),
265 iso_year: 4,
266 iso_month: 2,
267 iso_day: 29,
268 expected_year: 4,
269 expected_era: "ce",
270 expected_month: 2,
271 expected_day: 29,
272 },
273 TestCase {
274 rd: RataDie::new(1344),
275 iso_year: 4,
276 iso_month: 9,
277 iso_day: 5,
278 expected_year: 4,
279 expected_era: "ce",
280 expected_month: 9,
281 expected_day: 5,
282 },
283 TestCase {
284 rd: RataDie::new(36219),
285 iso_year: 100,
286 iso_month: 3,
287 iso_day: 1,
288 expected_year: 100,
289 expected_era: "ce",
290 expected_month: 3,
291 expected_day: 1,
292 },
293 ];
294
295 for case in cases {
296 check_test_case(case);
297 }
298 }
299
300 #[test]
301 fn test_gregorian_bce() {
302 let cases = [
306 TestCase {
307 rd: RataDie::new(0),
308 iso_year: 0,
309 iso_month: 12,
310 iso_day: 31,
311 expected_year: 1,
312 expected_era: "bce",
313 expected_month: 12,
314 expected_day: 31,
315 },
316 TestCase {
317 rd: RataDie::new(-365), iso_year: 0,
319 iso_month: 1,
320 iso_day: 1,
321 expected_year: 1,
322 expected_era: "bce",
323 expected_month: 1,
324 expected_day: 1,
325 },
326 TestCase {
327 rd: RataDie::new(-366),
328 iso_year: -1,
329 iso_month: 12,
330 iso_day: 31,
331 expected_year: 2,
332 expected_era: "bce",
333 expected_month: 12,
334 expected_day: 31,
335 },
336 TestCase {
337 rd: RataDie::new(-1461),
338 iso_year: -4,
339 iso_month: 12,
340 iso_day: 31,
341 expected_year: 5,
342 expected_era: "bce",
343 expected_month: 12,
344 expected_day: 31,
345 },
346 TestCase {
347 rd: RataDie::new(-1826),
348 iso_year: -4,
349 iso_month: 1,
350 iso_day: 1,
351 expected_year: 5,
352 expected_era: "bce",
353 expected_month: 1,
354 expected_day: 1,
355 },
356 ];
357
358 for case in cases {
359 check_test_case(case);
360 }
361 }
362
363 #[test]
364 fn check_gregorian_directionality() {
365 for i in -100..100 {
369 for j in -100..100 {
370 let iso_i = Date::from_rata_die(RataDie::new(i), Iso);
371 let iso_j = Date::from_rata_die(RataDie::new(j), Iso);
372
373 let greg_i = iso_i.to_calendar(Gregorian);
374 let greg_j = iso_j.to_calendar(Gregorian);
375
376 assert_eq!(
377 i.cmp(&j),
378 iso_i.cmp(&iso_j),
379 "ISO directionality inconsistent with directionality for i: {i}, j: {j}"
380 );
381 assert_eq!(
382 i.cmp(&j),
383 greg_i.cmp(&greg_j),
384 "Gregorian directionality inconsistent with directionality for i: {i}, j: {j}"
385 );
386 }
387 }
388 }
389}