/rust/registry/src/index.crates.io-6f17d22bba15001f/icu_calendar-1.5.2/src/chinese.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 Chinese calendar. |
6 | | //! |
7 | | //! ```rust |
8 | | //! use icu::calendar::{chinese::Chinese, Date, DateTime, Ref}; |
9 | | //! |
10 | | //! let chinese = Chinese::new(); |
11 | | //! let chinese = Ref(&chinese); // to avoid cloning |
12 | | //! |
13 | | //! // `Date` type |
14 | | //! let chinese_date = |
15 | | //! Date::try_new_chinese_date_with_calendar(4660, 6, 6, chinese) |
16 | | //! .expect("Failed to initialize Chinese Date instance."); |
17 | | //! |
18 | | //! // `DateTime` type |
19 | | //! let chinese_datetime = DateTime::try_new_chinese_datetime_with_calendar( |
20 | | //! 4660, 6, 6, 13, 1, 0, chinese, |
21 | | //! ) |
22 | | //! .expect("Failed to initialize Chinese DateTime instance"); |
23 | | //! |
24 | | //! // `Date` checks |
25 | | //! assert_eq!(chinese_date.year().number, 4660); |
26 | | //! assert_eq!(chinese_date.year().related_iso, Some(2023)); |
27 | | //! assert_eq!(chinese_date.year().cyclic.unwrap().get(), 40); |
28 | | //! assert_eq!(chinese_date.month().ordinal, 6); |
29 | | //! assert_eq!(chinese_date.day_of_month().0, 6); |
30 | | //! |
31 | | //! // `DateTime` checks |
32 | | //! assert_eq!(chinese_datetime.date.year().number, 4660); |
33 | | //! assert_eq!(chinese_datetime.date.year().related_iso, Some(2023)); |
34 | | //! assert_eq!(chinese_datetime.date.year().cyclic.unwrap().get(), 40); |
35 | | //! assert_eq!(chinese_datetime.date.month().ordinal, 6); |
36 | | //! assert_eq!(chinese_datetime.date.day_of_month().0, 6); |
37 | | //! assert_eq!(chinese_datetime.time.hour.number(), 13); |
38 | | //! assert_eq!(chinese_datetime.time.minute.number(), 1); |
39 | | //! assert_eq!(chinese_datetime.time.second.number(), 0); |
40 | | //! ``` |
41 | | |
42 | | use crate::any_calendar::AnyCalendarKind; |
43 | | use crate::calendar_arithmetic::CalendarArithmetic; |
44 | | use crate::calendar_arithmetic::PrecomputedDataSource; |
45 | | use crate::chinese_based::{ |
46 | | chinese_based_ordinal_lunar_month_from_code, ChineseBasedDateInner, |
47 | | ChineseBasedPrecomputedData, ChineseBasedWithDataLoading, ChineseBasedYearInfo, |
48 | | }; |
49 | | use crate::iso::Iso; |
50 | | use crate::provider::chinese_based::ChineseCacheV1Marker; |
51 | | use crate::types::{Era, FormattableYear}; |
52 | | use crate::AsCalendar; |
53 | | use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time}; |
54 | | use core::cmp::Ordering; |
55 | | use core::num::NonZeroU8; |
56 | | use icu_provider::prelude::*; |
57 | | use tinystr::tinystr; |
58 | | |
59 | | /// The Chinese Calendar |
60 | | /// |
61 | | /// The [Chinese Calendar] is a lunisolar calendar used traditionally in China as well as in other |
62 | | /// countries particularly in, but not limited to, East Asia. It is often used today to track important |
63 | | /// cultural events and holidays like the Chinese Lunar New Year. |
64 | | /// |
65 | | /// This type can be used with [`Date`] or [`DateTime`] to represent dates in the Chinese calendar. |
66 | | /// |
67 | | /// # Months |
68 | | /// |
69 | | /// The Chinese calendar is an astronomical calendar which uses the phases of the moon to track months. |
70 | | /// Each month starts on the date of the new moon as observed from China, meaning that months last 29 |
71 | | /// or 30 days. |
72 | | /// |
73 | | /// One year in the Chinese calendar is typically 12 lunar months; however, because 12 lunar months does |
74 | | /// not line up to one solar year, the Chinese calendar will add an intercalary leap month approximately |
75 | | /// every three years to keep Chinese calendar months in line with the solar year. |
76 | | /// |
77 | | /// Leap months can happen after any month; the month in which a leap month occurs is based on the alignment |
78 | | /// of months with 24 solar terms into which the solar year is divided. |
79 | | /// |
80 | | /// # Year and Era codes |
81 | | /// |
82 | | /// Unlike the Gregorian calendar, the Chinese calendar does not traditionally count years in an infinitely |
83 | | /// increasing sequence. Instead, 10 "celestial stems" and 12 "terrestrial branches" are combined to form a |
84 | | /// cycle of year names which repeats every 60 years. However, for the purposes of calendar calculations and |
85 | | /// conversions, this module counts Chinese years in an infinite system similar to ISO, with year 1 in the |
86 | | /// calendar corresponding to the inception of the calendar, marked as 2637 BCE (ISO: -2636), and negative |
87 | | /// years marking Chinese years before February 15, 2637 BCE. |
88 | | /// |
89 | | /// Because the Chinese calendar does not traditionally count years, era codes are not used in this calendar; |
90 | | /// this crate supports a single era code "chinese". |
91 | | /// |
92 | | /// This Chinese calendar implementation also supports a related ISO year, which marks the ISO year in which a |
93 | | /// Chinese year begins, and a cyclic year corresponding to the year in the 60 year cycle as described above. |
94 | | /// |
95 | | /// For more information, suggested reading materials include: |
96 | | /// * _Calendrical Calculations_ by Reingold & Dershowitz |
97 | | /// * _The Mathematics of the Chinese Calendar_ by Helmer Aslaksen <https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.139.9311&rep=rep1&type=pdf> |
98 | | /// * Wikipedia: <https://en.wikipedia.org/wiki/Chinese_calendar> |
99 | | /// |
100 | | /// # Month codes |
101 | | /// |
102 | | /// This calendar is a lunisolar calendar. It supports regular month codes `"M01" - "M12"` as well |
103 | | /// as leap month codes `"M01L" - "M12L"`. |
104 | | /// |
105 | | /// This calendar is currently in a preview state: formatting for this calendar is not |
106 | | /// going to be perfect. |
107 | | #[derive(Clone, Debug, Default)] |
108 | | pub struct Chinese { |
109 | | data: Option<DataPayload<ChineseCacheV1Marker>>, |
110 | | } |
111 | | |
112 | | /// The inner date type used for representing [`Date`]s of [`Chinese`]. See [`Date`] and [`Chinese`] for more details. |
113 | | #[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] |
114 | | pub struct ChineseDateInner(ChineseBasedDateInner<Chinese>); |
115 | | |
116 | | type Inner = ChineseBasedDateInner<Chinese>; |
117 | | |
118 | | // we want these impls without the `C: Copy/Clone` bounds |
119 | | impl Copy for ChineseDateInner {} |
120 | | impl Clone for ChineseDateInner { |
121 | 0 | fn clone(&self) -> Self { |
122 | 0 | *self |
123 | 0 | } |
124 | | } |
125 | | |
126 | | // These impls just make custom derives on types containing C |
127 | | // work. They're basically no-ops |
128 | | impl PartialEq for Chinese { |
129 | 0 | fn eq(&self, _: &Self) -> bool { |
130 | 0 | true |
131 | 0 | } |
132 | | } |
133 | | impl Eq for Chinese {} |
134 | | #[allow(clippy::non_canonical_partial_ord_impl)] // this is intentional |
135 | | impl PartialOrd for Chinese { |
136 | 0 | fn partial_cmp(&self, _: &Self) -> Option<Ordering> { |
137 | 0 | Some(Ordering::Equal) |
138 | 0 | } |
139 | | } |
140 | | |
141 | | impl Ord for Chinese { |
142 | 0 | fn cmp(&self, _: &Self) -> Ordering { |
143 | 0 | Ordering::Equal |
144 | 0 | } |
145 | | } |
146 | | |
147 | | impl Chinese { |
148 | | /// Creates a new [`Chinese`] with some precomputed calendrical calculations. |
149 | | /// |
150 | | /// ✨ *Enabled with the `compiled_data` Cargo feature.* |
151 | | /// |
152 | | /// [📚 Help choosing a constructor](icu_provider::constructors) |
153 | | #[cfg(feature = "compiled_data")] |
154 | 0 | pub const fn new() -> Self { |
155 | 0 | Self { |
156 | 0 | data: Some(DataPayload::from_static_ref( |
157 | 0 | crate::provider::Baked::SINGLETON_CALENDAR_CHINESECACHE_V1, |
158 | 0 | )), |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError, |
163 | | #[cfg(skip)] |
164 | | functions: [ |
165 | | new, |
166 | | try_new_with_any_provider, |
167 | | try_new_with_buffer_provider, |
168 | | try_new_unstable, |
169 | | Self, |
170 | | ]); |
171 | | |
172 | | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)] |
173 | 0 | pub fn try_new_unstable<D: DataProvider<ChineseCacheV1Marker> + ?Sized>( |
174 | 0 | provider: &D, |
175 | 0 | ) -> Result<Self, CalendarError> { |
176 | 0 | Ok(Self { |
177 | 0 | data: Some(provider.load(Default::default())?.take_payload()?), |
178 | | }) |
179 | 0 | } Unexecuted instantiation: <icu_calendar::chinese::Chinese>::try_new_unstable::<icu_provider::any::DowncastingAnyProvider<icu_provider_adapters::empty::EmptyDataProvider>> Unexecuted instantiation: <icu_calendar::chinese::Chinese>::try_new_unstable::<_> |
180 | | |
181 | | /// Construct a new [`Chinese`] without any precomputed calendrical calculations. |
182 | 0 | pub fn new_always_calculating() -> Self { |
183 | 0 | Chinese { data: None } |
184 | 0 | } |
185 | | |
186 | | pub(crate) const DEBUG_NAME: &'static str = "Chinese"; |
187 | | } |
188 | | |
189 | | impl Calendar for Chinese { |
190 | | type DateInner = ChineseDateInner; |
191 | | |
192 | | // Construct a date from era/month codes and fields |
193 | 0 | fn date_from_codes( |
194 | 0 | &self, |
195 | 0 | era: types::Era, |
196 | 0 | year: i32, |
197 | 0 | month_code: types::MonthCode, |
198 | 0 | day: u8, |
199 | 0 | ) -> Result<Self::DateInner, CalendarError> { |
200 | 0 | let year_info = self.get_precomputed_data().load_or_compute_info(year); |
201 | | |
202 | 0 | let month = if let Some(ordinal) = |
203 | 0 | chinese_based_ordinal_lunar_month_from_code(month_code, year_info) |
204 | | { |
205 | 0 | ordinal |
206 | | } else { |
207 | 0 | return Err(CalendarError::UnknownMonthCode( |
208 | 0 | month_code.0, |
209 | 0 | self.debug_name(), |
210 | 0 | )); |
211 | | }; |
212 | | |
213 | 0 | if era.0 != tinystr!(16, "chinese") { |
214 | 0 | return Err(CalendarError::UnknownEra(era.0, self.debug_name())); |
215 | 0 | } |
216 | 0 |
|
217 | 0 | let arithmetic = Inner::new_from_ordinals(year, month, day, year_info); |
218 | 0 | Ok(ChineseDateInner(ChineseBasedDateInner(arithmetic?))) |
219 | 0 | } |
220 | | |
221 | | // Construct the date from an ISO date |
222 | 0 | fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner { |
223 | 0 | let fixed = Iso::fixed_from_iso(iso.inner); |
224 | 0 | ChineseDateInner(Inner::chinese_based_date_from_fixed( |
225 | 0 | self, |
226 | 0 | fixed, |
227 | 0 | iso.inner.0, |
228 | 0 | )) |
229 | 0 | } |
230 | | |
231 | | // Obtain an ISO date from a Chinese date |
232 | 0 | fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> { |
233 | 0 | let fixed = Inner::fixed_from_chinese_based_date_inner(date.0); |
234 | 0 | Iso::iso_from_fixed(fixed) |
235 | 0 | } |
236 | | |
237 | | //Count the number of months in a given year, specified by providing a date |
238 | | // from that year |
239 | 0 | fn days_in_year(&self, date: &Self::DateInner) -> u16 { |
240 | 0 | date.0.days_in_year_inner() |
241 | 0 | } |
242 | | |
243 | 0 | fn days_in_month(&self, date: &Self::DateInner) -> u8 { |
244 | 0 | date.0.days_in_month_inner() |
245 | 0 | } |
246 | | |
247 | | #[doc(hidden)] // unstable |
248 | 0 | fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) { |
249 | 0 | date.0 .0.offset_date(offset, &self.get_precomputed_data()); |
250 | 0 | } |
251 | | |
252 | | #[doc(hidden)] // unstable |
253 | | #[allow(clippy::field_reassign_with_default)] |
254 | | /// Calculate `date2 - date` as a duration |
255 | | /// |
256 | | /// `calendar2` is the calendar object associated with `date2`. In case the specific calendar objects |
257 | | /// differ on date, the date for the first calendar is used, and `date2` may be converted if necessary. |
258 | 0 | fn until( |
259 | 0 | &self, |
260 | 0 | date1: &Self::DateInner, |
261 | 0 | date2: &Self::DateInner, |
262 | 0 | _calendar2: &Self, |
263 | 0 | _largest_unit: DateDurationUnit, |
264 | 0 | _smallest_unit: DateDurationUnit, |
265 | 0 | ) -> DateDuration<Self> { |
266 | 0 | date1.0 .0.until(date2.0 .0, _largest_unit, _smallest_unit) |
267 | 0 | } |
268 | | |
269 | | /// Obtain a name for the calendar for debug printing |
270 | 0 | fn debug_name(&self) -> &'static str { |
271 | 0 | Self::DEBUG_NAME |
272 | 0 | } |
273 | | |
274 | | /// The calendar-specific year represented by `date` |
275 | 0 | fn year(&self, date: &Self::DateInner) -> types::FormattableYear { |
276 | 0 | Self::format_chinese_year(date.0 .0.year, Some(date.0 .0.year_info)) |
277 | 0 | } |
278 | | |
279 | 0 | fn is_in_leap_year(&self, date: &Self::DateInner) -> bool { |
280 | 0 | Self::is_leap_year(date.0 .0.year, date.0 .0.year_info) |
281 | 0 | } |
282 | | |
283 | | /// The calendar-specific month code represented by `date`; |
284 | | /// since the Chinese calendar has leap months, an "L" is appended to the month code for |
285 | | /// leap months. For example, in a year where an intercalary month is added after the second |
286 | | /// month, the month codes for ordinal months 1, 2, 3, 4, 5 would be "M01", "M02", "M02L", "M03", "M04". |
287 | 0 | fn month(&self, date: &Self::DateInner) -> types::FormattableMonth { |
288 | 0 | date.0.month() |
289 | 0 | } |
290 | | |
291 | | /// The calendar-specific day-of-month represented by `date` |
292 | 0 | fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth { |
293 | 0 | types::DayOfMonth(date.0 .0.day as u32) |
294 | 0 | } |
295 | | |
296 | | /// Information of the day of the year |
297 | 0 | fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo { |
298 | 0 | let prev_year = date.0 .0.year.saturating_sub(1); |
299 | 0 | let next_year = date.0 .0.year.saturating_add(1); |
300 | 0 | types::DayOfYearInfo { |
301 | 0 | day_of_year: date.0.day_of_year(), |
302 | 0 | days_in_year: date.0.days_in_year_inner(), |
303 | 0 | prev_year: Self::format_chinese_year(prev_year, None), |
304 | 0 | days_in_prev_year: date.0.days_in_prev_year(), |
305 | 0 | next_year: Self::format_chinese_year(next_year, None), |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | /// The [`AnyCalendarKind`] corresponding to this calendar |
310 | 0 | fn any_calendar_kind(&self) -> Option<AnyCalendarKind> { |
311 | 0 | Some(AnyCalendarKind::Chinese) |
312 | 0 | } |
313 | | |
314 | 0 | fn months_in_year(&self, date: &Self::DateInner) -> u8 { |
315 | 0 | date.0.months_in_year_inner() |
316 | 0 | } |
317 | | } |
318 | | |
319 | | impl<A: AsCalendar<Calendar = Chinese>> Date<A> { |
320 | | /// Construct a new Chinese date from a `year`, `month`, and `day`. |
321 | | /// `year` represents the Chinese year counted infinitely with -2636 (2637 BCE) as Chinese year 1; |
322 | | /// `month` represents the month of the year ordinally (ex. if it is a leap year, the last month will be 13, not 12); |
323 | | /// `day` indicates the day of month |
324 | | /// |
325 | | /// This date will not use any precomputed calendrical calculations, |
326 | | /// one that loads such data from a provider will be added in the future (#3933) |
327 | | /// |
328 | | /// ```rust |
329 | | /// use icu::calendar::{chinese::Chinese, Date}; |
330 | | /// |
331 | | /// let chinese = Chinese::new_always_calculating(); |
332 | | /// |
333 | | /// let date_chinese = |
334 | | /// Date::try_new_chinese_date_with_calendar(4660, 6, 11, chinese) |
335 | | /// .expect("Failed to initialize Chinese Date instance."); |
336 | | /// |
337 | | /// assert_eq!(date_chinese.year().number, 4660); |
338 | | /// assert_eq!(date_chinese.year().cyclic.unwrap().get(), 40); |
339 | | /// assert_eq!(date_chinese.year().related_iso, Some(2023)); |
340 | | /// assert_eq!(date_chinese.month().ordinal, 6); |
341 | | /// assert_eq!(date_chinese.day_of_month().0, 11); |
342 | | /// ``` |
343 | 0 | pub fn try_new_chinese_date_with_calendar( |
344 | 0 | year: i32, |
345 | 0 | month: u8, |
346 | 0 | day: u8, |
347 | 0 | calendar: A, |
348 | 0 | ) -> Result<Date<A>, CalendarError> { |
349 | 0 | let year_info = calendar |
350 | 0 | .as_calendar() |
351 | 0 | .get_precomputed_data() |
352 | 0 | .load_or_compute_info(year); |
353 | 0 | let arithmetic = Inner::new_from_ordinals(year, month, day, year_info); |
354 | 0 | Ok(Date::from_raw( |
355 | 0 | ChineseDateInner(ChineseBasedDateInner(arithmetic?)), |
356 | 0 | calendar, |
357 | | )) |
358 | 0 | } |
359 | | } |
360 | | |
361 | | impl<A: AsCalendar<Calendar = Chinese>> DateTime<A> { |
362 | | /// Construct a new Chinese datetime from integers using the |
363 | | /// -2636-based year system |
364 | | /// |
365 | | /// This datetime will not use any precomputed calendrical calculations, |
366 | | /// one that loads such data from a provider will be added in the future (#3933) |
367 | | /// |
368 | | /// ```rust |
369 | | /// use icu::calendar::{chinese::Chinese, DateTime}; |
370 | | /// |
371 | | /// let chinese = Chinese::new_always_calculating(); |
372 | | /// |
373 | | /// let chinese_datetime = DateTime::try_new_chinese_datetime_with_calendar( |
374 | | /// 4660, 6, 11, 13, 1, 0, chinese, |
375 | | /// ) |
376 | | /// .expect("Failed to initialize Chinese DateTime instance."); |
377 | | /// |
378 | | /// assert_eq!(chinese_datetime.date.year().number, 4660); |
379 | | /// assert_eq!(chinese_datetime.date.year().related_iso, Some(2023)); |
380 | | /// assert_eq!(chinese_datetime.date.year().cyclic.unwrap().get(), 40); |
381 | | /// assert_eq!(chinese_datetime.date.month().ordinal, 6); |
382 | | /// assert_eq!(chinese_datetime.date.day_of_month().0, 11); |
383 | | /// assert_eq!(chinese_datetime.time.hour.number(), 13); |
384 | | /// assert_eq!(chinese_datetime.time.minute.number(), 1); |
385 | | /// assert_eq!(chinese_datetime.time.second.number(), 0); |
386 | | /// ``` |
387 | 0 | pub fn try_new_chinese_datetime_with_calendar( |
388 | 0 | year: i32, |
389 | 0 | month: u8, |
390 | 0 | day: u8, |
391 | 0 | hour: u8, |
392 | 0 | minute: u8, |
393 | 0 | second: u8, |
394 | 0 | calendar: A, |
395 | 0 | ) -> Result<DateTime<A>, CalendarError> { |
396 | 0 | Ok(DateTime { |
397 | 0 | date: Date::try_new_chinese_date_with_calendar(year, month, day, calendar)?, |
398 | 0 | time: Time::try_new(hour, minute, second, 0)?, |
399 | | }) |
400 | 0 | } |
401 | | } |
402 | | |
403 | | type ChineseCB = calendrical_calculations::chinese_based::Chinese; |
404 | | impl ChineseBasedWithDataLoading for Chinese { |
405 | | type CB = ChineseCB; |
406 | 0 | fn get_precomputed_data(&self) -> ChineseBasedPrecomputedData<Self::CB> { |
407 | 0 | ChineseBasedPrecomputedData::new(self.data.as_ref().map(|d| d.get())) |
408 | 0 | } |
409 | | } |
410 | | |
411 | | impl Chinese { |
412 | | /// Get a FormattableYear from an integer Chinese year; optionally, a `ChineseBasedYearInfo` |
413 | | /// can be passed in for faster results. |
414 | | /// |
415 | | /// `era` is always `Era(tinystr!(16, "chinese"))` |
416 | | /// `number` is the year since the inception of the Chinese calendar (see [`Chinese`]) |
417 | | /// `cyclic` is an option with the current year in the sexagesimal cycle (see [`Chinese`]) |
418 | | /// `related_iso` is the ISO year in which the given Chinese year begins (see [`Chinese`]) |
419 | 0 | fn format_chinese_year( |
420 | 0 | year: i32, |
421 | 0 | year_info_option: Option<ChineseBasedYearInfo>, |
422 | 0 | ) -> FormattableYear { |
423 | 0 | let era = Era(tinystr!(16, "chinese")); |
424 | 0 | let number = year; |
425 | 0 | let cyclic = (number - 1).rem_euclid(60) as u8; |
426 | 0 | let cyclic = NonZeroU8::new(cyclic + 1); // 1-indexed |
427 | 0 | let rata_die_in_year = if let Some(info) = year_info_option { |
428 | 0 | info.new_year::<ChineseCB>(year) |
429 | | } else { |
430 | 0 | Inner::fixed_mid_year_from_year(number) |
431 | | }; |
432 | 0 | let iso_formattable_year = Iso::iso_from_fixed(rata_die_in_year).year(); |
433 | 0 | let related_iso = Some(iso_formattable_year.number); |
434 | 0 | types::FormattableYear { |
435 | 0 | era, |
436 | 0 | number, |
437 | 0 | cyclic, |
438 | 0 | related_iso, |
439 | 0 | } |
440 | 0 | } |
441 | | } |
442 | | |
443 | | #[cfg(test)] |
444 | | mod test { |
445 | | |
446 | | use super::*; |
447 | | use crate::types::MonthCode; |
448 | | use calendrical_calculations::{iso::fixed_from_iso, rata_die::RataDie}; |
449 | | /// Run a test twice, with two calendars |
450 | | fn do_twice( |
451 | | chinese_calculating: &Chinese, |
452 | | chinese_cached: &Chinese, |
453 | | test: impl Fn(crate::Ref<Chinese>, &'static str), |
454 | | ) { |
455 | | test(crate::Ref(chinese_calculating), "calculating"); |
456 | | test(crate::Ref(chinese_cached), "cached"); |
457 | | } |
458 | | |
459 | | #[test] |
460 | | fn test_chinese_from_fixed() { |
461 | | #[derive(Debug)] |
462 | | struct TestCase { |
463 | | fixed: i64, |
464 | | expected_year: i32, |
465 | | expected_month: u8, |
466 | | expected_day: u8, |
467 | | } |
468 | | |
469 | | let cases = [ |
470 | | TestCase { |
471 | | fixed: -964192, |
472 | | expected_year: -2, |
473 | | expected_month: 1, |
474 | | expected_day: 1, |
475 | | }, |
476 | | TestCase { |
477 | | fixed: -963838, |
478 | | expected_year: -1, |
479 | | expected_month: 1, |
480 | | expected_day: 1, |
481 | | }, |
482 | | TestCase { |
483 | | fixed: -963129, |
484 | | expected_year: 0, |
485 | | expected_month: 13, |
486 | | expected_day: 1, |
487 | | }, |
488 | | TestCase { |
489 | | fixed: -963100, |
490 | | expected_year: 0, |
491 | | expected_month: 13, |
492 | | expected_day: 30, |
493 | | }, |
494 | | TestCase { |
495 | | fixed: -963099, |
496 | | expected_year: 1, |
497 | | expected_month: 1, |
498 | | expected_day: 1, |
499 | | }, |
500 | | TestCase { |
501 | | fixed: 738700, |
502 | | expected_year: 4660, |
503 | | expected_month: 6, |
504 | | expected_day: 12, |
505 | | }, |
506 | | TestCase { |
507 | | fixed: fixed_from_iso(2319, 2, 20).to_i64_date(), |
508 | | expected_year: 2319 + 2636, |
509 | | expected_month: 13, |
510 | | expected_day: 30, |
511 | | }, |
512 | | TestCase { |
513 | | fixed: fixed_from_iso(2319, 2, 21).to_i64_date(), |
514 | | expected_year: 2319 + 2636 + 1, |
515 | | expected_month: 1, |
516 | | expected_day: 1, |
517 | | }, |
518 | | TestCase { |
519 | | fixed: 738718, |
520 | | expected_year: 4660, |
521 | | expected_month: 6, |
522 | | expected_day: 30, |
523 | | }, |
524 | | TestCase { |
525 | | fixed: 738747, |
526 | | expected_year: 4660, |
527 | | expected_month: 7, |
528 | | expected_day: 29, |
529 | | }, |
530 | | TestCase { |
531 | | fixed: 738748, |
532 | | expected_year: 4660, |
533 | | expected_month: 8, |
534 | | expected_day: 1, |
535 | | }, |
536 | | TestCase { |
537 | | fixed: 738865, |
538 | | expected_year: 4660, |
539 | | expected_month: 11, |
540 | | expected_day: 29, |
541 | | }, |
542 | | TestCase { |
543 | | fixed: 738895, |
544 | | expected_year: 4660, |
545 | | expected_month: 12, |
546 | | expected_day: 29, |
547 | | }, |
548 | | TestCase { |
549 | | fixed: 738925, |
550 | | expected_year: 4660, |
551 | | expected_month: 13, |
552 | | expected_day: 30, |
553 | | }, |
554 | | ]; |
555 | | |
556 | | let chinese_calculating = Chinese::new_always_calculating(); |
557 | | let chinese_cached = Chinese::new(); |
558 | | for case in cases { |
559 | | let rata_die = RataDie::new(case.fixed); |
560 | | let iso = Iso::iso_from_fixed(rata_die); |
561 | | |
562 | | do_twice( |
563 | | &chinese_calculating, |
564 | | &chinese_cached, |
565 | | |chinese, calendar_type| { |
566 | | let chinese = |
567 | | Inner::chinese_based_date_from_fixed(chinese.0, rata_die, iso.inner.0); |
568 | | assert_eq!( |
569 | | case.expected_year, chinese.0.year, |
570 | | "[{calendar_type}] Chinese from fixed failed, case: {case:?}" |
571 | | ); |
572 | | assert_eq!( |
573 | | case.expected_month, chinese.0.month, |
574 | | "[{calendar_type}] Chinese from fixed failed, case: {case:?}" |
575 | | ); |
576 | | assert_eq!( |
577 | | case.expected_day, chinese.0.day, |
578 | | "[{calendar_type}] Chinese from fixed failed, case: {case:?}" |
579 | | ); |
580 | | }, |
581 | | ); |
582 | | } |
583 | | } |
584 | | |
585 | | #[test] |
586 | | fn test_fixed_from_chinese() { |
587 | | #[derive(Debug)] |
588 | | struct TestCase { |
589 | | year: i32, |
590 | | month: u8, |
591 | | day: u8, |
592 | | expected: i64, |
593 | | } |
594 | | |
595 | | let cases = [ |
596 | | TestCase { |
597 | | year: 4660, |
598 | | month: 6, |
599 | | day: 6, |
600 | | // June 23 2023 |
601 | | expected: 738694, |
602 | | }, |
603 | | TestCase { |
604 | | year: 1, |
605 | | month: 1, |
606 | | day: 1, |
607 | | expected: -963099, |
608 | | }, |
609 | | ]; |
610 | | |
611 | | let chinese_calculating = Chinese::new_always_calculating(); |
612 | | let chinese_cached = Chinese::new(); |
613 | | for case in cases { |
614 | | do_twice( |
615 | | &chinese_calculating, |
616 | | &chinese_cached, |
617 | | |chinese, calendar_type| { |
618 | | let date = Date::try_new_chinese_date_with_calendar( |
619 | | case.year, case.month, case.day, chinese, |
620 | | ) |
621 | | .unwrap(); |
622 | | let fixed = |
623 | | Inner::fixed_from_chinese_based_date_inner(date.inner.0).to_i64_date(); |
624 | | let expected = case.expected; |
625 | | assert_eq!(fixed, expected, "[{calendar_type}] Fixed from Chinese failed, with expected: {fixed} and calculated: {expected}, for test case: {case:?}"); |
626 | | }, |
627 | | ); |
628 | | } |
629 | | } |
630 | | |
631 | | #[test] |
632 | | fn test_fixed_chinese_roundtrip() { |
633 | | let mut fixed = -1963020; |
634 | | let max_fixed = 1963020; |
635 | | let mut iters = 0; |
636 | | let max_iters = 560; |
637 | | let chinese_calculating = Chinese::new_always_calculating(); |
638 | | let chinese_cached = Chinese::new(); |
639 | | while fixed < max_fixed && iters < max_iters { |
640 | | let rata_die = RataDie::new(fixed); |
641 | | let iso = Iso::iso_from_fixed(rata_die); |
642 | | |
643 | | do_twice( |
644 | | &chinese_calculating, |
645 | | &chinese_cached, |
646 | | |chinese, calendar_type| { |
647 | | let chinese = |
648 | | Inner::chinese_based_date_from_fixed(&chinese, rata_die, iso.inner.0); |
649 | | let result = Inner::fixed_from_chinese_based_date_inner(chinese); |
650 | | let result_debug = result.to_i64_date(); |
651 | | assert_eq!(result, rata_die, "[{calendar_type}] Failed roundtrip fixed -> Chinese -> fixed for fixed: {fixed}, with calculated: {result_debug} from Chinese date:\n{chinese:?}"); |
652 | | }, |
653 | | ); |
654 | | fixed += 7043; |
655 | | iters += 1; |
656 | | } |
657 | | } |
658 | | |
659 | | #[test] |
660 | | fn test_chinese_epoch() { |
661 | | let iso = Date::try_new_iso_date(-2636, 2, 15).unwrap(); |
662 | | |
663 | | do_twice( |
664 | | &Chinese::new_always_calculating(), |
665 | | &Chinese::new(), |
666 | | |chinese, _calendar_type| { |
667 | | let chinese = iso.to_calendar(chinese); |
668 | | |
669 | | assert_eq!(chinese.year().number, 1); |
670 | | assert_eq!(chinese.month().ordinal, 1); |
671 | | assert_eq!(chinese.month().code.0, "M01"); |
672 | | assert_eq!(chinese.day_of_month().0, 1); |
673 | | assert_eq!(chinese.year().cyclic.unwrap().get(), 1); |
674 | | assert_eq!(chinese.year().related_iso, Some(-2636)); |
675 | | }, |
676 | | ) |
677 | | } |
678 | | |
679 | | #[test] |
680 | | fn test_iso_to_chinese_negative_years() { |
681 | | #[derive(Debug)] |
682 | | struct TestCase { |
683 | | iso_year: i32, |
684 | | iso_month: u8, |
685 | | iso_day: u8, |
686 | | expected_year: i32, |
687 | | expected_month: u32, |
688 | | expected_day: u32, |
689 | | } |
690 | | |
691 | | let cases = [ |
692 | | TestCase { |
693 | | iso_year: -2636, |
694 | | iso_month: 2, |
695 | | iso_day: 14, |
696 | | expected_year: 0, |
697 | | expected_month: 13, |
698 | | expected_day: 30, |
699 | | }, |
700 | | TestCase { |
701 | | iso_year: -2636, |
702 | | iso_month: 1, |
703 | | iso_day: 15, |
704 | | expected_year: 0, |
705 | | expected_month: 12, |
706 | | expected_day: 30, |
707 | | }, |
708 | | ]; |
709 | | |
710 | | let chinese_calculating = Chinese::new_always_calculating(); |
711 | | let chinese_cached = Chinese::new(); |
712 | | |
713 | | for case in cases { |
714 | | let iso = Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day).unwrap(); |
715 | | do_twice( |
716 | | &chinese_calculating, |
717 | | &chinese_cached, |
718 | | |chinese, calendar_type| { |
719 | | let chinese = iso.to_calendar(chinese); |
720 | | assert_eq!( |
721 | | case.expected_year, |
722 | | chinese.year().number, |
723 | | "[{calendar_type}] ISO to Chinese failed for case: {case:?}" |
724 | | ); |
725 | | assert_eq!( |
726 | | case.expected_month, |
727 | | chinese.month().ordinal, |
728 | | "[{calendar_type}] ISO to Chinese failed for case: {case:?}" |
729 | | ); |
730 | | assert_eq!( |
731 | | case.expected_day, |
732 | | chinese.day_of_month().0, |
733 | | "[{calendar_type}] ISO to Chinese failed for case: {case:?}" |
734 | | ); |
735 | | }, |
736 | | ); |
737 | | } |
738 | | } |
739 | | |
740 | | #[test] |
741 | | fn test_chinese_leap_months() { |
742 | | let expected = [ |
743 | | (1933, 6), |
744 | | (1938, 8), |
745 | | (1984, 11), |
746 | | (2009, 6), |
747 | | (2017, 7), |
748 | | (2028, 6), |
749 | | ]; |
750 | | let chinese_calculating = Chinese::new_always_calculating(); |
751 | | let chinese_cached = Chinese::new(); |
752 | | |
753 | | for case in expected { |
754 | | let year = case.0; |
755 | | let expected_month = case.1; |
756 | | let iso = Date::try_new_iso_date(year, 6, 1).unwrap(); |
757 | | do_twice( |
758 | | &chinese_calculating, |
759 | | &chinese_cached, |
760 | | |chinese, calendar_type| { |
761 | | let chinese_date = iso.to_calendar(chinese); |
762 | | assert!( |
763 | | chinese_date.is_in_leap_year(), |
764 | | "[{calendar_type}] {year} should be a leap year" |
765 | | ); |
766 | | let new_year = chinese_date.inner.0.new_year(); |
767 | | assert_eq!( |
768 | | expected_month, |
769 | | calendrical_calculations::chinese_based::get_leap_month_from_new_year::< |
770 | | calendrical_calculations::chinese_based::Chinese, |
771 | | >(new_year), |
772 | | "[{calendar_type}] {year} have leap month {expected_month}" |
773 | | ); |
774 | | }, |
775 | | ); |
776 | | } |
777 | | } |
778 | | |
779 | | #[test] |
780 | | fn test_month_days() { |
781 | | let year = 4660; |
782 | | let year_info = |
783 | | ChineseBasedPrecomputedData::<<Chinese as ChineseBasedWithDataLoading>::CB>::default() |
784 | | .load_or_compute_info(year); |
785 | | let cases = [ |
786 | | (1, 29), |
787 | | (2, 30), |
788 | | (3, 29), |
789 | | (4, 29), |
790 | | (5, 30), |
791 | | (6, 30), |
792 | | (7, 29), |
793 | | (8, 30), |
794 | | (9, 30), |
795 | | (10, 29), |
796 | | (11, 30), |
797 | | (12, 29), |
798 | | (13, 30), |
799 | | ]; |
800 | | for case in cases { |
801 | | let days_in_month = Chinese::month_days(year, case.0, year_info); |
802 | | assert_eq!( |
803 | | case.1, days_in_month, |
804 | | "month_days test failed for case: {case:?}" |
805 | | ); |
806 | | } |
807 | | } |
808 | | |
809 | | #[test] |
810 | | fn test_ordinal_to_month_code() { |
811 | | #[derive(Debug)] |
812 | | struct TestCase { |
813 | | year: i32, |
814 | | month: u8, |
815 | | day: u8, |
816 | | expected_code: &'static str, |
817 | | } |
818 | | |
819 | | let cases = [ |
820 | | TestCase { |
821 | | year: 2023, |
822 | | month: 1, |
823 | | day: 9, |
824 | | expected_code: "M12", |
825 | | }, |
826 | | TestCase { |
827 | | year: 2023, |
828 | | month: 2, |
829 | | day: 9, |
830 | | expected_code: "M01", |
831 | | }, |
832 | | TestCase { |
833 | | year: 2023, |
834 | | month: 3, |
835 | | day: 9, |
836 | | expected_code: "M02", |
837 | | }, |
838 | | TestCase { |
839 | | year: 2023, |
840 | | month: 4, |
841 | | day: 9, |
842 | | expected_code: "M02L", |
843 | | }, |
844 | | TestCase { |
845 | | year: 2023, |
846 | | month: 5, |
847 | | day: 9, |
848 | | expected_code: "M03", |
849 | | }, |
850 | | TestCase { |
851 | | year: 2023, |
852 | | month: 6, |
853 | | day: 9, |
854 | | expected_code: "M04", |
855 | | }, |
856 | | TestCase { |
857 | | year: 2023, |
858 | | month: 7, |
859 | | day: 9, |
860 | | expected_code: "M05", |
861 | | }, |
862 | | TestCase { |
863 | | year: 2023, |
864 | | month: 8, |
865 | | day: 9, |
866 | | expected_code: "M06", |
867 | | }, |
868 | | TestCase { |
869 | | year: 2023, |
870 | | month: 9, |
871 | | day: 9, |
872 | | expected_code: "M07", |
873 | | }, |
874 | | TestCase { |
875 | | year: 2023, |
876 | | month: 10, |
877 | | day: 9, |
878 | | expected_code: "M08", |
879 | | }, |
880 | | TestCase { |
881 | | year: 2023, |
882 | | month: 11, |
883 | | day: 9, |
884 | | expected_code: "M09", |
885 | | }, |
886 | | TestCase { |
887 | | year: 2023, |
888 | | month: 12, |
889 | | day: 9, |
890 | | expected_code: "M10", |
891 | | }, |
892 | | TestCase { |
893 | | year: 2024, |
894 | | month: 1, |
895 | | day: 9, |
896 | | expected_code: "M11", |
897 | | }, |
898 | | TestCase { |
899 | | year: 2024, |
900 | | month: 2, |
901 | | day: 9, |
902 | | expected_code: "M12", |
903 | | }, |
904 | | TestCase { |
905 | | year: 2024, |
906 | | month: 2, |
907 | | day: 10, |
908 | | expected_code: "M01", |
909 | | }, |
910 | | ]; |
911 | | |
912 | | let chinese_calculating = Chinese::new_always_calculating(); |
913 | | let chinese_cached = Chinese::new(); |
914 | | |
915 | | for case in cases { |
916 | | let iso = Date::try_new_iso_date(case.year, case.month, case.day).unwrap(); |
917 | | do_twice( |
918 | | &chinese_calculating, |
919 | | &chinese_cached, |
920 | | |chinese, calendar_type| { |
921 | | let chinese = iso.to_calendar(chinese); |
922 | | let result_code = chinese.month().code.0; |
923 | | let expected_code = case.expected_code.to_string(); |
924 | | assert_eq!( |
925 | | expected_code, result_code, |
926 | | "[{calendar_type}] Month codes did not match for test case: {case:?}" |
927 | | ); |
928 | | }, |
929 | | ); |
930 | | } |
931 | | } |
932 | | |
933 | | #[test] |
934 | | fn test_month_code_to_ordinal() { |
935 | | let year = 4660; |
936 | | // construct using ::default() to force recomputation |
937 | | let year_info = |
938 | | ChineseBasedPrecomputedData::<<Chinese as ChineseBasedWithDataLoading>::CB>::default() |
939 | | .load_or_compute_info(year); |
940 | | let codes = [ |
941 | | (1, tinystr!(4, "M01")), |
942 | | (2, tinystr!(4, "M02")), |
943 | | (3, tinystr!(4, "M02L")), |
944 | | (4, tinystr!(4, "M03")), |
945 | | (5, tinystr!(4, "M04")), |
946 | | (6, tinystr!(4, "M05")), |
947 | | (7, tinystr!(4, "M06")), |
948 | | (8, tinystr!(4, "M07")), |
949 | | (9, tinystr!(4, "M08")), |
950 | | (10, tinystr!(4, "M09")), |
951 | | (11, tinystr!(4, "M10")), |
952 | | (12, tinystr!(4, "M11")), |
953 | | (13, tinystr!(4, "M12")), |
954 | | ]; |
955 | | for ordinal_code_pair in codes { |
956 | | let code = MonthCode(ordinal_code_pair.1); |
957 | | let ordinal = chinese_based_ordinal_lunar_month_from_code(code, year_info); |
958 | | assert_eq!( |
959 | | ordinal, |
960 | | Some(ordinal_code_pair.0), |
961 | | "Code to ordinal failed for year: {year}, code: {code}" |
962 | | ); |
963 | | } |
964 | | } |
965 | | |
966 | | #[test] |
967 | | fn check_invalid_month_code_to_ordinal() { |
968 | | let non_leap_year = 4659; |
969 | | let leap_year = 4660; |
970 | | let invalid_codes = [ |
971 | | (non_leap_year, tinystr!(4, "M2")), |
972 | | (leap_year, tinystr!(4, "M0")), |
973 | | (non_leap_year, tinystr!(4, "J01")), |
974 | | (leap_year, tinystr!(4, "3M")), |
975 | | (non_leap_year, tinystr!(4, "M04L")), |
976 | | (leap_year, tinystr!(4, "M04L")), |
977 | | (non_leap_year, tinystr!(4, "M13")), |
978 | | (leap_year, tinystr!(4, "M13")), |
979 | | ]; |
980 | | for year_code_pair in invalid_codes { |
981 | | let year = year_code_pair.0; |
982 | | // construct using ::default() to force recomputation |
983 | | let year_info = ChineseBasedPrecomputedData::< |
984 | | <Chinese as ChineseBasedWithDataLoading>::CB, |
985 | | >::default() |
986 | | .load_or_compute_info(year); |
987 | | let code = MonthCode(year_code_pair.1); |
988 | | let ordinal = chinese_based_ordinal_lunar_month_from_code(code, year_info); |
989 | | assert_eq!( |
990 | | ordinal, None, |
991 | | "Invalid month code failed for year: {year}, code: {code}" |
992 | | ); |
993 | | } |
994 | | } |
995 | | |
996 | | #[test] |
997 | | fn test_iso_chinese_roundtrip() { |
998 | | let chinese_calculating = Chinese::new_always_calculating(); |
999 | | let chinese_cached = Chinese::new(); |
1000 | | |
1001 | | for i in -1000..=1000 { |
1002 | | let year = i; |
1003 | | let month = i as u8 % 12 + 1; |
1004 | | let day = i as u8 % 28 + 1; |
1005 | | let iso = Date::try_new_iso_date(year, month, day).unwrap(); |
1006 | | do_twice( |
1007 | | &chinese_calculating, |
1008 | | &chinese_cached, |
1009 | | |chinese, calendar_type| { |
1010 | | let chinese = iso.to_calendar(chinese); |
1011 | | let result = chinese.to_calendar(Iso); |
1012 | | assert_eq!(iso, result, "[{calendar_type}] ISO to Chinese roundtrip failed!\nIso: {iso:?}\nChinese: {chinese:?}\nResult: {result:?}"); |
1013 | | }, |
1014 | | ); |
1015 | | } |
1016 | | } |
1017 | | |
1018 | | #[test] |
1019 | | fn test_consistent_with_icu() { |
1020 | | #[derive(Debug)] |
1021 | | struct TestCase { |
1022 | | iso_year: i32, |
1023 | | iso_month: u8, |
1024 | | iso_day: u8, |
1025 | | expected_rel_iso: i32, |
1026 | | expected_cyclic: u8, |
1027 | | expected_month: u32, |
1028 | | expected_day: u32, |
1029 | | } |
1030 | | |
1031 | | let cases = [ |
1032 | | TestCase { |
1033 | | iso_year: -2332, |
1034 | | iso_month: 3, |
1035 | | iso_day: 1, |
1036 | | expected_rel_iso: -2332, |
1037 | | expected_cyclic: 5, |
1038 | | expected_month: 1, |
1039 | | expected_day: 16, |
1040 | | }, |
1041 | | TestCase { |
1042 | | iso_year: -2332, |
1043 | | iso_month: 2, |
1044 | | iso_day: 15, |
1045 | | expected_rel_iso: -2332, |
1046 | | expected_cyclic: 5, |
1047 | | expected_month: 1, |
1048 | | expected_day: 1, |
1049 | | }, |
1050 | | TestCase { |
1051 | | // This test case fails to match ICU |
1052 | | iso_year: -2332, |
1053 | | iso_month: 2, |
1054 | | iso_day: 14, |
1055 | | expected_rel_iso: -2333, |
1056 | | expected_cyclic: 4, |
1057 | | expected_month: 13, |
1058 | | expected_day: 30, |
1059 | | }, |
1060 | | TestCase { |
1061 | | // This test case fails to match ICU |
1062 | | iso_year: -2332, |
1063 | | iso_month: 1, |
1064 | | iso_day: 17, |
1065 | | expected_rel_iso: -2333, |
1066 | | expected_cyclic: 4, |
1067 | | expected_month: 13, |
1068 | | expected_day: 2, |
1069 | | }, |
1070 | | TestCase { |
1071 | | // This test case fails to match ICU |
1072 | | iso_year: -2332, |
1073 | | iso_month: 1, |
1074 | | iso_day: 16, |
1075 | | expected_rel_iso: -2333, |
1076 | | expected_cyclic: 4, |
1077 | | expected_month: 13, |
1078 | | expected_day: 1, |
1079 | | }, |
1080 | | TestCase { |
1081 | | iso_year: -2332, |
1082 | | iso_month: 1, |
1083 | | iso_day: 15, |
1084 | | expected_rel_iso: -2333, |
1085 | | expected_cyclic: 4, |
1086 | | expected_month: 12, |
1087 | | expected_day: 29, |
1088 | | }, |
1089 | | TestCase { |
1090 | | iso_year: -2332, |
1091 | | iso_month: 1, |
1092 | | iso_day: 1, |
1093 | | expected_rel_iso: -2333, |
1094 | | expected_cyclic: 4, |
1095 | | expected_month: 12, |
1096 | | expected_day: 15, |
1097 | | }, |
1098 | | TestCase { |
1099 | | iso_year: -2333, |
1100 | | iso_month: 1, |
1101 | | iso_day: 16, |
1102 | | expected_rel_iso: -2334, |
1103 | | expected_cyclic: 3, |
1104 | | expected_month: 12, |
1105 | | expected_day: 19, |
1106 | | }, |
1107 | | ]; |
1108 | | |
1109 | | let chinese_calculating = Chinese::new_always_calculating(); |
1110 | | let chinese_cached = Chinese::new(); |
1111 | | |
1112 | | for case in cases { |
1113 | | let iso = Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day).unwrap(); |
1114 | | |
1115 | | do_twice( |
1116 | | &chinese_calculating, |
1117 | | &chinese_cached, |
1118 | | |chinese, calendar_type| { |
1119 | | let chinese = iso.to_calendar(chinese); |
1120 | | let chinese_rel_iso = chinese.year().related_iso; |
1121 | | let chinese_cyclic = chinese.year().cyclic; |
1122 | | let chinese_month = chinese.month().ordinal; |
1123 | | let chinese_day = chinese.day_of_month().0; |
1124 | | |
1125 | | assert_eq!( |
1126 | | chinese_rel_iso, |
1127 | | Some(case.expected_rel_iso), |
1128 | | "[{calendar_type}] Related ISO failed for test case: {case:?}" |
1129 | | ); |
1130 | | assert_eq!( |
1131 | | chinese_cyclic.unwrap().get(), |
1132 | | case.expected_cyclic, |
1133 | | "[{calendar_type}] Cyclic year failed for test case: {case:?}" |
1134 | | ); |
1135 | | assert_eq!( |
1136 | | chinese_month, case.expected_month, |
1137 | | "[{calendar_type}] Month failed for test case: {case:?}" |
1138 | | ); |
1139 | | assert_eq!( |
1140 | | chinese_day, case.expected_day, |
1141 | | "[{calendar_type}] Day failed for test case: {case:?}" |
1142 | | ); |
1143 | | }, |
1144 | | ); |
1145 | | } |
1146 | | } |
1147 | | } |