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        assert_eq!(FIXED_HEBREW_EPOCH, RataDie::new(-1373427));
577    }
578
579    #[test]
580    fn test_hebrew_molad() {
581        let precision = 1_00000f64;
582        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_MOLAD_DATES.iter()) {
583            let molad =
584                (BookHebrew::molad(case.year, case.month).inner() * precision).round() / precision;
585            let final_expected = (expected * precision).round() / precision;
586            assert_eq!(molad, final_expected, "{case:?}");
587        }
588    }
589
590    #[test]
591    fn test_last_book_hebrew_month() {
592        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_LAST_HEBREW_MONTH.iter()) {
593            let last_month = BookHebrew::last_month_of_book_hebrew_year(case.year);
594            assert_eq!(last_month, *expected);
595        }
596    }
597
598    #[test]
599    fn test_book_hebrew_calendar_elapsed_days() {
600        for (case, expected) in HEBREW_DATES
601            .iter()
602            .zip(EXPECTED_HEBREW_ELASPED_CALENDAR_DAYS.iter())
603        {
604            let elapsed_days = BookHebrew::book_hebrew_calendar_elapsed_days(case.year);
605            assert_eq!(elapsed_days, *expected);
606        }
607    }
608
609    #[test]
610    fn test_book_hebrew_year_length_correction() {
611        let year_length_correction: [u8; 33] = [
612            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,
613            0, 0, 0, 0,
614        ];
615        for (case, expected) in HEBREW_DATES.iter().zip(year_length_correction.iter()) {
616            let correction = BookHebrew::book_hebrew_year_length_correction(case.year);
617            assert_eq!(correction, *expected);
618        }
619    }
620
621    #[test]
622    fn test_book_hebrew_new_year() {
623        for (case, expected) in HEBREW_DATES
624            .iter()
625            .zip(EXPECTED_FIXED_HEBREW_NEW_YEAR.iter())
626        {
627            let f_date = BookHebrew::book_hebrew_new_year(case.year);
628            assert_eq!(f_date.to_i64_date(), *expected);
629        }
630    }
631
632    #[test]
633    fn test_days_in_book_hebrew_year() {
634        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_DAYS_IN_HEBREW_YEAR.iter()) {
635            let days_in_year = BookHebrew::days_in_book_hebrew_year(case.year);
636            assert_eq!(days_in_year, *expected);
637        }
638    }
639
640    #[test]
641    fn test_long_marheshvan() {
642        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_MARHESHVAN_VALUES.iter()) {
643            let marsheshvan = BookHebrew::is_long_marheshvan(case.year);
644            assert_eq!(marsheshvan, *expected);
645        }
646    }
647
648    #[test]
649    fn test_short_kislev() {
650        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_KISLEV_VALUES.iter()) {
651            let kislev = BookHebrew::is_short_kislev(case.year);
652            assert_eq!(kislev, *expected);
653        }
654    }
655
656    #[test]
657    fn test_last_day_in_book_hebrew_month() {
658        for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_DAY_IN_MONTH.iter()) {
659            let days_in_month = BookHebrew::last_day_of_book_hebrew_month(case.year, case.month);
660            assert_eq!(days_in_month, *expected);
661        }
662    }
663
664    #[test]
665    fn test_fixed_from_book_hebrew() {
666        for (case, f_date) in HEBREW_DATES.iter().zip(TEST_FIXED_DATE.iter()) {
667            assert_eq!(
668                BookHebrew::fixed_from_book_hebrew(BookHebrew {
669                    year: case.year,
670                    month: case.month,
671                    day: case.day
672                }),
673                RataDie::new(*f_date),
674                "{case:?}"
675            );
676        }
677    }
678
679    #[test]
680    fn test_book_hebrew_from_fixed() {
681        for (case, f_date) in HEBREW_DATES.iter().zip(TEST_FIXED_DATE.iter()) {
682            assert_eq!(
683                BookHebrew::book_hebrew_from_fixed(RataDie::new(*f_date)),
684                BookHebrew {
685                    year: case.year,
686                    month: case.month,
687                    day: case.day
688                },
689                "{case:?}"
690            );
691        }
692    }
693
694    #[test]
695    fn test_civil_to_book_conversion() {
696        for (f_date, case) in TEST_FIXED_DATE.iter().zip(HEBREW_DATES.iter()) {
697            let book_hebrew = BookHebrew::book_hebrew_from_fixed(RataDie::new(*f_date));
698            let (y, m, d) = book_hebrew.to_civil_date();
699            let book_hebrew = BookHebrew::from_civil_date(y, m, d);
700
701            assert_eq!(
702                (case.year, case.month),
703                (book_hebrew.year, book_hebrew.month)
704            )
705        }
706    }
707}