/rust/registry/src/index.crates.io-6f17d22bba15001f/icu_calendar-1.5.2/src/buddhist.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // This file is part of ICU4X. For terms of use, please see the file |
2 | | // called LICENSE at the top level of the ICU4X source tree |
3 | | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
4 | | |
5 | | //! This module contains types and implementations for the Buddhist calendar. |
6 | | //! |
7 | | //! ```rust |
8 | | //! use icu::calendar::{buddhist::Buddhist, Date, DateTime}; |
9 | | //! |
10 | | //! // `Date` type |
11 | | //! let date_iso = Date::try_new_iso_date(1970, 1, 2) |
12 | | //! .expect("Failed to initialize ISO Date instance."); |
13 | | //! let date_buddhist = Date::new_from_iso(date_iso, Buddhist); |
14 | | //! |
15 | | //! // `DateTime` type |
16 | | //! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0) |
17 | | //! .expect("Failed to initialize ISO DateTime instance."); |
18 | | //! let datetime_buddhist = DateTime::new_from_iso(datetime_iso, Buddhist); |
19 | | //! |
20 | | //! // `Date` checks |
21 | | //! assert_eq!(date_buddhist.year().number, 2513); |
22 | | //! assert_eq!(date_buddhist.month().ordinal, 1); |
23 | | //! assert_eq!(date_buddhist.day_of_month().0, 2); |
24 | | //! |
25 | | //! // `DateTime` type |
26 | | //! assert_eq!(datetime_buddhist.date.year().number, 2513); |
27 | | //! assert_eq!(datetime_buddhist.date.month().ordinal, 1); |
28 | | //! assert_eq!(datetime_buddhist.date.day_of_month().0, 2); |
29 | | //! assert_eq!(datetime_buddhist.time.hour.number(), 13); |
30 | | //! assert_eq!(datetime_buddhist.time.minute.number(), 1); |
31 | | //! assert_eq!(datetime_buddhist.time.second.number(), 0); |
32 | | //! ``` |
33 | | |
34 | | use crate::any_calendar::AnyCalendarKind; |
35 | | use crate::calendar_arithmetic::ArithmeticDate; |
36 | | use crate::iso::{Iso, IsoDateInner}; |
37 | | use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time}; |
38 | | use tinystr::tinystr; |
39 | | |
40 | | /// The number of years the Buddhist Era is ahead of C.E. by |
41 | | /// |
42 | | /// (1 AD = 544 BE) |
43 | | const BUDDHIST_ERA_OFFSET: i32 = 543; |
44 | | |
45 | | #[derive(Copy, Clone, Debug, Default)] |
46 | | /// The [Thai Solar Buddhist Calendar][cal] |
47 | | /// |
48 | | /// The [Thai Solar Buddhist Calendar][cal] is a solar calendar used in Thailand, with twelve months. |
49 | | /// The months and days are identical to that of the Gregorian calendar, however the years are counted |
50 | | /// differently using the Buddhist Era. |
51 | | /// |
52 | | /// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar. |
53 | | /// |
54 | | /// [cal]: https://en.wikipedia.org/wiki/Thai_solar_calendar |
55 | | /// |
56 | | /// # Era codes |
57 | | /// |
58 | | /// This calendar supports one era, `"be"`, with 1 B.E. being 543 BCE. |
59 | | /// |
60 | | /// # Month codes |
61 | | /// |
62 | | /// This calendar supports 12 solar month codes (`"M01" - "M12"`) |
63 | | #[allow(clippy::exhaustive_structs)] // this type is stable |
64 | | pub struct Buddhist; |
65 | | |
66 | | impl Calendar for Buddhist { |
67 | | type DateInner = IsoDateInner; |
68 | | |
69 | 0 | fn date_from_codes( |
70 | 0 | &self, |
71 | 0 | era: types::Era, |
72 | 0 | year: i32, |
73 | 0 | month_code: types::MonthCode, |
74 | 0 | day: u8, |
75 | 0 | ) -> Result<Self::DateInner, CalendarError> { |
76 | 0 | if era.0 != tinystr!(16, "be") { |
77 | 0 | return Err(CalendarError::UnknownEra(era.0, self.debug_name())); |
78 | 0 | } |
79 | 0 | let year = year - BUDDHIST_ERA_OFFSET; |
80 | 0 |
|
81 | 0 | ArithmeticDate::new_from_codes(self, year, month_code, day).map(IsoDateInner) |
82 | 0 | } |
83 | 0 | fn date_from_iso(&self, iso: Date<Iso>) -> IsoDateInner { |
84 | 0 | *iso.inner() |
85 | 0 | } |
86 | | |
87 | 0 | fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> { |
88 | 0 | Date::from_raw(*date, Iso) |
89 | 0 | } |
90 | | |
91 | 0 | fn months_in_year(&self, date: &Self::DateInner) -> u8 { |
92 | 0 | Iso.months_in_year(date) |
93 | 0 | } |
94 | | |
95 | 0 | fn days_in_year(&self, date: &Self::DateInner) -> u16 { |
96 | 0 | Iso.days_in_year(date) |
97 | 0 | } |
98 | | |
99 | 0 | fn days_in_month(&self, date: &Self::DateInner) -> u8 { |
100 | 0 | Iso.days_in_month(date) |
101 | 0 | } |
102 | | |
103 | 0 | fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) { |
104 | 0 | Iso.offset_date(date, offset.cast_unit()) |
105 | 0 | } |
106 | | |
107 | | #[allow(clippy::field_reassign_with_default)] // it's more clear this way |
108 | 0 | fn until( |
109 | 0 | &self, |
110 | 0 | date1: &Self::DateInner, |
111 | 0 | date2: &Self::DateInner, |
112 | 0 | _calendar2: &Self, |
113 | 0 | largest_unit: DateDurationUnit, |
114 | 0 | smallest_unit: DateDurationUnit, |
115 | 0 | ) -> DateDuration<Self> { |
116 | 0 | Iso.until(date1, date2, &Iso, largest_unit, smallest_unit) |
117 | 0 | .cast_unit() |
118 | 0 | } |
119 | | |
120 | | /// The calendar-specific year represented by `date` |
121 | 0 | fn year(&self, date: &Self::DateInner) -> types::FormattableYear { |
122 | 0 | iso_year_as_buddhist(date.0.year) |
123 | 0 | } |
124 | | |
125 | 0 | fn is_in_leap_year(&self, date: &Self::DateInner) -> bool { |
126 | 0 | Iso.is_in_leap_year(date) |
127 | 0 | } |
128 | | |
129 | | /// The calendar-specific month represented by `date` |
130 | 0 | fn month(&self, date: &Self::DateInner) -> types::FormattableMonth { |
131 | 0 | Iso.month(date) |
132 | 0 | } |
133 | | |
134 | | /// The calendar-specific day-of-month represented by `date` |
135 | 0 | fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth { |
136 | 0 | Iso.day_of_month(date) |
137 | 0 | } |
138 | | |
139 | | /// Information of the day of the year |
140 | 0 | fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo { |
141 | 0 | let prev_year = date.0.year - 1; |
142 | 0 | let next_year = date.0.year + 1; |
143 | 0 | types::DayOfYearInfo { |
144 | 0 | day_of_year: Iso::day_of_year(*date), |
145 | 0 | days_in_year: Iso::days_in_year_direct(date.0.year), |
146 | 0 | prev_year: iso_year_as_buddhist(prev_year), |
147 | 0 | days_in_prev_year: Iso::days_in_year_direct(prev_year), |
148 | 0 | next_year: iso_year_as_buddhist(next_year), |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | 0 | fn debug_name(&self) -> &'static str { |
153 | 0 | "Buddhist" |
154 | 0 | } |
155 | | |
156 | 0 | fn any_calendar_kind(&self) -> Option<AnyCalendarKind> { |
157 | 0 | Some(AnyCalendarKind::Buddhist) |
158 | 0 | } |
159 | | } |
160 | | |
161 | | impl Date<Buddhist> { |
162 | | /// Construct a new Buddhist Date. |
163 | | /// |
164 | | /// Years are specified as BE years. |
165 | | /// |
166 | | /// ```rust |
167 | | /// use icu::calendar::Date; |
168 | | /// |
169 | | /// let date_buddhist = Date::try_new_buddhist_date(1970, 1, 2) |
170 | | /// .expect("Failed to initialize Buddhist Date instance."); |
171 | | /// |
172 | | /// assert_eq!(date_buddhist.year().number, 1970); |
173 | | /// assert_eq!(date_buddhist.month().ordinal, 1); |
174 | | /// assert_eq!(date_buddhist.day_of_month().0, 2); |
175 | | /// ``` |
176 | 0 | pub fn try_new_buddhist_date( |
177 | 0 | year: i32, |
178 | 0 | month: u8, |
179 | 0 | day: u8, |
180 | 0 | ) -> Result<Date<Buddhist>, CalendarError> { |
181 | 0 | Date::try_new_iso_date(year - BUDDHIST_ERA_OFFSET, month, day) |
182 | 0 | .map(|d| Date::new_from_iso(d, Buddhist)) |
183 | 0 | } |
184 | | } |
185 | | |
186 | | impl DateTime<Buddhist> { |
187 | | /// Construct a new Buddhist datetime from integers. |
188 | | /// |
189 | | /// Years are specified as BE years. |
190 | | /// |
191 | | /// ```rust |
192 | | /// use icu::calendar::DateTime; |
193 | | /// |
194 | | /// let datetime_buddhist = |
195 | | /// DateTime::try_new_buddhist_datetime(1970, 1, 2, 13, 1, 0) |
196 | | /// .expect("Failed to initialize Buddhist DateTime instance."); |
197 | | /// |
198 | | /// assert_eq!(datetime_buddhist.date.year().number, 1970); |
199 | | /// assert_eq!(datetime_buddhist.date.month().ordinal, 1); |
200 | | /// assert_eq!(datetime_buddhist.date.day_of_month().0, 2); |
201 | | /// assert_eq!(datetime_buddhist.time.hour.number(), 13); |
202 | | /// assert_eq!(datetime_buddhist.time.minute.number(), 1); |
203 | | /// assert_eq!(datetime_buddhist.time.second.number(), 0); |
204 | | /// ``` |
205 | 0 | pub fn try_new_buddhist_datetime( |
206 | 0 | year: i32, |
207 | 0 | month: u8, |
208 | 0 | day: u8, |
209 | 0 | hour: u8, |
210 | 0 | minute: u8, |
211 | 0 | second: u8, |
212 | 0 | ) -> Result<DateTime<Buddhist>, CalendarError> { |
213 | 0 | Ok(DateTime { |
214 | 0 | date: Date::try_new_buddhist_date(year, month, day)?, |
215 | 0 | time: Time::try_new(hour, minute, second, 0)?, |
216 | | }) |
217 | 0 | } |
218 | | } |
219 | | |
220 | 0 | fn iso_year_as_buddhist(year: i32) -> types::FormattableYear { |
221 | 0 | let buddhist_year = year + BUDDHIST_ERA_OFFSET; |
222 | 0 | types::FormattableYear { |
223 | 0 | era: types::Era(tinystr!(16, "be")), |
224 | 0 | number: buddhist_year, |
225 | 0 | cyclic: None, |
226 | 0 | related_iso: None, |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | #[cfg(test)] |
231 | | mod test { |
232 | | use calendrical_calculations::rata_die::RataDie; |
233 | | |
234 | | use super::*; |
235 | | |
236 | | #[test] |
237 | | fn test_buddhist_roundtrip_near_rd_zero() { |
238 | | for i in -10000..=10000 { |
239 | | let rd = RataDie::new(i); |
240 | | let iso1 = Iso::iso_from_fixed(rd); |
241 | | let buddhist = iso1.to_calendar(Buddhist); |
242 | | let iso2 = buddhist.to_calendar(Iso); |
243 | | let result = Iso::fixed_from_iso(iso2.inner); |
244 | | assert_eq!(rd, result); |
245 | | } |
246 | | } |
247 | | |
248 | | #[test] |
249 | | fn test_buddhist_roundtrip_near_epoch() { |
250 | | // Buddhist epoch start RD: -198326 |
251 | | for i in -208326..=-188326 { |
252 | | let rd = RataDie::new(i); |
253 | | let iso1 = Iso::iso_from_fixed(rd); |
254 | | let buddhist = iso1.to_calendar(Buddhist); |
255 | | let iso2 = buddhist.to_calendar(Iso); |
256 | | let result = Iso::fixed_from_iso(iso2.inner); |
257 | | assert_eq!(rd, result); |
258 | | } |
259 | | } |
260 | | |
261 | | #[test] |
262 | | fn test_buddhist_directionality_near_rd_zero() { |
263 | | for i in -100..=100 { |
264 | | for j in -100..=100 { |
265 | | let iso_i = Iso::iso_from_fixed(RataDie::new(i)); |
266 | | let iso_j = Iso::iso_from_fixed(RataDie::new(j)); |
267 | | |
268 | | let buddhist_i = Date::new_from_iso(iso_i, Buddhist); |
269 | | let buddhist_j = Date::new_from_iso(iso_j, Buddhist); |
270 | | |
271 | | assert_eq!( |
272 | | i.cmp(&j), |
273 | | iso_i.cmp(&iso_j), |
274 | | "ISO directionality inconsistent with directionality for i: {i}, j: {j}" |
275 | | ); |
276 | | |
277 | | assert_eq!( |
278 | | i.cmp(&j), |
279 | | buddhist_i.cmp(&buddhist_j), |
280 | | "Buddhist directionality inconsistent with directionality for i: {i}, j: {j}" |
281 | | ); |
282 | | } |
283 | | } |
284 | | } |
285 | | |
286 | | #[test] |
287 | | fn test_buddhist_directionality_near_epoch() { |
288 | | // Buddhist epoch start RD: -198326 |
289 | | for i in -198426..=-198226 { |
290 | | for j in -198426..=-198226 { |
291 | | let iso_i = Iso::iso_from_fixed(RataDie::new(i)); |
292 | | let iso_j = Iso::iso_from_fixed(RataDie::new(j)); |
293 | | |
294 | | let buddhist_i = Date::new_from_iso(iso_i, Buddhist); |
295 | | let buddhist_j = Date::new_from_iso(iso_j, Buddhist); |
296 | | |
297 | | assert_eq!( |
298 | | i.cmp(&j), |
299 | | iso_i.cmp(&iso_j), |
300 | | "ISO directionality inconsistent with directionality for i: {i}, j: {j}" |
301 | | ); |
302 | | |
303 | | assert_eq!( |
304 | | i.cmp(&j), |
305 | | buddhist_i.cmp(&buddhist_j), |
306 | | "Buddhist directionality inconsistent with directionality for i: {i}, j: {j}" |
307 | | ); |
308 | | } |
309 | | } |
310 | | } |
311 | | |
312 | | #[derive(Debug)] |
313 | | struct TestCase { |
314 | | iso_year: i32, |
315 | | iso_month: u8, |
316 | | iso_day: u8, |
317 | | buddhist_year: i32, |
318 | | buddhist_month: u8, |
319 | | buddhist_day: u8, |
320 | | } |
321 | | |
322 | | fn check_test_case(case: TestCase) { |
323 | | let iso_year = case.iso_year; |
324 | | let iso_month = case.iso_month; |
325 | | let iso_day = case.iso_day; |
326 | | let buddhist_year = case.buddhist_year; |
327 | | let buddhist_month = case.buddhist_month; |
328 | | let buddhist_day = case.buddhist_day; |
329 | | |
330 | | let iso1 = Date::try_new_iso_date(iso_year, iso_month, iso_day).unwrap(); |
331 | | let buddhist1 = iso1.to_calendar(Buddhist); |
332 | | assert_eq!( |
333 | | buddhist1.year().number, |
334 | | buddhist_year, |
335 | | "Iso -> Buddhist year check failed for case: {case:?}" |
336 | | ); |
337 | | assert_eq!( |
338 | | buddhist1.month().ordinal, |
339 | | buddhist_month as u32, |
340 | | "Iso -> Buddhist month check failed for case: {case:?}" |
341 | | ); |
342 | | assert_eq!( |
343 | | buddhist1.day_of_month().0, |
344 | | buddhist_day as u32, |
345 | | "Iso -> Buddhist day check failed for case: {case:?}" |
346 | | ); |
347 | | |
348 | | let buddhist2 = |
349 | | Date::try_new_buddhist_date(buddhist_year, buddhist_month, buddhist_day).unwrap(); |
350 | | let iso2 = buddhist2.to_calendar(Iso); |
351 | | assert_eq!( |
352 | | iso2.year().number, |
353 | | iso_year, |
354 | | "Buddhist -> Iso year check failed for case: {case:?}" |
355 | | ); |
356 | | assert_eq!( |
357 | | iso2.month().ordinal, |
358 | | iso_month as u32, |
359 | | "Buddhist -> Iso month check failed for case: {case:?}" |
360 | | ); |
361 | | assert_eq!( |
362 | | iso2.day_of_month().0, |
363 | | iso_day as u32, |
364 | | "Buddhist -> Iso day check failed for case: {case:?}" |
365 | | ); |
366 | | } |
367 | | |
368 | | #[test] |
369 | | fn test_buddhist_cases_near_rd_zero() { |
370 | | let cases = [ |
371 | | TestCase { |
372 | | iso_year: -100, |
373 | | iso_month: 2, |
374 | | iso_day: 15, |
375 | | buddhist_year: 443, |
376 | | buddhist_month: 2, |
377 | | buddhist_day: 15, |
378 | | }, |
379 | | TestCase { |
380 | | iso_year: -3, |
381 | | iso_month: 10, |
382 | | iso_day: 29, |
383 | | buddhist_year: 540, |
384 | | buddhist_month: 10, |
385 | | buddhist_day: 29, |
386 | | }, |
387 | | TestCase { |
388 | | iso_year: 0, |
389 | | iso_month: 12, |
390 | | iso_day: 31, |
391 | | buddhist_year: 543, |
392 | | buddhist_month: 12, |
393 | | buddhist_day: 31, |
394 | | }, |
395 | | TestCase { |
396 | | iso_year: 1, |
397 | | iso_month: 1, |
398 | | iso_day: 1, |
399 | | buddhist_year: 544, |
400 | | buddhist_month: 1, |
401 | | buddhist_day: 1, |
402 | | }, |
403 | | TestCase { |
404 | | iso_year: 4, |
405 | | iso_month: 2, |
406 | | iso_day: 29, |
407 | | buddhist_year: 547, |
408 | | buddhist_month: 2, |
409 | | buddhist_day: 29, |
410 | | }, |
411 | | ]; |
412 | | |
413 | | for case in cases { |
414 | | check_test_case(case); |
415 | | } |
416 | | } |
417 | | |
418 | | #[test] |
419 | | fn test_buddhist_cases_near_epoch() { |
420 | | // 1 BE = 543 BCE = -542 ISO |
421 | | let cases = [ |
422 | | TestCase { |
423 | | iso_year: -554, |
424 | | iso_month: 12, |
425 | | iso_day: 31, |
426 | | buddhist_year: -11, |
427 | | buddhist_month: 12, |
428 | | buddhist_day: 31, |
429 | | }, |
430 | | TestCase { |
431 | | iso_year: -553, |
432 | | iso_month: 1, |
433 | | iso_day: 1, |
434 | | buddhist_year: -10, |
435 | | buddhist_month: 1, |
436 | | buddhist_day: 1, |
437 | | }, |
438 | | TestCase { |
439 | | iso_year: -544, |
440 | | iso_month: 8, |
441 | | iso_day: 31, |
442 | | buddhist_year: -1, |
443 | | buddhist_month: 8, |
444 | | buddhist_day: 31, |
445 | | }, |
446 | | TestCase { |
447 | | iso_year: -543, |
448 | | iso_month: 5, |
449 | | iso_day: 12, |
450 | | buddhist_year: 0, |
451 | | buddhist_month: 5, |
452 | | buddhist_day: 12, |
453 | | }, |
454 | | TestCase { |
455 | | iso_year: -543, |
456 | | iso_month: 12, |
457 | | iso_day: 31, |
458 | | buddhist_year: 0, |
459 | | buddhist_month: 12, |
460 | | buddhist_day: 31, |
461 | | }, |
462 | | TestCase { |
463 | | iso_year: -542, |
464 | | iso_month: 1, |
465 | | iso_day: 1, |
466 | | buddhist_year: 1, |
467 | | buddhist_month: 1, |
468 | | buddhist_day: 1, |
469 | | }, |
470 | | TestCase { |
471 | | iso_year: -541, |
472 | | iso_month: 7, |
473 | | iso_day: 9, |
474 | | buddhist_year: 2, |
475 | | buddhist_month: 7, |
476 | | buddhist_day: 9, |
477 | | }, |
478 | | ]; |
479 | | |
480 | | for case in cases { |
481 | | check_test_case(case); |
482 | | } |
483 | | } |
484 | | } |