1use crate::any_calendar::AnyCalendarKind;
43use crate::calendar_arithmetic::CalendarArithmetic;
44use crate::calendar_arithmetic::PrecomputedDataSource;
45use crate::chinese_based::{
46 chinese_based_ordinal_lunar_month_from_code, ChineseBasedDateInner,
47 ChineseBasedPrecomputedData, ChineseBasedWithDataLoading, ChineseBasedYearInfo,
48};
49use crate::iso::Iso;
50use crate::provider::chinese_based::ChineseCacheV1Marker;
51use crate::types::{Era, FormattableYear};
52use crate::AsCalendar;
53use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time};
54use core::cmp::Ordering;
55use core::num::NonZeroU8;
56use icu_provider::prelude::*;
57use tinystr::tinystr;
58
59#[derive(Clone, Debug, Default)]
108pub struct Chinese {
109 data: Option<DataPayload<ChineseCacheV1Marker>>,
110}
111
112#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
114pub struct ChineseDateInner(ChineseBasedDateInner<Chinese>);
115
116type Inner = ChineseBasedDateInner<Chinese>;
117
118impl Copy for ChineseDateInner {}
120impl Clone for ChineseDateInner {
121 fn clone(&self) -> Self {
122 *self
123 }
124}
125
126impl PartialEq for Chinese {
129 fn eq(&self, _: &Self) -> bool {
130 true
131 }
132}
133impl Eq for Chinese {}
134#[allow(clippy::non_canonical_partial_ord_impl)] impl PartialOrd for Chinese {
136 fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
137 Some(Ordering::Equal)
138 }
139}
140
141impl Ord for Chinese {
142 fn cmp(&self, _: &Self) -> Ordering {
143 Ordering::Equal
144 }
145}
146
147impl Chinese {
148 #[cfg(feature = "compiled_data")]
154 pub const fn new() -> Self {
155 Self {
156 data: Some(DataPayload::from_static_ref(
157 crate::provider::Baked::SINGLETON_CALENDAR_CHINESECACHE_V1,
158 )),
159 }
160 }
161
162 icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
163 #[cfg(skip)]
164 functions: [
165 new,
166 try_new_with_any_provider,
167 try_new_with_buffer_provider,
168 try_new_unstable,
169 Self,
170 ]);
171
172 #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
173 pub fn try_new_unstable<D: DataProvider<ChineseCacheV1Marker> + ?Sized>(
174 provider: &D,
175 ) -> Result<Self, CalendarError> {
176 Ok(Self {
177 data: Some(provider.load(Default::default())?.take_payload()?),
178 })
179 }
180
181 pub fn new_always_calculating() -> Self {
183 Chinese { data: None }
184 }
185
186 pub(crate) const DEBUG_NAME: &'static str = "Chinese";
187}
188
189impl Calendar for Chinese {
190 type DateInner = ChineseDateInner;
191
192 fn date_from_codes(
194 &self,
195 era: types::Era,
196 year: i32,
197 month_code: types::MonthCode,
198 day: u8,
199 ) -> Result<Self::DateInner, CalendarError> {
200 let year_info = self.get_precomputed_data().load_or_compute_info(year);
201
202 let month = if let Some(ordinal) =
203 chinese_based_ordinal_lunar_month_from_code(month_code, year_info)
204 {
205 ordinal
206 } else {
207 return Err(CalendarError::UnknownMonthCode(
208 month_code.0,
209 self.debug_name(),
210 ));
211 };
212
213 if era.0 != tinystr!(16, "chinese") {
214 return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
215 }
216
217 let arithmetic = Inner::new_from_ordinals(year, month, day, year_info);
218 Ok(ChineseDateInner(ChineseBasedDateInner(arithmetic?)))
219 }
220
221 fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
223 let fixed = Iso::fixed_from_iso(iso.inner);
224 ChineseDateInner(Inner::chinese_based_date_from_fixed(
225 self,
226 fixed,
227 iso.inner.0,
228 ))
229 }
230
231 fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
233 let fixed = Inner::fixed_from_chinese_based_date_inner(date.0);
234 Iso::iso_from_fixed(fixed)
235 }
236
237 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
240 date.0.days_in_year_inner()
241 }
242
243 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
244 date.0.days_in_month_inner()
245 }
246
247 #[doc(hidden)] fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
249 date.0 .0.offset_date(offset, &self.get_precomputed_data());
250 }
251
252 #[doc(hidden)] #[allow(clippy::field_reassign_with_default)]
254 fn until(
259 &self,
260 date1: &Self::DateInner,
261 date2: &Self::DateInner,
262 _calendar2: &Self,
263 _largest_unit: DateDurationUnit,
264 _smallest_unit: DateDurationUnit,
265 ) -> DateDuration<Self> {
266 date1.0 .0.until(date2.0 .0, _largest_unit, _smallest_unit)
267 }
268
269 fn debug_name(&self) -> &'static str {
271 Self::DEBUG_NAME
272 }
273
274 fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
276 Self::format_chinese_year(date.0 .0.year, Some(date.0 .0.year_info))
277 }
278
279 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
280 Self::is_leap_year(date.0 .0.year, date.0 .0.year_info)
281 }
282
283 fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
288 date.0.month()
289 }
290
291 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
293 types::DayOfMonth(date.0 .0.day as u32)
294 }
295
296 fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
298 let prev_year = date.0 .0.year.saturating_sub(1);
299 let next_year = date.0 .0.year.saturating_add(1);
300 types::DayOfYearInfo {
301 day_of_year: date.0.day_of_year(),
302 days_in_year: date.0.days_in_year_inner(),
303 prev_year: Self::format_chinese_year(prev_year, None),
304 days_in_prev_year: date.0.days_in_prev_year(),
305 next_year: Self::format_chinese_year(next_year, None),
306 }
307 }
308
309 fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
311 Some(AnyCalendarKind::Chinese)
312 }
313
314 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
315 date.0.months_in_year_inner()
316 }
317}
318
319impl<A: AsCalendar<Calendar = Chinese>> Date<A> {
320 pub fn try_new_chinese_date_with_calendar(
344 year: i32,
345 month: u8,
346 day: u8,
347 calendar: A,
348 ) -> Result<Date<A>, CalendarError> {
349 let year_info = calendar
350 .as_calendar()
351 .get_precomputed_data()
352 .load_or_compute_info(year);
353 let arithmetic = Inner::new_from_ordinals(year, month, day, year_info);
354 Ok(Date::from_raw(
355 ChineseDateInner(ChineseBasedDateInner(arithmetic?)),
356 calendar,
357 ))
358 }
359}
360
361impl<A: AsCalendar<Calendar = Chinese>> DateTime<A> {
362 pub fn try_new_chinese_datetime_with_calendar(
388 year: i32,
389 month: u8,
390 day: u8,
391 hour: u8,
392 minute: u8,
393 second: u8,
394 calendar: A,
395 ) -> Result<DateTime<A>, CalendarError> {
396 Ok(DateTime {
397 date: Date::try_new_chinese_date_with_calendar(year, month, day, calendar)?,
398 time: Time::try_new(hour, minute, second, 0)?,
399 })
400 }
401}
402
403type ChineseCB = calendrical_calculations::chinese_based::Chinese;
404impl ChineseBasedWithDataLoading for Chinese {
405 type CB = ChineseCB;
406 fn get_precomputed_data(&self) -> ChineseBasedPrecomputedData<Self::CB> {
407 ChineseBasedPrecomputedData::new(self.data.as_ref().map(|d| d.get()))
408 }
409}
410
411impl Chinese {
412 fn format_chinese_year(
420 year: i32,
421 year_info_option: Option<ChineseBasedYearInfo>,
422 ) -> FormattableYear {
423 let era = Era(tinystr!(16, "chinese"));
424 let number = year;
425 let cyclic = (number - 1).rem_euclid(60) as u8;
426 let cyclic = NonZeroU8::new(cyclic + 1); let rata_die_in_year = if let Some(info) = year_info_option {
428 info.new_year::<ChineseCB>(year)
429 } else {
430 Inner::fixed_mid_year_from_year(number)
431 };
432 let iso_formattable_year = Iso::iso_from_fixed(rata_die_in_year).year();
433 let related_iso = Some(iso_formattable_year.number);
434 types::FormattableYear {
435 era,
436 number,
437 cyclic,
438 related_iso,
439 }
440 }
441}
442
443#[cfg(test)]
444mod test {
445
446 use super::*;
447 use crate::types::MonthCode;
448 use calendrical_calculations::{iso::fixed_from_iso, rata_die::RataDie};
449 fn do_twice(
451 chinese_calculating: &Chinese,
452 chinese_cached: &Chinese,
453 test: impl Fn(crate::Ref<Chinese>, &'static str),
454 ) {
455 test(crate::Ref(chinese_calculating), "calculating");
456 test(crate::Ref(chinese_cached), "cached");
457 }
458
459 #[test]
460 fn test_chinese_from_fixed() {
461 #[derive(Debug)]
462 struct TestCase {
463 fixed: i64,
464 expected_year: i32,
465 expected_month: u8,
466 expected_day: u8,
467 }
468
469 let cases = [
470 TestCase {
471 fixed: -964192,
472 expected_year: -2,
473 expected_month: 1,
474 expected_day: 1,
475 },
476 TestCase {
477 fixed: -963838,
478 expected_year: -1,
479 expected_month: 1,
480 expected_day: 1,
481 },
482 TestCase {
483 fixed: -963129,
484 expected_year: 0,
485 expected_month: 13,
486 expected_day: 1,
487 },
488 TestCase {
489 fixed: -963100,
490 expected_year: 0,
491 expected_month: 13,
492 expected_day: 30,
493 },
494 TestCase {
495 fixed: -963099,
496 expected_year: 1,
497 expected_month: 1,
498 expected_day: 1,
499 },
500 TestCase {
501 fixed: 738700,
502 expected_year: 4660,
503 expected_month: 6,
504 expected_day: 12,
505 },
506 TestCase {
507 fixed: fixed_from_iso(2319, 2, 20).to_i64_date(),
508 expected_year: 2319 + 2636,
509 expected_month: 13,
510 expected_day: 30,
511 },
512 TestCase {
513 fixed: fixed_from_iso(2319, 2, 21).to_i64_date(),
514 expected_year: 2319 + 2636 + 1,
515 expected_month: 1,
516 expected_day: 1,
517 },
518 TestCase {
519 fixed: 738718,
520 expected_year: 4660,
521 expected_month: 6,
522 expected_day: 30,
523 },
524 TestCase {
525 fixed: 738747,
526 expected_year: 4660,
527 expected_month: 7,
528 expected_day: 29,
529 },
530 TestCase {
531 fixed: 738748,
532 expected_year: 4660,
533 expected_month: 8,
534 expected_day: 1,
535 },
536 TestCase {
537 fixed: 738865,
538 expected_year: 4660,
539 expected_month: 11,
540 expected_day: 29,
541 },
542 TestCase {
543 fixed: 738895,
544 expected_year: 4660,
545 expected_month: 12,
546 expected_day: 29,
547 },
548 TestCase {
549 fixed: 738925,
550 expected_year: 4660,
551 expected_month: 13,
552 expected_day: 30,
553 },
554 ];
555
556 let chinese_calculating = Chinese::new_always_calculating();
557 let chinese_cached = Chinese::new();
558 for case in cases {
559 let rata_die = RataDie::new(case.fixed);
560 let iso = Iso::iso_from_fixed(rata_die);
561
562 do_twice(
563 &chinese_calculating,
564 &chinese_cached,
565 |chinese, calendar_type| {
566 let chinese =
567 Inner::chinese_based_date_from_fixed(chinese.0, rata_die, iso.inner.0);
568 assert_eq!(
569 case.expected_year, chinese.0.year,
570 "[{calendar_type}] Chinese from fixed failed, case: {case:?}"
571 );
572 assert_eq!(
573 case.expected_month, chinese.0.month,
574 "[{calendar_type}] Chinese from fixed failed, case: {case:?}"
575 );
576 assert_eq!(
577 case.expected_day, chinese.0.day,
578 "[{calendar_type}] Chinese from fixed failed, case: {case:?}"
579 );
580 },
581 );
582 }
583 }
584
585 #[test]
586 fn test_fixed_from_chinese() {
587 #[derive(Debug)]
588 struct TestCase {
589 year: i32,
590 month: u8,
591 day: u8,
592 expected: i64,
593 }
594
595 let cases = [
596 TestCase {
597 year: 4660,
598 month: 6,
599 day: 6,
600 expected: 738694,
602 },
603 TestCase {
604 year: 1,
605 month: 1,
606 day: 1,
607 expected: -963099,
608 },
609 ];
610
611 let chinese_calculating = Chinese::new_always_calculating();
612 let chinese_cached = Chinese::new();
613 for case in cases {
614 do_twice(
615 &chinese_calculating,
616 &chinese_cached,
617 |chinese, calendar_type| {
618 let date = Date::try_new_chinese_date_with_calendar(
619 case.year, case.month, case.day, chinese,
620 )
621 .unwrap();
622 let fixed =
623 Inner::fixed_from_chinese_based_date_inner(date.inner.0).to_i64_date();
624 let expected = case.expected;
625 assert_eq!(fixed, expected, "[{calendar_type}] Fixed from Chinese failed, with expected: {fixed} and calculated: {expected}, for test case: {case:?}");
626 },
627 );
628 }
629 }
630
631 #[test]
632 fn test_fixed_chinese_roundtrip() {
633 let mut fixed = -1963020;
634 let max_fixed = 1963020;
635 let mut iters = 0;
636 let max_iters = 560;
637 let chinese_calculating = Chinese::new_always_calculating();
638 let chinese_cached = Chinese::new();
639 while fixed < max_fixed && iters < max_iters {
640 let rata_die = RataDie::new(fixed);
641 let iso = Iso::iso_from_fixed(rata_die);
642
643 do_twice(
644 &chinese_calculating,
645 &chinese_cached,
646 |chinese, calendar_type| {
647 let chinese =
648 Inner::chinese_based_date_from_fixed(&chinese, rata_die, iso.inner.0);
649 let result = Inner::fixed_from_chinese_based_date_inner(chinese);
650 let result_debug = result.to_i64_date();
651 assert_eq!(result, rata_die, "[{calendar_type}] Failed roundtrip fixed -> Chinese -> fixed for fixed: {fixed}, with calculated: {result_debug} from Chinese date:\n{chinese:?}");
652 },
653 );
654 fixed += 7043;
655 iters += 1;
656 }
657 }
658
659 #[test]
660 fn test_chinese_epoch() {
661 let iso = Date::try_new_iso_date(-2636, 2, 15).unwrap();
662
663 do_twice(
664 &Chinese::new_always_calculating(),
665 &Chinese::new(),
666 |chinese, _calendar_type| {
667 let chinese = iso.to_calendar(chinese);
668
669 assert_eq!(chinese.year().number, 1);
670 assert_eq!(chinese.month().ordinal, 1);
671 assert_eq!(chinese.month().code.0, "M01");
672 assert_eq!(chinese.day_of_month().0, 1);
673 assert_eq!(chinese.year().cyclic.unwrap().get(), 1);
674 assert_eq!(chinese.year().related_iso, Some(-2636));
675 },
676 )
677 }
678
679 #[test]
680 fn test_iso_to_chinese_negative_years() {
681 #[derive(Debug)]
682 struct TestCase {
683 iso_year: i32,
684 iso_month: u8,
685 iso_day: u8,
686 expected_year: i32,
687 expected_month: u32,
688 expected_day: u32,
689 }
690
691 let cases = [
692 TestCase {
693 iso_year: -2636,
694 iso_month: 2,
695 iso_day: 14,
696 expected_year: 0,
697 expected_month: 13,
698 expected_day: 30,
699 },
700 TestCase {
701 iso_year: -2636,
702 iso_month: 1,
703 iso_day: 15,
704 expected_year: 0,
705 expected_month: 12,
706 expected_day: 30,
707 },
708 ];
709
710 let chinese_calculating = Chinese::new_always_calculating();
711 let chinese_cached = Chinese::new();
712
713 for case in cases {
714 let iso = Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day).unwrap();
715 do_twice(
716 &chinese_calculating,
717 &chinese_cached,
718 |chinese, calendar_type| {
719 let chinese = iso.to_calendar(chinese);
720 assert_eq!(
721 case.expected_year,
722 chinese.year().number,
723 "[{calendar_type}] ISO to Chinese failed for case: {case:?}"
724 );
725 assert_eq!(
726 case.expected_month,
727 chinese.month().ordinal,
728 "[{calendar_type}] ISO to Chinese failed for case: {case:?}"
729 );
730 assert_eq!(
731 case.expected_day,
732 chinese.day_of_month().0,
733 "[{calendar_type}] ISO to Chinese failed for case: {case:?}"
734 );
735 },
736 );
737 }
738 }
739
740 #[test]
741 fn test_chinese_leap_months() {
742 let expected = [
743 (1933, 6),
744 (1938, 8),
745 (1984, 11),
746 (2009, 6),
747 (2017, 7),
748 (2028, 6),
749 ];
750 let chinese_calculating = Chinese::new_always_calculating();
751 let chinese_cached = Chinese::new();
752
753 for case in expected {
754 let year = case.0;
755 let expected_month = case.1;
756 let iso = Date::try_new_iso_date(year, 6, 1).unwrap();
757 do_twice(
758 &chinese_calculating,
759 &chinese_cached,
760 |chinese, calendar_type| {
761 let chinese_date = iso.to_calendar(chinese);
762 assert!(
763 chinese_date.is_in_leap_year(),
764 "[{calendar_type}] {year} should be a leap year"
765 );
766 let new_year = chinese_date.inner.0.new_year();
767 assert_eq!(
768 expected_month,
769 calendrical_calculations::chinese_based::get_leap_month_from_new_year::<
770 calendrical_calculations::chinese_based::Chinese,
771 >(new_year),
772 "[{calendar_type}] {year} have leap month {expected_month}"
773 );
774 },
775 );
776 }
777 }
778
779 #[test]
780 fn test_month_days() {
781 let year = 4660;
782 let year_info =
783 ChineseBasedPrecomputedData::<<Chinese as ChineseBasedWithDataLoading>::CB>::default()
784 .load_or_compute_info(year);
785 let cases = [
786 (1, 29),
787 (2, 30),
788 (3, 29),
789 (4, 29),
790 (5, 30),
791 (6, 30),
792 (7, 29),
793 (8, 30),
794 (9, 30),
795 (10, 29),
796 (11, 30),
797 (12, 29),
798 (13, 30),
799 ];
800 for case in cases {
801 let days_in_month = Chinese::month_days(year, case.0, year_info);
802 assert_eq!(
803 case.1, days_in_month,
804 "month_days test failed for case: {case:?}"
805 );
806 }
807 }
808
809 #[test]
810 fn test_ordinal_to_month_code() {
811 #[derive(Debug)]
812 struct TestCase {
813 year: i32,
814 month: u8,
815 day: u8,
816 expected_code: &'static str,
817 }
818
819 let cases = [
820 TestCase {
821 year: 2023,
822 month: 1,
823 day: 9,
824 expected_code: "M12",
825 },
826 TestCase {
827 year: 2023,
828 month: 2,
829 day: 9,
830 expected_code: "M01",
831 },
832 TestCase {
833 year: 2023,
834 month: 3,
835 day: 9,
836 expected_code: "M02",
837 },
838 TestCase {
839 year: 2023,
840 month: 4,
841 day: 9,
842 expected_code: "M02L",
843 },
844 TestCase {
845 year: 2023,
846 month: 5,
847 day: 9,
848 expected_code: "M03",
849 },
850 TestCase {
851 year: 2023,
852 month: 6,
853 day: 9,
854 expected_code: "M04",
855 },
856 TestCase {
857 year: 2023,
858 month: 7,
859 day: 9,
860 expected_code: "M05",
861 },
862 TestCase {
863 year: 2023,
864 month: 8,
865 day: 9,
866 expected_code: "M06",
867 },
868 TestCase {
869 year: 2023,
870 month: 9,
871 day: 9,
872 expected_code: "M07",
873 },
874 TestCase {
875 year: 2023,
876 month: 10,
877 day: 9,
878 expected_code: "M08",
879 },
880 TestCase {
881 year: 2023,
882 month: 11,
883 day: 9,
884 expected_code: "M09",
885 },
886 TestCase {
887 year: 2023,
888 month: 12,
889 day: 9,
890 expected_code: "M10",
891 },
892 TestCase {
893 year: 2024,
894 month: 1,
895 day: 9,
896 expected_code: "M11",
897 },
898 TestCase {
899 year: 2024,
900 month: 2,
901 day: 9,
902 expected_code: "M12",
903 },
904 TestCase {
905 year: 2024,
906 month: 2,
907 day: 10,
908 expected_code: "M01",
909 },
910 ];
911
912 let chinese_calculating = Chinese::new_always_calculating();
913 let chinese_cached = Chinese::new();
914
915 for case in cases {
916 let iso = Date::try_new_iso_date(case.year, case.month, case.day).unwrap();
917 do_twice(
918 &chinese_calculating,
919 &chinese_cached,
920 |chinese, calendar_type| {
921 let chinese = iso.to_calendar(chinese);
922 let result_code = chinese.month().code.0;
923 let expected_code = case.expected_code.to_string();
924 assert_eq!(
925 expected_code, result_code,
926 "[{calendar_type}] Month codes did not match for test case: {case:?}"
927 );
928 },
929 );
930 }
931 }
932
933 #[test]
934 fn test_month_code_to_ordinal() {
935 let year = 4660;
936 let year_info =
938 ChineseBasedPrecomputedData::<<Chinese as ChineseBasedWithDataLoading>::CB>::default()
939 .load_or_compute_info(year);
940 let codes = [
941 (1, tinystr!(4, "M01")),
942 (2, tinystr!(4, "M02")),
943 (3, tinystr!(4, "M02L")),
944 (4, tinystr!(4, "M03")),
945 (5, tinystr!(4, "M04")),
946 (6, tinystr!(4, "M05")),
947 (7, tinystr!(4, "M06")),
948 (8, tinystr!(4, "M07")),
949 (9, tinystr!(4, "M08")),
950 (10, tinystr!(4, "M09")),
951 (11, tinystr!(4, "M10")),
952 (12, tinystr!(4, "M11")),
953 (13, tinystr!(4, "M12")),
954 ];
955 for ordinal_code_pair in codes {
956 let code = MonthCode(ordinal_code_pair.1);
957 let ordinal = chinese_based_ordinal_lunar_month_from_code(code, year_info);
958 assert_eq!(
959 ordinal,
960 Some(ordinal_code_pair.0),
961 "Code to ordinal failed for year: {year}, code: {code}"
962 );
963 }
964 }
965
966 #[test]
967 fn check_invalid_month_code_to_ordinal() {
968 let non_leap_year = 4659;
969 let leap_year = 4660;
970 let invalid_codes = [
971 (non_leap_year, tinystr!(4, "M2")),
972 (leap_year, tinystr!(4, "M0")),
973 (non_leap_year, tinystr!(4, "J01")),
974 (leap_year, tinystr!(4, "3M")),
975 (non_leap_year, tinystr!(4, "M04L")),
976 (leap_year, tinystr!(4, "M04L")),
977 (non_leap_year, tinystr!(4, "M13")),
978 (leap_year, tinystr!(4, "M13")),
979 ];
980 for year_code_pair in invalid_codes {
981 let year = year_code_pair.0;
982 let year_info = ChineseBasedPrecomputedData::<
984 <Chinese as ChineseBasedWithDataLoading>::CB,
985 >::default()
986 .load_or_compute_info(year);
987 let code = MonthCode(year_code_pair.1);
988 let ordinal = chinese_based_ordinal_lunar_month_from_code(code, year_info);
989 assert_eq!(
990 ordinal, None,
991 "Invalid month code failed for year: {year}, code: {code}"
992 );
993 }
994 }
995
996 #[test]
997 fn test_iso_chinese_roundtrip() {
998 let chinese_calculating = Chinese::new_always_calculating();
999 let chinese_cached = Chinese::new();
1000
1001 for i in -1000..=1000 {
1002 let year = i;
1003 let month = i as u8 % 12 + 1;
1004 let day = i as u8 % 28 + 1;
1005 let iso = Date::try_new_iso_date(year, month, day).unwrap();
1006 do_twice(
1007 &chinese_calculating,
1008 &chinese_cached,
1009 |chinese, calendar_type| {
1010 let chinese = iso.to_calendar(chinese);
1011 let result = chinese.to_calendar(Iso);
1012 assert_eq!(iso, result, "[{calendar_type}] ISO to Chinese roundtrip failed!\nIso: {iso:?}\nChinese: {chinese:?}\nResult: {result:?}");
1013 },
1014 );
1015 }
1016 }
1017
1018 #[test]
1019 fn test_consistent_with_icu() {
1020 #[derive(Debug)]
1021 struct TestCase {
1022 iso_year: i32,
1023 iso_month: u8,
1024 iso_day: u8,
1025 expected_rel_iso: i32,
1026 expected_cyclic: u8,
1027 expected_month: u32,
1028 expected_day: u32,
1029 }
1030
1031 let cases = [
1032 TestCase {
1033 iso_year: -2332,
1034 iso_month: 3,
1035 iso_day: 1,
1036 expected_rel_iso: -2332,
1037 expected_cyclic: 5,
1038 expected_month: 1,
1039 expected_day: 16,
1040 },
1041 TestCase {
1042 iso_year: -2332,
1043 iso_month: 2,
1044 iso_day: 15,
1045 expected_rel_iso: -2332,
1046 expected_cyclic: 5,
1047 expected_month: 1,
1048 expected_day: 1,
1049 },
1050 TestCase {
1051 iso_year: -2332,
1053 iso_month: 2,
1054 iso_day: 14,
1055 expected_rel_iso: -2333,
1056 expected_cyclic: 4,
1057 expected_month: 13,
1058 expected_day: 30,
1059 },
1060 TestCase {
1061 iso_year: -2332,
1063 iso_month: 1,
1064 iso_day: 17,
1065 expected_rel_iso: -2333,
1066 expected_cyclic: 4,
1067 expected_month: 13,
1068 expected_day: 2,
1069 },
1070 TestCase {
1071 iso_year: -2332,
1073 iso_month: 1,
1074 iso_day: 16,
1075 expected_rel_iso: -2333,
1076 expected_cyclic: 4,
1077 expected_month: 13,
1078 expected_day: 1,
1079 },
1080 TestCase {
1081 iso_year: -2332,
1082 iso_month: 1,
1083 iso_day: 15,
1084 expected_rel_iso: -2333,
1085 expected_cyclic: 4,
1086 expected_month: 12,
1087 expected_day: 29,
1088 },
1089 TestCase {
1090 iso_year: -2332,
1091 iso_month: 1,
1092 iso_day: 1,
1093 expected_rel_iso: -2333,
1094 expected_cyclic: 4,
1095 expected_month: 12,
1096 expected_day: 15,
1097 },
1098 TestCase {
1099 iso_year: -2333,
1100 iso_month: 1,
1101 iso_day: 16,
1102 expected_rel_iso: -2334,
1103 expected_cyclic: 3,
1104 expected_month: 12,
1105 expected_day: 19,
1106 },
1107 ];
1108
1109 let chinese_calculating = Chinese::new_always_calculating();
1110 let chinese_cached = Chinese::new();
1111
1112 for case in cases {
1113 let iso = Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day).unwrap();
1114
1115 do_twice(
1116 &chinese_calculating,
1117 &chinese_cached,
1118 |chinese, calendar_type| {
1119 let chinese = iso.to_calendar(chinese);
1120 let chinese_rel_iso = chinese.year().related_iso;
1121 let chinese_cyclic = chinese.year().cyclic;
1122 let chinese_month = chinese.month().ordinal;
1123 let chinese_day = chinese.day_of_month().0;
1124
1125 assert_eq!(
1126 chinese_rel_iso,
1127 Some(case.expected_rel_iso),
1128 "[{calendar_type}] Related ISO failed for test case: {case:?}"
1129 );
1130 assert_eq!(
1131 chinese_cyclic.unwrap().get(),
1132 case.expected_cyclic,
1133 "[{calendar_type}] Cyclic year failed for test case: {case:?}"
1134 );
1135 assert_eq!(
1136 chinese_month, case.expected_month,
1137 "[{calendar_type}] Month failed for test case: {case:?}"
1138 );
1139 assert_eq!(
1140 chinese_day, case.expected_day,
1141 "[{calendar_type}] Day failed for test case: {case:?}"
1142 );
1143 },
1144 );
1145 }
1146 }
1147}