1use crate::cal::iso::{Iso, IsoDateInner};
20use crate::calendar_arithmetic::ArithmeticDate;
21use crate::error::DateError;
22use crate::{types, Calendar, Date, DateDuration, DateDurationUnit, RangeError};
23use calendrical_calculations::rata_die::RataDie;
24use tinystr::tinystr;
25
26const BUDDHIST_ERA_OFFSET: i32 = 543;
30
31#[derive(Copy, Clone, Debug, Default)]
32#[allow(clippy::exhaustive_structs)] pub struct Buddhist;
51
52impl crate::cal::scaffold::UnstableSealed for Buddhist {}
53impl Calendar for Buddhist {
54 type DateInner = IsoDateInner;
55 type Year = types::EraYear;
56
57 fn from_codes(
58 &self,
59 era: Option<&str>,
60 year: i32,
61 month_code: types::MonthCode,
62 day: u8,
63 ) -> Result<Self::DateInner, DateError> {
64 match era {
65 Some("be") | None => {}
66 _ => return Err(DateError::UnknownEra),
67 }
68 let year = year - BUDDHIST_ERA_OFFSET;
69
70 ArithmeticDate::new_from_codes(self, year, month_code, day).map(IsoDateInner)
71 }
72
73 fn from_iso(&self, iso: IsoDateInner) -> Self::DateInner {
74 iso
75 }
76
77 fn to_iso(&self, date: &Self::DateInner) -> IsoDateInner {
78 *date
79 }
80
81 fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
82 Iso.from_rata_die(rd)
83 }
84
85 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
86 Iso.to_rata_die(date)
87 }
88
89 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
90 Iso.months_in_year(date)
91 }
92
93 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
94 Iso.days_in_year(date)
95 }
96
97 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
98 Iso.days_in_month(date)
99 }
100
101 fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
102 Iso.offset_date(date, offset.cast_unit())
103 }
104
105 #[allow(clippy::field_reassign_with_default)] fn until(
107 &self,
108 date1: &Self::DateInner,
109 date2: &Self::DateInner,
110 _calendar2: &Self,
111 largest_unit: DateDurationUnit,
112 smallest_unit: DateDurationUnit,
113 ) -> DateDuration<Self> {
114 Iso.until(date1, date2, &Iso, largest_unit, smallest_unit)
115 .cast_unit()
116 }
117
118 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
120 types::EraYear {
121 era: tinystr!(16, "be"),
122 era_index: Some(0),
123 year: self.extended_year(date),
124 ambiguity: types::YearAmbiguity::CenturyRequired,
125 }
126 }
127
128 fn extended_year(&self, date: &Self::DateInner) -> i32 {
129 Iso.extended_year(date) + BUDDHIST_ERA_OFFSET
130 }
131
132 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
133 Iso.is_in_leap_year(date)
134 }
135
136 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
138 Iso.month(date)
139 }
140
141 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
143 Iso.day_of_month(date)
144 }
145
146 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
148 Iso.day_of_year(date)
149 }
150
151 fn debug_name(&self) -> &'static str {
152 "Buddhist"
153 }
154
155 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
156 Some(crate::preferences::CalendarAlgorithm::Buddhist)
157 }
158}
159
160impl Date<Buddhist> {
161 pub fn try_new_buddhist(year: i32, month: u8, day: u8) -> Result<Date<Buddhist>, RangeError> {
176 Date::try_new_iso(year - BUDDHIST_ERA_OFFSET, month, day)
177 .map(|d| Date::new_from_iso(d, Buddhist))
178 }
179}
180
181#[cfg(test)]
182mod test {
183 use calendrical_calculations::rata_die::RataDie;
184
185 use super::*;
186
187 #[test]
188 fn test_buddhist_roundtrip_near_rd_zero() {
189 for i in -10000..=10000 {
190 let rd = RataDie::new(i);
191 let iso1 = Date::from_rata_die(rd, Iso);
192 let buddhist = iso1.to_calendar(Buddhist);
193 let iso2 = buddhist.to_calendar(Iso);
194 let result = iso2.to_rata_die();
195 assert_eq!(rd, result);
196 }
197 }
198
199 #[test]
200 fn test_buddhist_roundtrip_near_epoch() {
201 for i in -208326..=-188326 {
203 let rd = RataDie::new(i);
204 let iso1 = Date::from_rata_die(rd, Iso);
205 let buddhist = iso1.to_calendar(Buddhist);
206 let iso2 = buddhist.to_calendar(Iso);
207 let result = iso2.to_rata_die();
208 assert_eq!(rd, result);
209 }
210 }
211
212 #[test]
213 fn test_buddhist_directionality_near_rd_zero() {
214 for i in -100..=100 {
215 for j in -100..=100 {
216 let iso_i = Date::from_rata_die(RataDie::new(i), Iso);
217 let iso_j = Date::from_rata_die(RataDie::new(j), Iso);
218
219 let buddhist_i = Date::new_from_iso(iso_i, Buddhist);
220 let buddhist_j = Date::new_from_iso(iso_j, Buddhist);
221
222 assert_eq!(
223 i.cmp(&j),
224 iso_i.cmp(&iso_j),
225 "ISO directionality inconsistent with directionality for i: {i}, j: {j}"
226 );
227
228 assert_eq!(
229 i.cmp(&j),
230 buddhist_i.cmp(&buddhist_j),
231 "Buddhist directionality inconsistent with directionality for i: {i}, j: {j}"
232 );
233 }
234 }
235 }
236
237 #[test]
238 fn test_buddhist_directionality_near_epoch() {
239 for i in -198426..=-198226 {
241 for j in -198426..=-198226 {
242 let iso_i = Date::from_rata_die(RataDie::new(i), Iso);
243 let iso_j = Date::from_rata_die(RataDie::new(j), Iso);
244
245 let buddhist_i = Date::new_from_iso(iso_i, Buddhist);
246 let buddhist_j = Date::new_from_iso(iso_j, Buddhist);
247
248 assert_eq!(
249 i.cmp(&j),
250 iso_i.cmp(&iso_j),
251 "ISO directionality inconsistent with directionality for i: {i}, j: {j}"
252 );
253
254 assert_eq!(
255 i.cmp(&j),
256 buddhist_i.cmp(&buddhist_j),
257 "Buddhist directionality inconsistent with directionality for i: {i}, j: {j}"
258 );
259 }
260 }
261 }
262
263 #[derive(Debug)]
264 struct TestCase {
265 iso_year: i32,
266 iso_month: u8,
267 iso_day: u8,
268 buddhist_year: i32,
269 buddhist_month: u8,
270 buddhist_day: u8,
271 }
272
273 fn check_test_case(case: TestCase) {
274 let iso_year = case.iso_year;
275 let iso_month = case.iso_month;
276 let iso_day = case.iso_day;
277 let buddhist_year = case.buddhist_year;
278 let buddhist_month = case.buddhist_month;
279 let buddhist_day = case.buddhist_day;
280
281 let iso1 = Date::try_new_iso(iso_year, iso_month, iso_day).unwrap();
282 let buddhist1 = iso1.to_calendar(Buddhist);
283 assert_eq!(
284 buddhist1.era_year().year,
285 buddhist_year,
286 "Iso -> Buddhist year check failed for case: {case:?}"
287 );
288 assert_eq!(
289 buddhist1.month().ordinal,
290 buddhist_month,
291 "Iso -> Buddhist month check failed for case: {case:?}"
292 );
293 assert_eq!(
294 buddhist1.day_of_month().0,
295 buddhist_day,
296 "Iso -> Buddhist day check failed for case: {case:?}"
297 );
298
299 let buddhist2 =
300 Date::try_new_buddhist(buddhist_year, buddhist_month, buddhist_day).unwrap();
301 let iso2 = buddhist2.to_calendar(Iso);
302 assert_eq!(
303 iso2.era_year().year,
304 iso_year,
305 "Buddhist -> Iso year check failed for case: {case:?}"
306 );
307 assert_eq!(
308 iso2.month().ordinal,
309 iso_month,
310 "Buddhist -> Iso month check failed for case: {case:?}"
311 );
312 assert_eq!(
313 iso2.day_of_month().0,
314 iso_day,
315 "Buddhist -> Iso day check failed for case: {case:?}"
316 );
317 }
318
319 #[test]
320 fn test_buddhist_cases_near_rd_zero() {
321 let cases = [
322 TestCase {
323 iso_year: -100,
324 iso_month: 2,
325 iso_day: 15,
326 buddhist_year: 443,
327 buddhist_month: 2,
328 buddhist_day: 15,
329 },
330 TestCase {
331 iso_year: -3,
332 iso_month: 10,
333 iso_day: 29,
334 buddhist_year: 540,
335 buddhist_month: 10,
336 buddhist_day: 29,
337 },
338 TestCase {
339 iso_year: 0,
340 iso_month: 12,
341 iso_day: 31,
342 buddhist_year: 543,
343 buddhist_month: 12,
344 buddhist_day: 31,
345 },
346 TestCase {
347 iso_year: 1,
348 iso_month: 1,
349 iso_day: 1,
350 buddhist_year: 544,
351 buddhist_month: 1,
352 buddhist_day: 1,
353 },
354 TestCase {
355 iso_year: 4,
356 iso_month: 2,
357 iso_day: 29,
358 buddhist_year: 547,
359 buddhist_month: 2,
360 buddhist_day: 29,
361 },
362 ];
363
364 for case in cases {
365 check_test_case(case);
366 }
367 }
368
369 #[test]
370 fn test_buddhist_cases_near_epoch() {
371 let cases = [
373 TestCase {
374 iso_year: -554,
375 iso_month: 12,
376 iso_day: 31,
377 buddhist_year: -11,
378 buddhist_month: 12,
379 buddhist_day: 31,
380 },
381 TestCase {
382 iso_year: -553,
383 iso_month: 1,
384 iso_day: 1,
385 buddhist_year: -10,
386 buddhist_month: 1,
387 buddhist_day: 1,
388 },
389 TestCase {
390 iso_year: -544,
391 iso_month: 8,
392 iso_day: 31,
393 buddhist_year: -1,
394 buddhist_month: 8,
395 buddhist_day: 31,
396 },
397 TestCase {
398 iso_year: -543,
399 iso_month: 5,
400 iso_day: 12,
401 buddhist_year: 0,
402 buddhist_month: 5,
403 buddhist_day: 12,
404 },
405 TestCase {
406 iso_year: -543,
407 iso_month: 12,
408 iso_day: 31,
409 buddhist_year: 0,
410 buddhist_month: 12,
411 buddhist_day: 31,
412 },
413 TestCase {
414 iso_year: -542,
415 iso_month: 1,
416 iso_day: 1,
417 buddhist_year: 1,
418 buddhist_month: 1,
419 buddhist_day: 1,
420 },
421 TestCase {
422 iso_year: -541,
423 iso_month: 7,
424 iso_day: 9,
425 buddhist_year: 2,
426 buddhist_month: 7,
427 buddhist_day: 9,
428 },
429 ];
430
431 for case in cases {
432 check_test_case(case);
433 }
434 }
435}