1use crate::cal::iso::{Iso, IsoDateInner};
25use crate::error::{year_check, DateError};
26use crate::provider::{CalendarJapaneseExtendedV1, CalendarJapaneseModernV1, EraStartDate};
27use crate::{types, AsCalendar, Calendar, Date, DateDuration, DateDurationUnit, Ref};
28use calendrical_calculations::rata_die::RataDie;
29use icu_provider::prelude::*;
30use tinystr::{tinystr, TinyStr16};
31
32#[derive(Clone, Debug, Default)]
59pub struct Japanese {
60 eras: DataPayload<CalendarJapaneseModernV1>,
61}
62
63#[derive(Clone, Debug, Default)]
89pub struct JapaneseExtended(Japanese);
90
91#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
92pub struct JapaneseDateInner {
94 inner: IsoDateInner,
95 adjusted_year: i32,
96 era: TinyStr16,
97}
98
99impl Japanese {
100 #[cfg(feature = "compiled_data")]
106 pub const fn new() -> Self {
107 Self {
108 eras: DataPayload::from_static_ref(
109 crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_MODERN_V1,
110 ),
111 }
112 }
113
114 icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
115 functions: [
116 new: skip,
117 try_new_with_buffer_provider,
118 try_new_unstable,
119 Self,
120 ]);
121
122 #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
123 pub fn try_new_unstable<D: DataProvider<CalendarJapaneseModernV1> + ?Sized>(
124 provider: &D,
125 ) -> Result<Self, DataError> {
126 Ok(Self {
127 eras: provider.load(Default::default())?.payload,
128 })
129 }
130
131 pub(crate) const DEBUG_NAME: &'static str = "Japanese";
132}
133
134impl JapaneseExtended {
135 #[cfg(feature = "compiled_data")]
141 pub const fn new() -> Self {
142 Self(Japanese {
143 eras: DataPayload::from_static_ref(
144 crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_EXTENDED_V1,
145 ),
146 })
147 }
148
149 icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
150 functions: [
151 new: skip,
152 try_new_with_buffer_provider,
153 try_new_unstable,
154 Self,
155 ]);
156
157 #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
158 pub fn try_new_unstable<D: DataProvider<CalendarJapaneseExtendedV1> + ?Sized>(
159 provider: &D,
160 ) -> Result<Self, DataError> {
161 Ok(Self(Japanese {
162 eras: provider.load(Default::default())?.payload.cast(),
163 }))
164 }
165
166 pub(crate) const DEBUG_NAME: &'static str = "Japanese (historical era data)";
167}
168
169impl crate::cal::scaffold::UnstableSealed for Japanese {}
170impl Calendar for Japanese {
171 type DateInner = JapaneseDateInner;
172 type Year = types::EraYear;
173
174 fn from_codes(
175 &self,
176 era: Option<&str>,
177 year: i32,
178 month_code: types::MonthCode,
179 day: u8,
180 ) -> Result<Self::DateInner, DateError> {
181 let Some((month, false)) = month_code.parsed() else {
182 return Err(DateError::UnknownMonthCode(month_code));
183 };
184
185 if month > 12 {
186 return Err(DateError::UnknownMonthCode(month_code));
187 }
188
189 self.new_japanese_date_inner(era.unwrap_or("ce"), year, month, day)
190 }
191
192 fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
193 self.from_iso(Iso.from_rata_die(rd))
194 }
195
196 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
197 Iso.to_rata_die(&self.to_iso(date))
198 }
199
200 fn from_iso(&self, iso: IsoDateInner) -> JapaneseDateInner {
201 let (adjusted_year, era) = self.adjusted_year_for(iso);
202 JapaneseDateInner {
203 inner: iso,
204 adjusted_year,
205 era,
206 }
207 }
208
209 fn to_iso(&self, date: &Self::DateInner) -> IsoDateInner {
210 date.inner
211 }
212
213 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
214 Iso.months_in_year(&date.inner)
215 }
216
217 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
218 Iso.days_in_year(&date.inner)
219 }
220
221 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
222 Iso.days_in_month(&date.inner)
223 }
224
225 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
226 Iso.offset_date(&mut date.inner, offset.cast_unit());
227 let (adjusted_year, era) = self.adjusted_year_for(date.inner);
228 date.adjusted_year = adjusted_year;
229 date.era = era
230 }
231
232 fn until(
233 &self,
234 date1: &Self::DateInner,
235 date2: &Self::DateInner,
236 _calendar2: &Self,
237 largest_unit: DateDurationUnit,
238 smallest_unit: DateDurationUnit,
239 ) -> DateDuration<Self> {
240 Iso.until(
241 &date1.inner,
242 &date2.inner,
243 &Iso,
244 largest_unit,
245 smallest_unit,
246 )
247 .cast_unit()
248 }
249
250 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
251 types::EraYear {
252 era: date.era,
253 era_index: None,
254 year: date.adjusted_year,
255 ambiguity: types::YearAmbiguity::CenturyRequired,
256 }
257 }
258
259 fn extended_year(&self, date: &Self::DateInner) -> i32 {
260 Iso.extended_year(&date.inner)
261 }
262
263 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
264 Iso.is_in_leap_year(&date.inner)
265 }
266
267 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
269 Iso.month(&date.inner)
270 }
271
272 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
274 Iso.day_of_month(&date.inner)
275 }
276
277 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
278 Iso.day_of_year(&date.inner)
279 }
280
281 fn debug_name(&self) -> &'static str {
282 Self::DEBUG_NAME
283 }
284
285 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
286 Some(crate::preferences::CalendarAlgorithm::Japanese)
287 }
288}
289
290impl crate::cal::scaffold::UnstableSealed for JapaneseExtended {}
291impl Calendar for JapaneseExtended {
292 type DateInner = JapaneseDateInner;
293 type Year = types::EraYear;
294
295 fn from_codes(
296 &self,
297 era: Option<&str>,
298 year: i32,
299 month_code: types::MonthCode,
300 day: u8,
301 ) -> Result<Self::DateInner, DateError> {
302 self.0.from_codes(era, year, month_code, day)
303 }
304
305 fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
306 Japanese::from_rata_die(&self.0, rd)
307 }
308
309 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
310 Japanese::to_rata_die(&self.0, date)
311 }
312
313 fn from_iso(&self, iso: IsoDateInner) -> JapaneseDateInner {
314 Japanese::from_iso(&self.0, iso)
315 }
316
317 fn to_iso(&self, date: &Self::DateInner) -> IsoDateInner {
318 Japanese::to_iso(&self.0, date)
319 }
320
321 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
322 Japanese::months_in_year(&self.0, date)
323 }
324
325 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
326 Japanese::days_in_year(&self.0, date)
327 }
328
329 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
330 Japanese::days_in_month(&self.0, date)
331 }
332
333 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
334 Japanese::offset_date(&self.0, date, offset.cast_unit())
335 }
336
337 fn until(
338 &self,
339 date1: &Self::DateInner,
340 date2: &Self::DateInner,
341 calendar2: &Self,
342 largest_unit: DateDurationUnit,
343 smallest_unit: DateDurationUnit,
344 ) -> DateDuration<Self> {
345 Japanese::until(
346 &self.0,
347 date1,
348 date2,
349 &calendar2.0,
350 largest_unit,
351 smallest_unit,
352 )
353 .cast_unit()
354 }
355
356 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
357 Japanese::year_info(&self.0, date)
358 }
359
360 fn extended_year(&self, date: &Self::DateInner) -> i32 {
361 Japanese::extended_year(&self.0, date)
362 }
363
364 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
365 Japanese::is_in_leap_year(&self.0, date)
366 }
367
368 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
370 Japanese::month(&self.0, date)
371 }
372
373 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
375 Japanese::day_of_month(&self.0, date)
376 }
377
378 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
380 Japanese::day_of_year(&self.0, date)
381 }
382
383 fn debug_name(&self) -> &'static str {
384 Self::DEBUG_NAME
385 }
386
387 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
388 Some(crate::preferences::CalendarAlgorithm::Japanese)
389 }
390}
391
392impl Date<Japanese> {
393 pub fn try_new_japanese_with_calendar<A: AsCalendar<Calendar = Japanese>>(
439 era: &str,
440 year: i32,
441 month: u8,
442 day: u8,
443 japanese_calendar: A,
444 ) -> Result<Date<A>, DateError> {
445 let inner = japanese_calendar
446 .as_calendar()
447 .new_japanese_date_inner(era, year, month, day)?;
448 Ok(Date::from_raw(inner, japanese_calendar))
449 }
450}
451
452impl Date<JapaneseExtended> {
453 pub fn try_new_japanese_extended_with_calendar<A: AsCalendar<Calendar = JapaneseExtended>>(
487 era: &str,
488 year: i32,
489 month: u8,
490 day: u8,
491 japanext_calendar: A,
492 ) -> Result<Date<A>, DateError> {
493 let inner = japanext_calendar
494 .as_calendar()
495 .0
496 .new_japanese_date_inner(era, year, month, day)?;
497 Ok(Date::from_raw(inner, japanext_calendar))
498 }
499}
500
501const MEIJI_START: EraStartDate = EraStartDate {
502 year: 1868,
503 month: 10,
504 day: 23,
505};
506const TAISHO_START: EraStartDate = EraStartDate {
507 year: 1912,
508 month: 7,
509 day: 30,
510};
511const SHOWA_START: EraStartDate = EraStartDate {
512 year: 1926,
513 month: 12,
514 day: 25,
515};
516const HEISEI_START: EraStartDate = EraStartDate {
517 year: 1989,
518 month: 1,
519 day: 8,
520};
521const REIWA_START: EraStartDate = EraStartDate {
522 year: 2019,
523 month: 5,
524 day: 1,
525};
526
527impl Japanese {
528 fn adjusted_year_for(&self, date: IsoDateInner) -> (i32, TinyStr16) {
532 let date: EraStartDate = EraStartDate {
533 year: date.0.year,
534 month: date.0.month,
535 day: date.0.day,
536 };
537 let (start, era) = self.japanese_era_for(date);
538 if date < start {
544 if date.year <= 0 {
545 (1 - date.year, tinystr!(16, "bce"))
546 } else {
547 (date.year, tinystr!(16, "ce"))
548 }
549 } else {
550 (date.year - start.year + 1, era)
551 }
552 }
553
554 fn japanese_era_for(&self, date: EraStartDate) -> (EraStartDate, TinyStr16) {
556 let era_data = self.eras.get();
557 if date >= MEIJI_START
561 && era_data.dates_to_eras.last().map(|x| x.1) == Some(tinystr!(16, "reiwa"))
562 {
563 return if date >= REIWA_START {
565 (REIWA_START, tinystr!(16, "reiwa"))
566 } else if date >= HEISEI_START {
567 (HEISEI_START, tinystr!(16, "heisei"))
568 } else if date >= SHOWA_START {
569 (SHOWA_START, tinystr!(16, "showa"))
570 } else if date >= TAISHO_START {
571 (TAISHO_START, tinystr!(16, "taisho"))
572 } else {
573 (MEIJI_START, tinystr!(16, "meiji"))
574 };
575 }
576 let data = &era_data.dates_to_eras;
577 match data.binary_search_by(|(d, _)| d.cmp(&date)) {
578 Ok(index) => data.get(index),
579 Err(index) if index == 0 => data.get(index),
580 Err(index) => data.get(index - 1).or_else(|| data.iter().next_back()),
581 }
582 .unwrap_or((REIWA_START, tinystr!(16, "reiwa")))
583 }
584
585 fn japanese_era_range_for(
590 &self,
591 era: TinyStr16,
592 ) -> Result<(EraStartDate, Option<EraStartDate>), DateError> {
593 if era == tinystr!(16, "reiwa") {
595 if let Some(last) = self.eras.get().dates_to_eras.last() {
597 if last.1 == era {
598 return Ok((REIWA_START, None));
599 }
600 }
601 } else if era == tinystr!(16, "heisei") {
602 return Ok((HEISEI_START, Some(REIWA_START)));
603 } else if era == tinystr!(16, "showa") {
604 return Ok((SHOWA_START, Some(HEISEI_START)));
605 } else if era == tinystr!(16, "taisho") {
606 return Ok((TAISHO_START, Some(SHOWA_START)));
607 } else if era == tinystr!(16, "meiji") {
608 return Ok((MEIJI_START, Some(TAISHO_START)));
609 }
610
611 let era_data = self.eras.get();
612 let data = &era_data.dates_to_eras;
613 if let Some(year) = era.split('-').nth(1) {
615 if let Ok(ref int) = year.parse::<i32>() {
616 if let Ok(index) = data.binary_search_by(|(d, _)| d.year.cmp(int)) {
617 #[allow(clippy::expect_used)] let (era_start, code) = data
619 .get(index)
620 .expect("Indexing from successful binary search must succeed");
621 if code == era {
625 return Ok((era_start, data.get(index + 1).map(|e| e.0)));
626 }
627 }
628 }
629 }
630
631 if let Some((index, (start, _))) = data.iter().enumerate().rev().find(|d| d.1 .1 == era) {
633 return Ok((start, data.get(index + 1).map(|e| e.0)));
634 }
635
636 Err(DateError::UnknownEra)
637 }
638
639 fn new_japanese_date_inner(
640 &self,
641 era: &str,
642 year: i32,
643 month: u8,
644 day: u8,
645 ) -> Result<JapaneseDateInner, DateError> {
646 let cal = Ref(self);
647 let era = match era {
648 "ce" | "ad" => {
649 return Ok(Date::try_new_gregorian(year_check(year, 1..)?, month, day)?
650 .to_calendar(cal)
651 .inner);
652 }
653 "bce" | "bc" => {
654 return Ok(
655 Date::try_new_gregorian(1 - year_check(year, 1..)?, month, day)?
656 .to_calendar(cal)
657 .inner,
658 );
659 }
660 e => e.parse().map_err(|_| DateError::UnknownEra)?,
661 };
662
663 let (era_start, next_era_start) = self.japanese_era_range_for(era)?;
664
665 let next_era_start = next_era_start.unwrap_or(EraStartDate {
666 year: i32::MAX,
667 month: 12,
668 day: 31,
669 });
670
671 let date_in_iso = EraStartDate {
672 year: era_start.year + year - 1,
673 month,
674 day,
675 };
676
677 if date_in_iso < era_start {
678 return Err(if date_in_iso.year < era_start.year {
679 DateError::Range {
680 field: "year",
681 value: year,
682 min: 1,
683 max: 1 + next_era_start.year - era_start.year,
684 }
685 } else if date_in_iso.month < era_start.month {
686 DateError::Range {
687 field: "month",
688 value: month as i32,
689 min: era_start.month as i32,
690 max: 12,
691 }
692 } else
693 {
695 DateError::Range {
696 field: "day",
697 value: day as i32,
698 min: era_start.day as i32,
699 max: 31,
700 }
701 });
702 } else if date_in_iso >= next_era_start {
703 return Err(if date_in_iso.year > era_start.year {
704 DateError::Range {
705 field: "year",
706 value: year,
707 min: 1,
708 max: 1 + next_era_start.year - era_start.year,
709 }
710 } else if date_in_iso.month > era_start.month {
711 DateError::Range {
712 field: "month",
713 value: month as i32,
714 min: 1,
715 max: next_era_start.month as i32 - 1,
716 }
717 } else
718 {
720 DateError::Range {
721 field: "day",
722 value: day as i32,
723 min: 1,
724 max: next_era_start.day as i32 - 1,
725 }
726 });
727 }
728
729 let iso = Date::try_new_iso(date_in_iso.year, date_in_iso.month, date_in_iso.day)?;
730 Ok(JapaneseDateInner {
731 inner: iso.inner,
732 adjusted_year: year,
733 era,
734 })
735 }
736}
737
738#[cfg(test)]
739mod tests {
740 use super::*;
741 use crate::Ref;
742
743 fn single_test_roundtrip(calendar: Ref<Japanese>, era: &str, year: i32, month: u8, day: u8) {
744 let date = Date::try_new_japanese_with_calendar(era, year, month, day, calendar)
745 .unwrap_or_else(|e| {
746 panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
747 });
748 let iso = date.to_iso();
749 let reconstructed = Date::new_from_iso(iso, calendar);
750 assert_eq!(
751 date, reconstructed,
752 "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
753 );
754
755 assert_eq!(reconstructed.era_year().era, era);
757 assert_eq!(reconstructed.era_year().year, year);
758 }
759
760 fn single_test_roundtrip_ext(
761 calendar: Ref<JapaneseExtended>,
762 era: &str,
763 year: i32,
764 month: u8,
765 day: u8,
766 ) {
767 let date = Date::try_new_japanese_extended_with_calendar(era, year, month, day, calendar)
768 .unwrap_or_else(|e| {
769 panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
770 });
771 let iso = date.to_iso();
772 let reconstructed = Date::new_from_iso(iso, calendar);
773 assert_eq!(
774 date, reconstructed,
775 "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
776 )
777 }
778
779 fn single_test_gregorian_roundtrip_ext(
781 calendar: Ref<JapaneseExtended>,
782 era: &str,
783 year: i32,
784 month: u8,
785 day: u8,
786 era2: &str,
787 year2: i32,
788 ) {
789 let expected = Date::try_new_japanese_extended_with_calendar(era2, year2, month, day, calendar)
790 .unwrap_or_else(|e| {
791 panic!(
792 "Failed to construct expectation date with {era2:?}, {year2}, {month}, {day}: {e:?}"
793 )
794 });
795
796 let date = Date::try_new_japanese_extended_with_calendar(era, year, month, day, calendar)
797 .unwrap_or_else(|e| {
798 panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
799 });
800 let iso = date.to_iso();
801 let reconstructed = Date::new_from_iso(iso, calendar);
802 assert_eq!(
803 expected, reconstructed,
804 "Failed to roundtrip with {era:?}, {year}, {month}, {day} == {era2:?}, {year}"
805 )
806 }
807
808 fn single_test_error(
809 calendar: Ref<Japanese>,
810 era: &str,
811 year: i32,
812 month: u8,
813 day: u8,
814 error: DateError,
815 ) {
816 let date = Date::try_new_japanese_with_calendar(era, year, month, day, calendar);
817 assert_eq!(
818 date,
819 Err(error),
820 "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
821 )
822 }
823
824 fn single_test_error_ext(
825 calendar: Ref<JapaneseExtended>,
826 era: &str,
827 year: i32,
828 month: u8,
829 day: u8,
830 error: DateError,
831 ) {
832 let date = Date::try_new_japanese_extended_with_calendar(era, year, month, day, calendar);
833 assert_eq!(
834 date,
835 Err(error),
836 "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
837 )
838 }
839
840 #[test]
841 fn test_japanese() {
842 let calendar = Japanese::new();
843 let calendar_ext = JapaneseExtended::new();
844 let calendar = Ref(&calendar);
845 let calendar_ext = Ref(&calendar_ext);
846
847 single_test_roundtrip(calendar, "heisei", 12, 3, 1);
848 single_test_roundtrip(calendar, "taisho", 3, 3, 1);
849 single_test_error(
851 calendar,
852 "heisei",
853 1,
854 1,
855 1,
856 DateError::Range {
857 field: "day",
858 value: 1,
859 min: 8,
860 max: 31,
861 },
862 );
863
864 single_test_roundtrip_ext(calendar_ext, "heisei", 12, 3, 1);
865 single_test_roundtrip_ext(calendar_ext, "taisho", 3, 3, 1);
866 single_test_error_ext(
867 calendar_ext,
868 "heisei",
869 1,
870 1,
871 1,
872 DateError::Range {
873 field: "day",
874 value: 1,
875 min: 8,
876 max: 31,
877 },
878 );
879
880 single_test_roundtrip_ext(calendar_ext, "hakuho-672", 4, 3, 1);
881 single_test_error(calendar, "hakuho-672", 4, 3, 1, DateError::UnknownEra);
882
883 single_test_roundtrip(calendar, "bce", 100, 3, 1);
885 single_test_roundtrip(calendar, "bce", 1, 3, 1);
886 single_test_roundtrip(calendar, "ce", 1, 3, 1);
887 single_test_roundtrip(calendar, "ce", 100, 3, 1);
888 single_test_roundtrip_ext(calendar_ext, "ce", 100, 3, 1);
889 single_test_roundtrip(calendar, "ce", 1000, 3, 1);
890 single_test_error(
891 calendar,
892 "ce",
893 0,
894 3,
895 1,
896 DateError::Range {
897 field: "year",
898 value: 0,
899 min: 1,
900 max: i32::MAX,
901 },
902 );
903 single_test_error(
904 calendar,
905 "bce",
906 -1,
907 3,
908 1,
909 DateError::Range {
910 field: "year",
911 value: -1,
912 min: 1,
913 max: i32::MAX,
914 },
915 );
916
917 single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 1000, 3, 1, "choho-999", 2);
920 single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 749, 5, 10, "tenpyokampo-749", 1);
921 single_test_gregorian_roundtrip_ext(calendar_ext, "bce", 10, 3, 1, "bce", 10);
922
923 single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 20);
926 single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 14);
927 single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 7, 1);
928 single_test_error_ext(
929 calendar_ext,
930 "tenpyokampo-749",
931 1,
932 7,
933 5,
934 DateError::Range {
935 field: "month",
936 value: 7,
937 min: 1,
938 max: 6,
939 },
940 );
941 single_test_error_ext(
942 calendar_ext,
943 "tenpyokampo-749",
944 1,
945 4,
946 13,
947 DateError::Range {
948 field: "day",
949 value: 13,
950 min: 14,
951 max: 31,
952 },
953 );
954 }
955}