1use crate::cal::abstract_gregorian::{impl_with_abstract_gregorian, GregorianYears};
6use crate::calendar_arithmetic::ArithmeticDate;
7use crate::error::UnknownEraError;
8use crate::preferences::CalendarAlgorithm;
9use crate::{types, Date, DateError, RangeError};
10use tinystr::tinystr;
11
12#[derive(Copy, Clone, Debug, Default)]
31#[allow(clippy::exhaustive_structs)] pub struct Roc;
33
34impl_with_abstract_gregorian!(crate::cal::Roc, RocDateInner, RocEra, _x, RocEra);
35
36#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub(crate) struct RocEra;
38
39impl GregorianYears for RocEra {
40 const EXTENDED_YEAR_OFFSET: i32 = 1911;
41
42 fn extended_from_era_year(
43 &self,
44 era: Option<&[u8]>,
45 year: i32,
46 ) -> Result<i32, UnknownEraError> {
47 match era {
48 None => Ok(year),
49 Some(b"roc") => Ok(year),
50 Some(b"broc") => Ok(1 - year),
51 Some(_) => Err(UnknownEraError),
52 }
53 }
54
55 fn era_year_from_extended(&self, extended_year: i32, _month: u8, _day: u8) -> types::EraYear {
56 if extended_year > 0 {
57 types::EraYear {
58 era: tinystr!(16, "roc"),
59 era_index: Some(1),
60 year: extended_year,
61 extended_year,
62 ambiguity: types::YearAmbiguity::CenturyRequired,
63 }
64 } else {
65 types::EraYear {
66 era: tinystr!(16, "broc"),
67 era_index: Some(0),
68 year: 1 - extended_year,
69 extended_year,
70 ambiguity: types::YearAmbiguity::EraAndCenturyRequired,
71 }
72 }
73 }
74
75 fn debug_name(&self) -> &'static str {
76 "ROC"
77 }
78
79 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
80 Some(CalendarAlgorithm::Roc)
81 }
82}
83
84impl Date<Roc> {
85 pub fn try_new_roc(year: i32, month: u8, day: u8) -> Result<Date<Roc>, RangeError> {
112 ArithmeticDate::new_gregorian::<RocEra>(year, month, day)
113 .map(RocDateInner)
114 .map(|i| Date::from_raw(i, Roc))
115 }
116}
117
118#[cfg(test)]
119mod test {
120
121 use super::*;
122 use crate::cal::Iso;
123 use calendrical_calculations::rata_die::RataDie;
124
125 #[derive(Debug)]
126 struct TestCase {
127 rd: RataDie,
128 iso_year: i32,
129 iso_month: u8,
130 iso_day: u8,
131 expected_year: i32,
132 expected_era: &'static str,
133 expected_month: u8,
134 expected_day: u8,
135 }
136
137 fn check_test_case(case: TestCase) {
138 let iso_from_rd = Date::from_rata_die(case.rd, Iso);
139 let roc_from_rd = Date::from_rata_die(case.rd, Roc);
140 assert_eq!(
141 roc_from_rd.era_year().year,
142 case.expected_year,
143 "Failed year check from RD: {case:?}\nISO: {iso_from_rd:?}\nROC: {roc_from_rd:?}"
144 );
145 assert_eq!(
146 roc_from_rd.era_year().era,
147 case.expected_era,
148 "Failed era check from RD: {case:?}\nISO: {iso_from_rd:?}\nROC: {roc_from_rd:?}"
149 );
150 assert_eq!(
151 roc_from_rd.extended_year(),
152 if case.expected_era == "roc" {
153 case.expected_year
154 } else {
155 1 - case.expected_year
156 },
157 "Failed year check from RD: {case:?}\nISO: {iso_from_rd:?}\nROC: {roc_from_rd:?}"
158 );
159 assert_eq!(
160 roc_from_rd.month().ordinal,
161 case.expected_month,
162 "Failed month check from RD: {case:?}\nISO: {iso_from_rd:?}\nROC: {roc_from_rd:?}"
163 );
164 assert_eq!(roc_from_rd.day_of_month().0, case.expected_day,
165 "Failed day_of_month check from RD: {case:?}\nISO: {iso_from_rd:?}\nROC: {roc_from_rd:?}");
166
167 let iso_from_case = Date::try_new_iso(case.iso_year, case.iso_month, case.iso_day)
168 .expect("Failed to initialize ISO date for {case:?}");
169 let roc_from_case = Date::new_from_iso(iso_from_case, Roc);
170 assert_eq!(iso_from_rd, iso_from_case,
171 "ISO from RD not equal to ISO generated from manually-input ymd\nCase: {case:?}\nRD: {iso_from_rd:?}\nManual: {iso_from_case:?}");
172 assert_eq!(roc_from_rd, roc_from_case,
173 "ROC date from RD not equal to ROC generated from manually-input ymd\nCase: {case:?}\nRD: {roc_from_rd:?}\nManual: {roc_from_case:?}");
174 }
175
176 #[test]
177 fn test_roc_current_era() {
178 let cases = [
184 TestCase {
185 rd: RataDie::new(697978),
186 iso_year: 1912,
187 iso_month: 1,
188 iso_day: 1,
189 expected_year: 1,
190 expected_era: "roc",
191 expected_month: 1,
192 expected_day: 1,
193 },
194 TestCase {
195 rd: RataDie::new(698037),
196 iso_year: 1912,
197 iso_month: 2,
198 iso_day: 29,
199 expected_year: 1,
200 expected_era: "roc",
201 expected_month: 2,
202 expected_day: 29,
203 },
204 TestCase {
205 rd: RataDie::new(698524),
206 iso_year: 1913,
207 iso_month: 6,
208 iso_day: 30,
209 expected_year: 2,
210 expected_era: "roc",
211 expected_month: 6,
212 expected_day: 30,
213 },
214 TestCase {
215 rd: RataDie::new(738714),
216 iso_year: 2023,
217 iso_month: 7,
218 iso_day: 13,
219 expected_year: 112,
220 expected_era: "roc",
221 expected_month: 7,
222 expected_day: 13,
223 },
224 ];
225
226 for case in cases {
227 check_test_case(case);
228 }
229 }
230
231 #[test]
232 fn test_roc_prior_era() {
233 let cases = [
238 TestCase {
239 rd: RataDie::new(697977),
240 iso_year: 1911,
241 iso_month: 12,
242 iso_day: 31,
243 expected_year: 1,
244 expected_era: "broc",
245 expected_month: 12,
246 expected_day: 31,
247 },
248 TestCase {
249 rd: RataDie::new(697613),
250 iso_year: 1911,
251 iso_month: 1,
252 iso_day: 1,
253 expected_year: 1,
254 expected_era: "broc",
255 expected_month: 1,
256 expected_day: 1,
257 },
258 TestCase {
259 rd: RataDie::new(697612),
260 iso_year: 1910,
261 iso_month: 12,
262 iso_day: 31,
263 expected_year: 2,
264 expected_era: "broc",
265 expected_month: 12,
266 expected_day: 31,
267 },
268 TestCase {
269 rd: RataDie::new(696576),
270 iso_year: 1908,
271 iso_month: 2,
272 iso_day: 29,
273 expected_year: 4,
274 expected_era: "broc",
275 expected_month: 2,
276 expected_day: 29,
277 },
278 TestCase {
279 rd: RataDie::new(1),
280 iso_year: 1,
281 iso_month: 1,
282 iso_day: 1,
283 expected_year: 1911,
284 expected_era: "broc",
285 expected_month: 1,
286 expected_day: 1,
287 },
288 TestCase {
289 rd: RataDie::new(0),
290 iso_year: 0,
291 iso_month: 12,
292 iso_day: 31,
293 expected_year: 1912,
294 expected_era: "broc",
295 expected_month: 12,
296 expected_day: 31,
297 },
298 ];
299
300 for case in cases {
301 check_test_case(case);
302 }
303 }
304
305 #[test]
306 fn test_roc_directionality_near_epoch() {
307 let rd_epoch_start = 697978;
311 for i in (rd_epoch_start - 100)..=(rd_epoch_start + 100) {
312 for j in (rd_epoch_start - 100)..=(rd_epoch_start + 100) {
313 let iso_i = Date::from_rata_die(RataDie::new(i), Iso);
314 let iso_j = Date::from_rata_die(RataDie::new(j), Iso);
315
316 let roc_i = Date::from_rata_die(RataDie::new(i), Roc);
317 let roc_j = Date::from_rata_die(RataDie::new(j), Roc);
318
319 assert_eq!(
320 i.cmp(&j),
321 iso_i.cmp(&iso_j),
322 "ISO directionality inconsistent with directionality for i: {i}, j: {j}"
323 );
324 assert_eq!(
325 i.cmp(&j),
326 roc_i.cmp(&roc_j),
327 "ROC directionality inconsistent with directionality for i: {i}, j: {j}"
328 );
329 }
330 }
331 }
332
333 #[test]
334 fn test_roc_directionality_near_rd_zero() {
335 for i in -100..=100 {
337 for j in -100..100 {
338 let iso_i = Date::from_rata_die(RataDie::new(i), Iso);
339 let iso_j = Date::from_rata_die(RataDie::new(j), Iso);
340
341 let roc_i = Date::from_rata_die(RataDie::new(i), Roc);
342 let roc_j = Date::from_rata_die(RataDie::new(j), Roc);
343
344 assert_eq!(
345 i.cmp(&j),
346 iso_i.cmp(&iso_j),
347 "ISO directionality inconsistent with directionality for i: {i}, j: {j}"
348 );
349 assert_eq!(
350 i.cmp(&j),
351 roc_i.cmp(&roc_j),
352 "ROC directionality inconsistent with directionality for i: {i}, j: {j}"
353 );
354 }
355 }
356 }
357}