kosher_rust/limudim/mod.rs
1//! Daily Torah and Talmud learning schedules (*limudim*).
2//!
3//! This module maps a Hebrew calendar date to the learning unit assigned by a
4//! particular program: Daf Yomi, Mishna Yomis, Pirkei Avos, monthly Tehillim,
5//! and others. Each schedule is a zero-sized calculator type implementing
6//! [`Limud`]; call [`LimudCalendar::limud`] on any [`HebrewCalendarDate`] to
7//! look up that day's assignment.
8//!
9//! # Schedules
10//!
11//! | Calculator | Unit type | Notes |
12//! |------------|-----------|-------|
13//! | [`DafYomiBavli`] | [`Daf`] | Daily Bavli daf; cycle 1 began 1923-09-11 |
14//! | [`DafHashavuaBavli`] | [`Daf`] | Weekly Bavli daf (Sunday–Friday); began 2005-03-06 |
15//! | [`AmudYomiBavliDirshu`] | [`Amud`] | Daily Bavli amud (Dirshu); began 2023-10-16 |
16//! | [`DafYomiYerushalmiVilna`] | [`Daf`] | Daily Yerushalmi daf (Vilna edition); began 1980-02-02 |
17//! | [`MishnaYomis`] | [`Mishnas`] | Two mishnayos per day; began 1947-05-20 |
18//! | [`PirkeiAvos`] | [`PirkeiAvosUnit`] | Summer Shabbos afternoons; Israel vs. diaspora |
19//! | [`TehillimMonthly`] | [`TehillimUnit`] | Psalms divided across the Hebrew month |
20//!
21//! Returns `None` when no learning is scheduled (for example, before a cycle
22//! starts, on Shabbat for weekly programs, or outside Pirkei Avos season).
23//!
24//! # Quick start
25//!
26//! ```
27//! use jiff::civil;
28//! use kosher_rust::limudim::prelude::*;
29//!
30//! let date = civil::date(2017, 12, 28);
31//! let daf = date.limud(DafYomiBavli::default()).unwrap();
32//! assert_eq!(daf.tractate, Tractate::Shevuos);
33//! assert_eq!(daf.page, 30);
34//! ```
35//!
36//! Import [`prelude`] (or [`crate::prelude`]) for calculators,
37//! unit types, and [`LimudCalendar`].
38
39use icu_calendar::{
40 Date,
41 cal::Hebrew,
42 options::{DateAddOptions, DateDifferenceOptions},
43 types::DateDuration,
44};
45
46mod amud_yomi_bavli_dirshu;
47mod cycle;
48mod daf_hashavua_bavli;
49mod daf_yomi_bavli;
50mod daf_yomi_yerushalmi;
51mod interval;
52mod limud;
53mod mishna_yomis;
54mod pirkei_avos;
55mod tehillim_monthly;
56mod units;
57
58/// Looks up the scheduled daily limud for a calendar date.
59///
60/// Implemented for any type that can be viewed as a Hebrew date (ICU, Jiff, etc.).
61/// Pass a [`Limud`] such as [`DafYomiBavli`] to select the schedule.
62pub trait LimudCalendar {
63 /// Calculate the limud (learning unit) for this date using the given calculator.
64 ///
65 /// # Arguments
66 /// * `limud_calculator` - A calculator implementing the [`Limud`] trait
67 ///
68 /// # Returns
69 /// The learning unit for this date, or `None` if no learning is scheduled
70 fn limud<U>(&self, limud: impl Limud<U>) -> Option<U>;
71}
72impl<T> LimudCalendar for T
73where
74 T: HebrewCalendarDate,
75{
76 fn limud<U>(&self, limud: impl Limud<U>) -> Option<U> {
77 limud.limud(self.hebrew_date())
78 }
79}
80pub use amud_yomi_bavli_dirshu::AmudYomiBavliDirshu;
81pub use daf_hashavua_bavli::DafHashavuaBavli;
82pub use daf_yomi_bavli::DafYomiBavli;
83pub use daf_yomi_yerushalmi::DafYomiYerushalmiVilna;
84pub use mishna_yomis::{MishnaYomis, Mishnas};
85pub use pirkei_avos::{PirkeiAvos, PirkeiAvosUnit};
86pub use tehillim_monthly::{TehillimMonthly, TehillimUnit};
87
88pub use units::{Amud, Daf, Mishna, Side, Tractate};
89
90pub use limud::Limud;
91
92use crate::calendar::HebrewCalendarDate;
93
94/// Common limudim imports.
95///
96/// Import this module to bring the limud extension trait, calculators, unit
97/// types, and public schedule constants into scope.
98pub mod prelude {
99 pub use super::{
100 Amud, AmudYomiBavliDirshu, Daf, DafHashavuaBavli, DafYomiBavli, DafYomiYerushalmiVilna, Limud, LimudCalendar,
101 Mishna, MishnaYomis, Mishnas, PirkeiAvos, PirkeiAvosUnit, Side, TehillimMonthly, TehillimUnit, Tractate,
102 };
103}
104
105pub(crate) trait HebrewDateExt {
106 /// Return a copy of this date with `days` added.
107 fn add_days(&self, days: i32) -> Option<Date<Hebrew>>;
108 /// Calculate the number of days between two Hebrew dates, inclusive of partial spans.
109 ///
110 /// Returns `None` when `self > end`, or when ICU cannot compute the difference
111 /// (e.g. dates outside the supported Hebrew calendar range).
112 fn days_until(&self, end: &Date<Hebrew>) -> Option<u32>;
113}
114
115impl HebrewDateExt for Date<Hebrew> {
116 fn add_days(&self, days: i32) -> Option<Date<Hebrew>> {
117 self.try_added_with_options(DateDuration::for_days(days), DateAddOptions::default())
118 .ok()
119 }
120
121 fn days_until(&self, end: &Date<Hebrew>) -> Option<u32> {
122 if self > end {
123 return None;
124 }
125 match self.try_until_with_options(end, DateDifferenceOptions::default()) {
126 Ok(duration) => Some(duration.days),
127 Err(error) => match error {},
128 }
129 }
130}
131
132/// Total number of amudim (half-pages) in the Babylonian Talmud for Dirshu
133pub(crate) const BAVLI_TOTAL_AMUDIM: i32 = 5407;
134
135/// Number of dafim in Daf Yomi Bavli cycles 1-7 (before Shekalim expansion)
136pub(crate) const BAVLI_DAF_COUNT_EARLY: i32 = 2702;
137
138/// Number of dafim in Daf Yomi Bavli cycles 8+ (after Shekalim expansion)
139pub(crate) const BAVLI_DAF_COUNT_MODERN: i32 = 2711;
140
141/// Number of dafim in the Yerushalmi Talmud
142pub(crate) const YERUSHALMI_DAF_COUNT: i32 = 1554;
143
144/// Number of days in a Mishna Yomis cycle.
145///
146/// This is the zero-based offset from cycle start to the last inclusive day
147/// (4192 mishnas at 2 per day → 2096 days, so last day = start + 2095).
148pub(crate) const MISHNA_YOMIS_CYCLE_DAYS: i32 = 2095;
149
150/// Cycle number at which Shekalim expanded from 13 to 22 pages
151pub(crate) const SHEKALIM_EXPANSION_CYCLE: i32 = 8;
152
153#[cfg(test)]
154#[allow(clippy::expect_used)]
155/// A helper function to convert a Gregorian date to a Hebrew date.
156/// This is only allowed in test code.
157pub(crate) fn from_gregorian_date(year: i32, month: u8, day: u8) -> Date<Hebrew> {
158 Date::try_new_gregorian(year, month, day)
159 .expect("hard-coded Gregorian date should be valid")
160 .to_calendar(Hebrew)
161}