calendrical_calculations/
hebrew.rs

1// This file is part of ICU4X.
2//
3// The contents of this file implement algorithms from Calendrical Calculations
4// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
5// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
6// under the Apache-2.0 license. Accordingly, this file is released under
7// the Apache License, Version 2.0 which can be found at the calendrical_calculations
8// package root or at http://www.apache.org/licenses/LICENSE-2.0.
9
10use crate::helpers::{final_func, i64_to_i32, next_u8};
11use crate::rata_die::{Moment, RataDie};
12#[allow(unused_imports)]
13use core_maths::*;
14
15/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2206>
16pub(crate) const FIXED_HEBREW_EPOCH: RataDie =
17    crate::julian::fixed_from_julian_book_version(-3761, 10, 7);
18
19/// Biblical Hebrew dates. The months are reckoned a bit strangely, with the new year occurring on
20/// Tishri (as in the civil calendar) but the months being numbered in a different order
21#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
22#[allow(clippy::exhaustive_structs)]
23pub struct BookHebrew {
24    /// The year
25    pub year: i32,
26    /// The month
27    pub month: u8,
28    /// The day
29    pub day: u8,
30}
31
32// The BookHebrew Months
33/// The biblical month number used for the month of Nisan
34pub const NISAN: u8 = 1;
35/// The biblical month number used for the month of Iyyar
36pub const IYYAR: u8 = 2;
37/// The biblical month number used for the month of Sivan
38pub const SIVAN: u8 = 3;
39/// The biblical month number used for the month of Tammuz
40pub const TAMMUZ: u8 = 4;
41/// The biblical month number used for the month of Av
42pub const AV: u8 = 5;
43/// The biblical month number used for the month of Elul
44pub const ELUL: u8 = 6;
45/// The biblical month number used for the month of Tishri
46pub const TISHRI: u8 = 7;
47/// The biblical month number used for the month of Marheshvan
48pub const MARHESHVAN: u8 = 8;
49/// The biblical month number used for the month of Kislev
50pub const KISLEV: u8 = 9;
51/// The biblical month number used for the month of Tevet
52pub const TEVET: u8 = 10;
53/// The biblical month number used for the month of Shevat
54pub const SHEVAT: u8 = 11;
55/// The biblical month number used for the month of Adar (and Adar I)
56pub const ADAR: u8 = 12;
57/// The biblical month number used for the month of Adar II
58pub const ADARII: u8 = 13;
59
60// BIBLICAL HEBREW CALENDAR FUNCTIONS
61
62impl BookHebrew {
63    /// The civil calendar has the same year and day numbering as the book one, but the months are numbered
64    /// differently
65    pub fn to_civil_date(self) -> (i32, u8, u8) {
66        let biblical_month = self.month;
67        let biblical_year = self.year;
68        let mut civil_month;
69        civil_month = (biblical_month + 6) % 12;
70
71        if civil_month == 0 {
72            civil_month = 12;
73        }
74
75        if Self::is_hebrew_leap_year(biblical_year) && biblical_month < TISHRI {
76            civil_month += 1;
77        }
78        (biblical_year, civil_month, self.day)
79    }
80
81    /// The civil calendar has the same year and day numbering as the book one, but the months are numbered
82    /// differently
83    pub fn from_civil_date(civil_year: i32, civil_month: u8, civil_day: u8) -> Self {
84        let mut biblical_month;
85
86        if civil_month <= 6 {
87            biblical_month = civil_month + 6; //  months 1-6 correspond to biblical months 7-12
88        } else {
89            biblical_month = civil_month - 6; //  months 7-12 correspond to biblical months 1-6
90            if Self::is_hebrew_leap_year(civil_year) {
91                biblical_month -= 1
92            }
93            if biblical_month == 0 {
94                // Special case for Adar II in a leap year
95                biblical_month = 13;
96            }
97        }
98
99        BookHebrew {
100            year: civil_year,
101            month: biblical_month,
102            day: civil_day,
103        }
104    }
105    // Moment of mean conjunction (New Moon) of h_month in BookHebrew
106    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2244>
107    #[allow(dead_code)]
108    pub(crate) fn molad(book_year: i32, book_month: u8) -> Moment {
109        let y = if book_month < TISHRI {
110            book_year + 1
111        } else {
112            book_year
113        }; // Treat Nisan as start of year
114
115        let months_elapsed = (book_month as f64 - TISHRI as f64) // Months this year
116            + ((235.0 * y as f64 - 234.0) / 19.0).floor(); // Months until New Year.
117
118        Moment::new(
119            FIXED_HEBREW_EPOCH.to_f64_date() - (876.0 / 25920.0)
120                + months_elapsed * (29.0 + (1.0 / 2.0) + (793.0 / 25920.0)),
121        )
122    }
123
124    // ADAR = 12, ADARII = 13
125    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2217>
126    #[allow(dead_code)]
127    fn last_month_of_book_hebrew_year(book_year: i32) -> u8 {
128        if Self::is_hebrew_leap_year(book_year) {
129            ADARII
130        } else {
131            ADAR
132        }
133    }
134
135    // Number of days elapsed from the (Sunday) noon prior to the epoch of the BookHebrew Calendar to the molad of Tishri of BookHebrew year (h_year) or one day later
136    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2261>
137    fn book_hebrew_calendar_elapsed_days(book_year: i32) -> i32 {
138        let months_elapsed = ((235.0 * book_year as f64 - 234.0) / 19.0).floor() as i64;
139        let parts_elapsed = 12084 + 13753 * months_elapsed;
140        let days = 29 * months_elapsed + (parts_elapsed as f64 / 25920.0).floor() as i64;
141
142        if (3 * (days + 1)).rem_euclid(7) < 3 {
143            days as i32 + 1
144        } else {
145            days as i32
146        }
147    }
148
149    // Delays to start of BookHebrew year to keep ordinary year in range 353-356 and leap year in range 383-386
150    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2301>
151    fn book_hebrew_year_length_correction(book_year: i32) -> u8 {
152        let ny0 = Self::book_hebrew_calendar_elapsed_days(book_year - 1);
153        let ny1 = Self::book_hebrew_calendar_elapsed_days(book_year);
154        let ny2 = Self::book_hebrew_calendar_elapsed_days(book_year + 1);
155
156        if (ny2 - ny1) == 356 {
157            2
158        } else if (ny1 - ny0) == 382 {
159            1
160        } else {
161            0
162        }
163    }
164
165    // Fixed date of BookHebrew new year
166    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2294>
167    pub fn book_hebrew_new_year(book_year: i32) -> RataDie {
168        RataDie::new(
169            FIXED_HEBREW_EPOCH.to_i64_date()
170                + Self::book_hebrew_calendar_elapsed_days(book_year) as i64
171                + Self::book_hebrew_year_length_correction(book_year) as i64,
172        )
173    }
174
175    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2315>
176    pub fn days_in_book_hebrew_year(book_year: i32) -> u16 {
177        (Self::book_hebrew_new_year(1 + book_year) - Self::book_hebrew_new_year(book_year)) as u16
178    }
179
180    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L2275-L2278>
181    pub fn is_hebrew_leap_year(book_year: i32) -> bool {
182        (7 * book_year + 1).rem_euclid(19) < 7
183    }
184
185    // True if the month Marheshvan is going to be long in given BookHebrew year
186    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2321>
187    #[allow(dead_code)]
188    fn is_long_marheshvan(book_year: i32) -> bool {
189        let long_marheshavan_year_lengths = [355, 385];
190        long_marheshavan_year_lengths.contains(&Self::days_in_book_hebrew_year(book_year))
191    }
192
193    // True if the month Kislve is going to be short in given BookHebrew year
194    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2326>
195    #[allow(dead_code)]
196    fn is_short_kislev(book_year: i32) -> bool {
197        let short_kislev_year_lengths = [353, 383];
198        short_kislev_year_lengths.contains(&Self::days_in_book_hebrew_year(book_year))
199    }
200
201    // Last day of month (h_month) in BookHebrew year (book_year)
202    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2230>
203    pub fn last_day_of_book_hebrew_month(book_year: i32, book_month: u8) -> u8 {
204        match book_month {
205            IYYAR | TAMMUZ | ELUL | TEVET | ADARII => 29,
206            ADAR => {
207                if !Self::is_hebrew_leap_year(book_year) {
208                    29
209                } else {
210                    30
211                }
212            }
213            MARHESHVAN => {
214                if !Self::is_long_marheshvan(book_year) {
215                    29
216                } else {
217                    30
218                }
219            }
220            KISLEV => {
221                if Self::is_short_kislev(book_year) {
222                    29
223                } else {
224                    30
225                }
226            }
227            _ => 30,
228        }
229    }
230
231    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2331>
232    pub fn fixed_from_book_hebrew(date: BookHebrew) -> RataDie {
233        let book_year = date.year;
234        let book_month = date.month;
235        let book_day = date.day;
236
237        let mut total_days = Self::book_hebrew_new_year(book_year) + book_day.into() - 1; // (day - 1) Days so far this month.
238
239        if book_month < TISHRI {
240            // Then add days in prior months this year before
241            for m in
242                (TISHRI..=Self::last_month_of_book_hebrew_year(book_year)).chain(NISAN..book_month)
243            {
244                total_days += Self::last_day_of_book_hebrew_month(book_year, m).into();
245            }
246        } else {
247            // Else add days in prior months this year
248            for m in TISHRI..book_month {
249                total_days += Self::last_day_of_book_hebrew_month(book_year, m).into();
250            }
251        }
252
253        total_days
254    }
255
256    /// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2352>
257    pub fn book_hebrew_from_fixed(date: RataDie) -> BookHebrew {
258        let approx = i64_to_i32(
259            1 + ((date - FIXED_HEBREW_EPOCH) as f64).div_euclid(35975351.0 / 98496.0) as i64, //  The value 35975351/98496, the average length of a BookHebrew year, can be approximated by 365.25
260        )
261        .unwrap_or_else(|e| e.saturate());
262
263        // Search forward for the year
264        let year_condition = |year: i32| Self::book_hebrew_new_year(year) <= date;
265        let year = final_func(approx - 1, year_condition);
266
267        // Starting month for search for month.
268        let start = if date
269            < Self::fixed_from_book_hebrew(BookHebrew {
270                year,
271                month: NISAN,
272                day: 1,
273            }) {
274            TISHRI
275        } else {
276            NISAN
277        };
278
279        let month_condition = |m: u8| {
280            date <= Self::fixed_from_book_hebrew(BookHebrew {
281                year,
282                month: m,
283                day: Self::last_day_of_book_hebrew_month(year, m),
284            })
285        };
286        // Search forward from either Tishri or Nisan.
287        let month = next_u8(start, month_condition);
288
289        // Calculate the day by subtraction.
290        let day = (date
291            - Self::fixed_from_book_hebrew(BookHebrew {
292                year,
293                month,
294                day: 1,
295            }))
296            + 1;
297
298        BookHebrew {
299            year,
300            month,
301            day: day as u8,
302        }
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    #[derive(Debug)]
311    struct DateCase {
312        year: i32,
313        month: u8,
314        day: u8,
315    }
316
317    static TEST_FIXED_DATE: [i64; 33] = [
318        -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
319        470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
320        664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
321    ];
322
323    static HEBREW_DATES: [DateCase; 33] = [
324        DateCase {
325            year: 3174,
326            month: 5,
327            day: 10,
328        },
329        DateCase {
330            year: 3593,
331            month: 9,
332            day: 25,
333        },
334        DateCase {
335            year: 3831,
336            month: 7,
337            day: 3,
338        },
339        DateCase {
340            year: 3896,
341            month: 7,
342            day: 9,
343        },
344        DateCase {
345            year: 4230,
346            month: 10,
347            day: 18,
348        },
349        DateCase {
350            year: 4336,
351            month: 3,
352            day: 4,
353        },
354        DateCase {
355            year: 4455,
356            month: 8,
357            day: 13,
358        },
359        DateCase {
360            year: 4773,
361            month: 2,
362            day: 6,
363        },
364        DateCase {
365            year: 4856,
366            month: 2,
367            day: 23,
368        },
369        DateCase {
370            year: 4950,
371            month: 1,
372            day: 7,
373        },
374        DateCase {
375            year: 5000,
376            month: 13,
377            day: 8,
378        },
379        DateCase {
380            year: 5048,
381            month: 1,
382            day: 21,
383        },
384        DateCase {
385            year: 5058,
386            month: 2,
387            day: 7,
388        },
389        DateCase {
390            year: 5151,
391            month: 4,
392            day: 1,
393        },
394        DateCase {
395            year: 5196,
396            month: 11,
397            day: 7,
398        },
399        DateCase {
400            year: 5252,
401            month: 1,
402            day: 3,
403        },
404        DateCase {
405            year: 5314,
406            month: 7,
407            day: 1,
408        },
409        DateCase {
410            year: 5320,
411            month: 12,
412            day: 27,
413        },
414        DateCase {
415            year: 5408,
416            month: 3,
417            day: 20,
418        },
419        DateCase {
420            year: 5440,
421            month: 4,
422            day: 3,
423        },
424        DateCase {
425            year: 5476,
426            month: 5,
427            day: 5,
428        },
429        DateCase {
430            year: 5528,
431            month: 4,
432            day: 4,
433        },
434        DateCase {
435            year: 5579,
436            month: 5,
437            day: 11,
438        },
439        DateCase {
440            year: 5599,
441            month: 1,
442            day: 12,
443        },
444        DateCase {
445            year: 5663,
446            month: 1,
447            day: 22,
448        },
449        DateCase {
450            year: 5689,
451            month: 5,
452            day: 19,
453        },
454        DateCase {
455            year: 5702,
456            month: 7,
457            day: 8,
458        },
459        DateCase {
460            year: 5703,
461            month: 1,
462            day: 14,
463        },
464        DateCase {
465            year: 5704,
466            month: 7,
467            day: 8,
468        },
469        DateCase {
470            year: 5752,
471            month: 13,
472            day: 12,
473        },
474        DateCase {
475            year: 5756,
476            month: 12,
477            day: 5,
478        },
479        DateCase {
480            year: 5799,
481            month: 8,
482            day: 12,
483        },
484        DateCase {
485            year: 5854,
486            month: 5,
487            day: 5,
488        },
489    ];
490
491    static EXPECTED_MOLAD_DATES: [f64; 33] = [
492        -1850718767f64 / 8640f64,
493        -1591805959f64 / 25920f64,
494        660097927f64 / 25920f64,
495        1275506059f64 / 25920f64,
496        4439806081f64 / 25920f64,
497        605235101f64 / 2880f64,
498        3284237627f64 / 12960f64,
499        9583515841f64 / 25920f64,
500        2592403883f64 / 6480f64,
501        2251656649f64 / 5184f64,
502        11731320839f64 / 25920f64,
503        12185988041f64 / 25920f64,
504        6140833583f64 / 12960f64,
505        6581722991f64 / 12960f64,
506        6792982499f64 / 12960f64,
507        4705980311f64 / 8640f64,
508        14699670013f64 / 25920f64,
509        738006961f64 / 1296f64,
510        1949499007f64 / 3240f64,
511        5299956319f64 / 8640f64,
512        3248250415f64 / 5184f64,
513        16732660061f64 / 25920f64,
514        17216413717f64 / 25920f64,
515        1087650871f64 / 1620f64,
516        2251079609f64 / 3240f64,
517        608605601f64 / 864f64,
518        306216383f64 / 432f64,
519        18387526207f64 / 25920f64,
520        3678423761f64 / 5184f64,
521        1570884431f64 / 2160f64,
522        18888119389f64 / 25920f64,
523        19292268013f64 / 25920f64,
524        660655045f64 / 864f64,
525    ];
526
527    static EXPECTED_LAST_HEBREW_MONTH: [u8; 33] = [
528        12, 12, 12, 12, 12, 12, 12, 12, 13, 12, 13, 12, 12, 12, 12, 13, 12, 13, 12, 13, 12, 12, 12,
529        12, 12, 13, 12, 13, 12, 13, 12, 12, 12,
530    ];
531
532    static EXPECTED_HEBREW_ELASPED_CALENDAR_DAYS: [i32; 33] = [
533        1158928, 1311957, 1398894, 1422636, 1544627, 1583342, 1626812, 1742956, 1773254, 1807597,
534        1825848, 1843388, 1847051, 1881010, 1897460, 1917895, 1940545, 1942729, 1974889, 1986554,
535        1999723, 2018712, 2037346, 2044640, 2068027, 2077507, 2082262, 2082617, 2083000, 2100511,
536        2101988, 2117699, 2137779,
537    ];
538
539    static EXPECTED_FIXED_HEBREW_NEW_YEAR: [i64; 33] = [
540        -214497, -61470, 25467, 49209, 171200, 209915, 253385, 369529, 399827, 434172, 452421,
541        469963, 473624, 507583, 524033, 544468, 567118, 569302, 601462, 613127, 626296, 645285,
542        663919, 671213, 694600, 704080, 708835, 709190, 709573, 727084, 728561, 744272, 764352,
543    ];
544
545    static EXPECTED_DAYS_IN_HEBREW_YEAR: [u16; 33] = [
546        354, 354, 355, 355, 355, 355, 355, 353, 383, 354, 383, 354, 354, 355, 353, 383, 353, 385,
547        353, 383, 355, 354, 354, 354, 355, 385, 355, 383, 354, 385, 355, 354, 355,
548    ];
549
550    static EXPECTED_MARHESHVAN_VALUES: [bool; 33] = [
551        false, false, true, true, true, true, true, false, false, false, false, false, false, true,
552        false, false, false, true, false, false, true, false, false, false, true, true, true,
553        false, false, true, true, false, true,
554    ];
555
556    static EXPECTED_KISLEV_VALUES: [bool; 33] = [
557        false, false, false, false, false, false, false, true, true, false, true, false, false,
558        false, true, true, true, false, true, true, false, false, false, false, false, false,
559        false, true, false, false, false, false, false,
560    ];
561
562    static EXPECTED_DAY_IN_MONTH: [u8; 33] = [
563        30, 30, 30, 30, 29, 30, 30, 29, 29, 30, 29, 30, 29, 29, 30, 30, 30, 30, 30, 29, 30, 29, 30,
564        30, 30, 30, 30, 30, 30, 29, 29, 29, 30,
565    ];
566
567    #[allow(dead_code)]
568    static CIVIL_EXPECTED_DAY_IN_MONTH: [u8; 33] = [
569        30, 30, 30, 30, 29, 30, 29, 29, 29, 30, 30, 30, 29, 29, 30, 29, 30, 29, 29, 30, 30, 29, 30,
570        30, 30, 30, 30, 29, 30, 30, 29, 29, 30,
571    ];
572
573    #[test]
574    fn test_hebrew_epoch() {
575        // page 119 of the Calendrical Calculations book
576        let fixed_hebrew_date = -1373427.0;
577        assert_eq!(FIXED_HEBREW_EPOCH.to_f64_date(), fixed_hebrew_date);
578    }
579
580    #[test]
581    fn test_hebrew_molad() {
582        let precision = 1_00000f64;
583        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_MOLAD_DATES.iter()) {
584            let molad =
585                (BookHebrew::molad(case.year, case.month).inner() * precision).round() / precision;
586            let final_expected = (expected * precision).round() / precision;
587            assert_eq!(molad, final_expected, "{case:?}");
588        }
589    }
590
591    #[test]
592    fn test_last_book_hebrew_month() {
593        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_LAST_HEBREW_MONTH.iter()) {
594            let last_month = BookHebrew::last_month_of_book_hebrew_year(case.year);
595            assert_eq!(last_month, *expected);
596        }
597    }
598
599    #[test]
600    fn test_book_hebrew_calendar_elapsed_days() {
601        for (case, expected) in HEBREW_DATES
602            .iter()
603            .zip(EXPECTED_HEBREW_ELASPED_CALENDAR_DAYS.iter())
604        {
605            let elapsed_days = BookHebrew::book_hebrew_calendar_elapsed_days(case.year);
606            assert_eq!(elapsed_days, *expected);
607        }
608    }
609
610    #[test]
611    fn test_book_hebrew_year_length_correction() {
612        let year_length_correction: [u8; 33] = [
613            2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
614            0, 0, 0, 0,
615        ];
616        for (case, expected) in HEBREW_DATES.iter().zip(year_length_correction.iter()) {
617            let correction = BookHebrew::book_hebrew_year_length_correction(case.year);
618            assert_eq!(correction, *expected);
619        }
620    }
621
622    #[test]
623    fn test_book_hebrew_new_year() {
624        for (case, expected) in HEBREW_DATES
625            .iter()
626            .zip(EXPECTED_FIXED_HEBREW_NEW_YEAR.iter())
627        {
628            let f_date = BookHebrew::book_hebrew_new_year(case.year);
629            assert_eq!(f_date.to_i64_date(), *expected);
630        }
631    }
632
633    #[test]
634    fn test_days_in_book_hebrew_year() {
635        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_DAYS_IN_HEBREW_YEAR.iter()) {
636            let days_in_year = BookHebrew::days_in_book_hebrew_year(case.year);
637            assert_eq!(days_in_year, *expected);
638        }
639    }
640
641    #[test]
642    fn test_long_marheshvan() {
643        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_MARHESHVAN_VALUES.iter()) {
644            let marsheshvan = BookHebrew::is_long_marheshvan(case.year);
645            assert_eq!(marsheshvan, *expected);
646        }
647    }
648
649    #[test]
650    fn test_short_kislev() {
651        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_KISLEV_VALUES.iter()) {
652            let kislev = BookHebrew::is_short_kislev(case.year);
653            assert_eq!(kislev, *expected);
654        }
655    }
656
657    #[test]
658    fn test_last_day_in_book_hebrew_month() {
659        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_DAY_IN_MONTH.iter()) {
660            let days_in_month = BookHebrew::last_day_of_book_hebrew_month(case.year, case.month);
661            assert_eq!(days_in_month, *expected);
662        }
663    }
664
665    #[test]
666    fn test_fixed_from_book_hebrew() {
667        for (case, f_date) in HEBREW_DATES.iter().zip(TEST_FIXED_DATE.iter()) {
668            assert_eq!(
669                BookHebrew::fixed_from_book_hebrew(BookHebrew {
670                    year: case.year,
671                    month: case.month,
672                    day: case.day
673                }),
674                RataDie::new(*f_date),
675                "{case:?}"
676            );
677        }
678    }
679
680    #[test]
681    fn test_book_hebrew_from_fixed() {
682        for (case, f_date) in HEBREW_DATES.iter().zip(TEST_FIXED_DATE.iter()) {
683            assert_eq!(
684                BookHebrew::book_hebrew_from_fixed(RataDie::new(*f_date)),
685                BookHebrew {
686                    year: case.year,
687                    month: case.month,
688                    day: case.day
689                },
690                "{case:?}"
691            );
692        }
693    }
694
695    #[test]
696    fn test_civil_to_book_conversion() {
697        for (f_date, case) in TEST_FIXED_DATE.iter().zip(HEBREW_DATES.iter()) {
698            let book_hebrew = BookHebrew::book_hebrew_from_fixed(RataDie::new(*f_date));
699            let (y, m, d) = book_hebrew.to_civil_date();
700            let book_hebrew = BookHebrew::from_civil_date(y, m, d);
701
702            assert_eq!(
703                (case.year, case.month),
704                (book_hebrew.year, book_hebrew.month)
705            )
706        }
707    }
708}