icu_calendar/cal/
coptic.rs1use crate::cal::iso::{Iso, IsoDateInner};
20use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
21use crate::error::DateError;
22use crate::{types, Calendar, Date, DateDuration, DateDurationUnit, RangeError};
23use calendrical_calculations::helpers::I32CastError;
24use calendrical_calculations::rata_die::RataDie;
25use tinystr::tinystr;
26
27#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)]
46#[allow(clippy::exhaustive_structs)] pub struct Coptic;
48
49#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
51pub struct CopticDateInner(pub(crate) ArithmeticDate<Coptic>);
52
53impl CalendarArithmetic for Coptic {
54 type YearInfo = i32;
55
56 fn days_in_provided_month(year: i32, month: u8) -> u8 {
57 if (1..=12).contains(&month) {
58 30
59 } else if month == 13 {
60 if Self::provided_year_is_leap(year) {
61 6
62 } else {
63 5
64 }
65 } else {
66 0
67 }
68 }
69
70 fn months_in_provided_year(_: i32) -> u8 {
71 13
72 }
73
74 fn provided_year_is_leap(year: i32) -> bool {
75 year.rem_euclid(4) == 3
76 }
77
78 fn last_month_day_in_provided_year(year: i32) -> (u8, u8) {
79 if Self::provided_year_is_leap(year) {
80 (13, 6)
81 } else {
82 (13, 5)
83 }
84 }
85
86 fn days_in_provided_year(year: i32) -> u16 {
87 if Self::provided_year_is_leap(year) {
88 366
89 } else {
90 365
91 }
92 }
93}
94
95impl crate::cal::scaffold::UnstableSealed for Coptic {}
96impl Calendar for Coptic {
97 type DateInner = CopticDateInner;
98 type Year = types::EraYear;
99 fn from_codes(
100 &self,
101 era: Option<&str>,
102 year: i32,
103 month_code: types::MonthCode,
104 day: u8,
105 ) -> Result<Self::DateInner, DateError> {
106 let year = match era {
107 Some("am") | None => year,
108 Some(_) => return Err(DateError::UnknownEra),
109 };
110
111 ArithmeticDate::new_from_codes(self, year, month_code, day).map(CopticDateInner)
112 }
113
114 fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
115 CopticDateInner(
116 match calendrical_calculations::coptic::coptic_from_fixed(rd) {
117 Err(I32CastError::BelowMin) => ArithmeticDate::min_date(),
118 Err(I32CastError::AboveMax) => ArithmeticDate::max_date(),
119 Ok((year, month, day)) => ArithmeticDate::new_unchecked(year, month, day),
120 },
121 )
122 }
123
124 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
125 calendrical_calculations::coptic::fixed_from_coptic(date.0.year, date.0.month, date.0.day)
126 }
127
128 fn from_iso(&self, iso: IsoDateInner) -> CopticDateInner {
129 self.from_rata_die(Iso.to_rata_die(&iso))
130 }
131
132 fn to_iso(&self, date: &Self::DateInner) -> IsoDateInner {
133 Iso.from_rata_die(self.to_rata_die(date))
134 }
135
136 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
137 date.0.months_in_year()
138 }
139
140 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
141 date.0.days_in_year()
142 }
143
144 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
145 date.0.days_in_month()
146 }
147
148 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
149 date.0.offset_date(offset, &());
150 }
151
152 #[allow(clippy::field_reassign_with_default)]
153 fn until(
154 &self,
155 date1: &Self::DateInner,
156 date2: &Self::DateInner,
157 _calendar2: &Self,
158 _largest_unit: DateDurationUnit,
159 _smallest_unit: DateDurationUnit,
160 ) -> DateDuration<Self> {
161 date1.0.until(date2.0, _largest_unit, _smallest_unit)
162 }
163
164 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
165 let year = self.extended_year(date);
166 types::EraYear {
167 era: tinystr!(16, "am"),
168 era_index: Some(0),
169 year,
170 ambiguity: types::YearAmbiguity::CenturyRequired,
171 }
172 }
173
174 fn extended_year(&self, date: &Self::DateInner) -> i32 {
175 date.0.extended_year()
176 }
177
178 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
179 Self::provided_year_is_leap(date.0.year)
180 }
181
182 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
183 date.0.month()
184 }
185
186 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
187 date.0.day_of_month()
188 }
189
190 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
191 date.0.day_of_year()
192 }
193
194 fn debug_name(&self) -> &'static str {
195 "Coptic"
196 }
197
198 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
199 Some(crate::preferences::CalendarAlgorithm::Coptic)
200 }
201}
202
203impl Date<Coptic> {
204 pub fn try_new_coptic(year: i32, month: u8, day: u8) -> Result<Date<Coptic>, RangeError> {
217 ArithmeticDate::new_from_ordinals(year, month, day)
218 .map(CopticDateInner)
219 .map(|inner| Date::from_raw(inner, Coptic))
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 #[test]
227 fn test_coptic_regression() {
228 let iso_date = Date::try_new_iso(-100, 3, 3).unwrap();
230 let coptic = iso_date.to_calendar(Coptic);
231 let recovered_iso = coptic.to_iso();
232 assert_eq!(iso_date, recovered_iso);
233 }
234}