1use crate::any_calendar::AnyCalendarKind;
35use crate::calendar_arithmetic::ArithmeticDate;
36use crate::iso::{Iso, IsoDateInner};
37use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time};
38use tinystr::tinystr;
39
40#[derive(Copy, Clone, Debug, Default)]
52#[allow(clippy::exhaustive_structs)] pub struct Gregorian;
54
55#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
56pub struct GregorianDateInner(IsoDateInner);
58
59impl Calendar for Gregorian {
60 type DateInner = GregorianDateInner;
61 fn date_from_codes(
62 &self,
63 era: types::Era,
64 year: i32,
65 month_code: types::MonthCode,
66 day: u8,
67 ) -> Result<Self::DateInner, CalendarError> {
68 let year = if era.0 == tinystr!(16, "ce") {
69 if year <= 0 {
70 return Err(CalendarError::OutOfRange);
71 }
72 year
73 } else if era.0 == tinystr!(16, "bce") {
74 if year <= 0 {
75 return Err(CalendarError::OutOfRange);
76 }
77 1 - year
78 } else {
79 return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
80 };
81
82 ArithmeticDate::new_from_codes(self, year, month_code, day)
83 .map(IsoDateInner)
84 .map(GregorianDateInner)
85 }
86
87 fn date_from_iso(&self, iso: Date<Iso>) -> GregorianDateInner {
88 GregorianDateInner(*iso.inner())
89 }
90
91 fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
92 Date::from_raw(date.0, Iso)
93 }
94
95 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
96 Iso.months_in_year(&date.0)
97 }
98
99 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
100 Iso.days_in_year(&date.0)
101 }
102
103 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
104 Iso.days_in_month(&date.0)
105 }
106
107 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
108 Iso.offset_date(&mut date.0, offset.cast_unit())
109 }
110
111 #[allow(clippy::field_reassign_with_default)] fn until(
113 &self,
114 date1: &Self::DateInner,
115 date2: &Self::DateInner,
116 _calendar2: &Self,
117 largest_unit: DateDurationUnit,
118 smallest_unit: DateDurationUnit,
119 ) -> DateDuration<Self> {
120 Iso.until(&date1.0, &date2.0, &Iso, largest_unit, smallest_unit)
121 .cast_unit()
122 }
123
124 fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
126 year_as_gregorian(date.0 .0.year)
127 }
128
129 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
130 Iso.is_in_leap_year(&date.0)
131 }
132
133 fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
135 Iso.month(&date.0)
136 }
137
138 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
140 Iso.day_of_month(&date.0)
141 }
142
143 fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
145 let prev_year = date.0 .0.year.saturating_sub(1);
146 let next_year = date.0 .0.year.saturating_add(1);
147 types::DayOfYearInfo {
148 day_of_year: Iso::day_of_year(date.0),
149 days_in_year: Iso::days_in_year_direct(date.0 .0.year),
150 prev_year: year_as_gregorian(prev_year),
151 days_in_prev_year: Iso::days_in_year_direct(prev_year),
152 next_year: year_as_gregorian(next_year),
153 }
154 }
155
156 fn debug_name(&self) -> &'static str {
157 "Gregorian"
158 }
159
160 fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
161 Some(AnyCalendarKind::Gregorian)
162 }
163}
164
165impl Date<Gregorian> {
166 pub fn try_new_gregorian_date(
182 year: i32,
183 month: u8,
184 day: u8,
185 ) -> Result<Date<Gregorian>, CalendarError> {
186 Date::try_new_iso_date(year, month, day).map(|d| Date::new_from_iso(d, Gregorian))
187 }
188}
189
190impl DateTime<Gregorian> {
191 pub fn try_new_gregorian_datetime(
210 year: i32,
211 month: u8,
212 day: u8,
213 hour: u8,
214 minute: u8,
215 second: u8,
216 ) -> Result<DateTime<Gregorian>, CalendarError> {
217 Ok(DateTime {
218 date: Date::try_new_gregorian_date(year, month, day)?,
219 time: Time::try_new(hour, minute, second, 0)?,
220 })
221 }
222}
223
224pub(crate) fn year_as_gregorian(year: i32) -> types::FormattableYear {
225 if year > 0 {
226 types::FormattableYear {
227 era: types::Era(tinystr!(16, "ce")),
228 number: year,
229 cyclic: None,
230 related_iso: None,
231 }
232 } else {
233 types::FormattableYear {
234 era: types::Era(tinystr!(16, "bce")),
235 number: 1_i32.saturating_sub(year),
236 cyclic: None,
237 related_iso: None,
238 }
239 }
240}
241
242#[cfg(test)]
243mod test {
244 use calendrical_calculations::rata_die::RataDie;
245
246 use super::*;
247 use types::Era;
248
249 #[test]
250 fn day_of_year_info_max() {
251 #[derive(Debug)]
252 struct MaxCase {
253 year: i32,
254 month: u8,
255 day: u8,
256 next_era_year: i32,
257 era: &'static str,
258 }
259 let cases = [
260 MaxCase {
261 year: i32::MAX,
262 month: 7,
263 day: 11,
264 next_era_year: i32::MAX,
265 era: "ce",
266 },
267 MaxCase {
268 year: i32::MAX,
269 month: 7,
270 day: 12,
271 next_era_year: i32::MAX,
272 era: "ce",
273 },
274 MaxCase {
275 year: i32::MAX,
276 month: 8,
277 day: 10,
278 next_era_year: i32::MAX,
279 era: "ce",
280 },
281 MaxCase {
282 year: i32::MAX - 1,
283 month: 7,
284 day: 11,
285 next_era_year: i32::MAX,
286 era: "ce",
287 },
288 MaxCase {
289 year: -2,
290 month: 1,
291 day: 1,
292 next_era_year: 2,
293 era: "bce",
294 },
295 MaxCase {
296 year: -1,
297 month: 1,
298 day: 1,
299 next_era_year: 1,
300 era: "bce",
301 },
302 MaxCase {
303 year: 0,
304 month: 1,
305 day: 1,
306 next_era_year: 1,
307 era: "ce",
308 },
309 MaxCase {
310 year: 1,
311 month: 1,
312 day: 1,
313 next_era_year: 2,
314 era: "ce",
315 },
316 MaxCase {
317 year: 2000,
318 month: 6,
319 day: 15,
320 next_era_year: 2001,
321 era: "ce",
322 },
323 MaxCase {
324 year: 2020,
325 month: 12,
326 day: 31,
327 next_era_year: 2021,
328 era: "ce",
329 },
330 ];
331
332 for case in cases {
333 let date = Date::try_new_gregorian_date(case.year, case.month, case.day).unwrap();
334
335 assert_eq!(
336 Calendar::day_of_year_info(&Gregorian, &date.inner)
337 .next_year
338 .number,
339 case.next_era_year,
340 "{case:?}",
341 );
342 assert_eq!(
343 Calendar::day_of_year_info(&Gregorian, &date.inner)
344 .next_year
345 .era
346 .0,
347 case.era,
348 "{case:?}",
349 );
350 }
351 }
352
353 #[derive(Debug)]
354 struct TestCase {
355 fixed_date: RataDie,
356 iso_year: i32,
357 iso_month: u8,
358 iso_day: u8,
359 expected_year: i32,
360 expected_era: Era,
361 expected_month: u32,
362 expected_day: u32,
363 }
364
365 fn check_test_case(case: TestCase) {
366 let iso_from_fixed: Date<Iso> = Iso::iso_from_fixed(case.fixed_date);
367 let greg_date_from_fixed: Date<Gregorian> = Date::new_from_iso(iso_from_fixed, Gregorian);
368 assert_eq!(greg_date_from_fixed.year().number, case.expected_year,
369 "Failed year check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
370 assert_eq!(greg_date_from_fixed.year().era, case.expected_era,
371 "Failed era check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
372 assert_eq!(greg_date_from_fixed.month().ordinal, case.expected_month,
373 "Failed month check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
374 assert_eq!(greg_date_from_fixed.day_of_month().0, case.expected_day,
375 "Failed day check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
376
377 let iso_date_man: Date<Iso> =
378 Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day)
379 .expect("Failed to initialize ISO date for {case:?}");
380 let greg_date_man: Date<Gregorian> = Date::new_from_iso(iso_date_man, Gregorian);
381 assert_eq!(iso_from_fixed, iso_date_man,
382 "ISO from fixed not equal to ISO generated from manually-input ymd\nCase: {case:?}\nFixed: {iso_from_fixed:?}\nMan: {iso_date_man:?}");
383 assert_eq!(greg_date_from_fixed, greg_date_man,
384 "Greg. date from fixed not equal to Greg. generated from manually-input ymd\nCase: {case:?}\nFixed: {greg_date_from_fixed:?}\nMan: {greg_date_man:?}");
385 }
386
387 #[test]
388 fn test_gregorian_ce() {
389 let cases = [
393 TestCase {
394 fixed_date: RataDie::new(1),
395 iso_year: 1,
396 iso_month: 1,
397 iso_day: 1,
398 expected_year: 1,
399 expected_era: Era(tinystr!(16, "ce")),
400 expected_month: 1,
401 expected_day: 1,
402 },
403 TestCase {
404 fixed_date: RataDie::new(181),
405 iso_year: 1,
406 iso_month: 6,
407 iso_day: 30,
408 expected_year: 1,
409 expected_era: Era(tinystr!(16, "ce")),
410 expected_month: 6,
411 expected_day: 30,
412 },
413 TestCase {
414 fixed_date: RataDie::new(1155),
415 iso_year: 4,
416 iso_month: 2,
417 iso_day: 29,
418 expected_year: 4,
419 expected_era: Era(tinystr!(16, "ce")),
420 expected_month: 2,
421 expected_day: 29,
422 },
423 TestCase {
424 fixed_date: RataDie::new(1344),
425 iso_year: 4,
426 iso_month: 9,
427 iso_day: 5,
428 expected_year: 4,
429 expected_era: Era(tinystr!(16, "ce")),
430 expected_month: 9,
431 expected_day: 5,
432 },
433 TestCase {
434 fixed_date: RataDie::new(36219),
435 iso_year: 100,
436 iso_month: 3,
437 iso_day: 1,
438 expected_year: 100,
439 expected_era: Era(tinystr!(16, "ce")),
440 expected_month: 3,
441 expected_day: 1,
442 },
443 ];
444
445 for case in cases {
446 check_test_case(case);
447 }
448 }
449
450 #[test]
451 fn day_of_year_info_min() {
452 #[derive(Debug)]
453 struct MinCase {
454 year: i32,
455 month: u8,
456 day: u8,
457 prev_era_year: i32,
458 era: &'static str,
459 }
460 let cases = [
461 MinCase {
462 year: i32::MIN + 4,
463 month: 1,
464 day: 1,
465 prev_era_year: i32::MAX - 1,
466 era: "bce",
467 },
468 MinCase {
469 year: i32::MIN + 3,
470 month: 12,
471 day: 31,
472 prev_era_year: i32::MAX,
473 era: "bce",
474 },
475 MinCase {
476 year: i32::MIN + 2,
477 month: 2,
478 day: 2,
479 prev_era_year: i32::MAX,
480 era: "bce",
481 },
482 MinCase {
483 year: i32::MIN + 1,
484 month: 1,
485 day: 1,
486 prev_era_year: i32::MAX,
487 era: "bce",
488 },
489 MinCase {
490 year: i32::MIN,
491 month: 1,
492 day: 1,
493 prev_era_year: i32::MAX,
494 era: "bce",
495 },
496 MinCase {
497 year: 3,
498 month: 1,
499 day: 1,
500 prev_era_year: 2,
501 era: "ce",
502 },
503 MinCase {
504 year: 2,
505 month: 1,
506 day: 1,
507 prev_era_year: 1,
508 era: "ce",
509 },
510 MinCase {
511 year: 1,
512 month: 1,
513 day: 1,
514 prev_era_year: 1,
515 era: "bce",
516 },
517 MinCase {
518 year: 0,
519 month: 1,
520 day: 1,
521 prev_era_year: 2,
522 era: "bce",
523 },
524 MinCase {
525 year: -2000,
526 month: 6,
527 day: 15,
528 prev_era_year: 2002,
529 era: "bce",
530 },
531 MinCase {
532 year: 2020,
533 month: 12,
534 day: 31,
535 prev_era_year: 2019,
536 era: "ce",
537 },
538 ];
539
540 for case in cases {
541 let date = Date::try_new_gregorian_date(case.year, case.month, case.day).unwrap();
542
543 assert_eq!(
544 Calendar::day_of_year_info(&Gregorian, &date.inner)
545 .prev_year
546 .number,
547 case.prev_era_year,
548 "{case:?}",
549 );
550 assert_eq!(
551 Calendar::day_of_year_info(&Gregorian, &date.inner)
552 .prev_year
553 .era
554 .0,
555 case.era,
556 "{case:?}",
557 );
558 }
559 }
560
561 #[test]
562 fn test_gregorian_bce() {
563 let cases = [
567 TestCase {
568 fixed_date: RataDie::new(0),
569 iso_year: 0,
570 iso_month: 12,
571 iso_day: 31,
572 expected_year: 1,
573 expected_era: Era(tinystr!(16, "bce")),
574 expected_month: 12,
575 expected_day: 31,
576 },
577 TestCase {
578 fixed_date: RataDie::new(-365), iso_year: 0,
580 iso_month: 1,
581 iso_day: 1,
582 expected_year: 1,
583 expected_era: Era(tinystr!(16, "bce")),
584 expected_month: 1,
585 expected_day: 1,
586 },
587 TestCase {
588 fixed_date: RataDie::new(-366),
589 iso_year: -1,
590 iso_month: 12,
591 iso_day: 31,
592 expected_year: 2,
593 expected_era: Era(tinystr!(16, "bce")),
594 expected_month: 12,
595 expected_day: 31,
596 },
597 TestCase {
598 fixed_date: RataDie::new(-1461),
599 iso_year: -4,
600 iso_month: 12,
601 iso_day: 31,
602 expected_year: 5,
603 expected_era: Era(tinystr!(16, "bce")),
604 expected_month: 12,
605 expected_day: 31,
606 },
607 TestCase {
608 fixed_date: RataDie::new(-1826),
609 iso_year: -4,
610 iso_month: 1,
611 iso_day: 1,
612 expected_year: 5,
613 expected_era: Era(tinystr!(16, "bce")),
614 expected_month: 1,
615 expected_day: 1,
616 },
617 ];
618
619 for case in cases {
620 check_test_case(case);
621 }
622 }
623
624 #[test]
625 fn check_gregorian_directionality() {
626 for i in -100..100 {
630 for j in -100..100 {
631 let iso_i: Date<Iso> = Iso::iso_from_fixed(RataDie::new(i));
632 let iso_j: Date<Iso> = Iso::iso_from_fixed(RataDie::new(j));
633
634 let greg_i: Date<Gregorian> = Date::new_from_iso(iso_i, Gregorian);
635 let greg_j: Date<Gregorian> = Date::new_from_iso(iso_j, Gregorian);
636
637 assert_eq!(
638 i.cmp(&j),
639 iso_i.cmp(&iso_j),
640 "ISO directionality inconsistent with directionality for i: {i}, j: {j}"
641 );
642 assert_eq!(
643 i.cmp(&j),
644 greg_i.cmp(&greg_j),
645 "Gregorian directionality inconsistent with directionality for i: {i}, j: {j}"
646 );
647 }
648 }
649 }
650}