rust_zmanim/lib.rs
1// This library is free software; you can redistribute it and/or
2// modify it under the terms of the GNU Lesser General Public
3// License as published by the Free Software Foundation; either
4// version 2.1 of the License, or (at your option) any later version.
5//
6// This library is distributed in the hope that it will be useful,
7// but WITHOUT ANY WARRANTY; without even the implied warranty of
8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// Lesser General Public License for more details.
10//
11// You should have received a copy of the GNU Lesser General Public
12// License along with this library; if not, see:
13// <https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html>
14
15//! # rust-zmanim
16//! `rust-zmanim` calculates solar events (such as sunrise, sunset, and
17//! twilight) and Jewish *zmanim* built on those events.
18//!
19//! ## Modules
20//! - [`astronomical_calculator`]: Astronomical calculations.
21//! - [`zmanim_calculator`]: Core *zmanim* building blocks.
22//! - [`complex_zmanim_calendar`]: Stateful convenience API with many predefined
23//! *zmanim* (See
24//! [`ComplexZmanimCalendar`](complex_zmanim_calendar::ComplexZmanimCalendar))
25//!
26//! ## Examples
27//!
28//! ### `zmanim_calculator`: [*Hanetz*](zmanim_calculator::hanetz) (sunrise)
29//! ```rust
30//! use jiff::{tz::TimeZone, Zoned};
31//! use rust_zmanim::prelude::*;
32//!
33//! let date = Zoned::now().date();
34//! let location = GeoLocation::new(
35//! 31.778,
36//! 35.234,
37//! 754.0,
38//! TimeZone::get("Asia/Jerusalem").unwrap(),
39//! )
40//! .unwrap();
41//!
42//! let hanetz = zmanim_calculator::hanetz(date, &location, false).unwrap();
43//! println!("Hanetz: {}", hanetz.strftime("%H:%M:%S %Z"));
44//! ```
45//!
46//! ### `zmanim_calculator`: [*Shkia*](zmanim_calculator::shkia) (sunset)
47//! ```rust
48//! # use jiff::{tz::TimeZone, Zoned};
49//! # use rust_zmanim::prelude::*;
50//! #
51//! # let date = Zoned::now().date();
52//! # let location = GeoLocation::new(
53//! # 31.778,
54//! # 35.234,
55//! # 754.0,
56//! # TimeZone::get("Asia/Jerusalem").unwrap(),
57//! # )
58//! # .unwrap();
59//! #
60//! let shkia = zmanim_calculator::shkia(date, &location, false).unwrap();
61//! println!("Shkia: {}", shkia.strftime("%H:%M:%S %Z"));
62//! ```
63//!
64//! ### `zmanim_calculator`: [*Chatzos*](zmanim_calculator::chatzos_hayom) (Solar Noon)
65//! ```rust
66//! # use jiff::{tz::TimeZone, Zoned};
67//! # use rust_zmanim::prelude::*;
68//! #
69//! # let date = Zoned::now().date();
70//! # let location = GeoLocation::new(
71//! # 31.778,
72//! # 35.234,
73//! # 754.0,
74//! # TimeZone::get("Asia/Jerusalem").unwrap(),
75//! # )
76//! # .unwrap();
77//! #
78//! let chatzos = zmanim_calculator::chatzos_hayom(date, &location).unwrap();
79//! println!("Chatzos: {}", chatzos.strftime("%H:%M:%S %Z"));
80//! ```
81//!
82//! ### `zmanim_calculator`: [*Alos*](zmanim_calculator::alos) with a Minutes Offset
83//! ```rust
84//! # use jiff::{tz::TimeZone, Zoned};
85//! # use rust_zmanim::prelude::*;
86//! #
87//! # let date = Zoned::now().date();
88//! # let location = GeoLocation::new(
89//! # 31.778,
90//! # 35.234,
91//! # 754.0,
92//! # TimeZone::get("Asia/Jerusalem").unwrap(),
93//! # )
94//! # .unwrap();
95//! #
96//! let alos_72 =
97//! zmanim_calculator::alos(date, &location, false, &ZmanOffset::Minutes(72.0)).unwrap();
98//! println!("Alos (72 min): {}", alos_72.strftime("%H:%M:%S %Z"));
99//! ```
100//!
101//! ### `zmanim_calculator`: [*Tzeis*](zmanim_calculator::tzeis) with a Degrees Offset
102//! ```rust
103//! # use jiff::{tz::TimeZone, Zoned};
104//! # use rust_zmanim::prelude::*;
105//! #
106//! # let date = Zoned::now().date();
107//! # let location = GeoLocation::new(
108//! # 31.778,
109//! # 35.234,
110//! # 754.0,
111//! # TimeZone::get("Asia/Jerusalem").unwrap(),
112//! # )
113//! # .unwrap();
114//! #
115//! let tzeis_4_5 =
116//! zmanim_calculator::tzeis(date, &location, false, &ZmanOffset::Degrees(4.5)).unwrap();
117//! println!("Tzeis (4.5 deg): {}", tzeis_4_5.strftime("%H:%M:%S %Z"));
118//! ```
119//!
120//! ### `zmanim_calculator`: [*Sof Zman Shema*](zmanim_calculator::sof_zman_shema) (MGA) using *zmanis*-based *Alos* and *Tzeis*
121//! ```rust
122//! # use jiff::{tz::TimeZone, Zoned};
123//! # use rust_zmanim::prelude::*;
124//! #
125//! # let date = Zoned::now().date();
126//! # let location = GeoLocation::new(
127//! # 31.778,
128//! # 35.234,
129//! # 754.0,
130//! # TimeZone::get("Asia/Jerusalem").unwrap(),
131//! # )
132//! # .unwrap();
133//! #
134//! let hanetz = zmanim_calculator::hanetz(date, &location, false).unwrap();
135//! let shkia = zmanim_calculator::shkia(date, &location, false).unwrap();
136//! // this is a GRA shaah zmanis, based on sunrise and sunset
137//! let shaah_zmanis_gra = zmanim_calculator::shaah_zmanis(&hanetz, &shkia);
138//!
139//! // the offset used for alos and tzeis will be based on shaos zmaniyos
140//! let offset_50_zmanis = ZmanOffset::MinutesZmaniyos {
141//! minutes_zmaniyos: 50.0,
142//! shaah_zmanis: shaah_zmanis_gra,
143//! };
144//! let alos_50_zmanis =
145//! zmanim_calculator::alos(date, &location, false, &offset_50_zmanis).unwrap();
146//! let tzeis_50_zmanis =
147//! zmanim_calculator::tzeis(date, &location, false, &offset_50_zmanis).unwrap();
148//!
149//! // calculate Sof Zman Shema according to the Magen Avraham, that it is
150//! // calculated from alos to tzeis
151//! let szks = zmanim_calculator::sof_zman_shema(&alos_50_zmanis, &tzeis_50_zmanis);
152//! println!(
153//! "Sof Zman Shema (MGA 50 min zmaniyos): {}",
154//! szks.strftime("%H:%M:%S %Z")
155//! );
156//! ```
157//!
158//! ### [`ComplexZmanimCalendar`](complex_zmanim_calendar::ComplexZmanimCalendar): Build Once, Reuse Many Times
159//! ```rust
160//! # use jiff::{tz::TimeZone, Zoned};
161//! # use rust_zmanim::prelude::*;
162//! #
163//! # let date = Zoned::now().date();
164//! # let location = GeoLocation::new(
165//! # 31.778,
166//! # 35.234,
167//! # 754.0,
168//! # TimeZone::get("Asia/Jerusalem").unwrap(),
169//! # )
170//! # .unwrap();
171//! #
172//! let czc = ComplexZmanimCalendar::new(location, date, UseElevation::No);
173//!
174//! println!("Hanetz: {}", czc.hanetz().unwrap().strftime("%H:%M:%S %Z"));
175//! println!("Shkia: {}", czc.shkia().unwrap().strftime("%H:%M:%S %Z"));
176//! ```
177//!
178//! ### `ComplexZmanimCalendar`: Premade [72-Minute *Alos*](complex_zmanim_calendar::ComplexZmanimCalendar::alos_72_minutes)
179//! ```rust
180//! # use jiff::{tz::TimeZone, Zoned};
181//! # use rust_zmanim::prelude::*;
182//! #
183//! # let date = Zoned::now().date();
184//! # let location = GeoLocation::new(
185//! # 31.778,
186//! # 35.234,
187//! # 754.0,
188//! # TimeZone::get("Asia/Jerusalem").unwrap(),
189//! # )
190//! # .unwrap();
191//! #
192//! # let czc = ComplexZmanimCalendar::new(location, date, UseElevation::No);
193//! #
194//! println!("Alos (72 min): {}", czc.alos_72_minutes().unwrap().strftime("%H:%M:%S %Z"));
195//! ```
196//!
197//! ### `ComplexZmanimCalendar`: [Generic *Tzeis*](complex_zmanim_calendar::ComplexZmanimCalendar::tzeis) with Custom Offset
198//! ```rust
199//! # use jiff::{tz::TimeZone, Zoned};
200//! # use rust_zmanim::prelude::*;
201//! #
202//! # let date = Zoned::now().date();
203//! # let location = GeoLocation::new(
204//! # 31.778,
205//! # 35.234,
206//! # 754.0,
207//! # TimeZone::get("Asia/Jerusalem").unwrap(),
208//! # )
209//! # .unwrap();
210//! #
211//! # let czc = ComplexZmanimCalendar::new(location, date, UseElevation::No);
212//! #
213//! let tzeis_7_083 = czc.tzeis(&ZmanOffset::Degrees(7.083)).unwrap();
214//! println!("Tzeis (7.083 deg): {}", tzeis_7_083.strftime("%H:%M:%S %Z"));
215//! ```
216//!
217//! ### `ComplexZmanimCalendar`: [*Tzeis* 90 Minutes *Zmaniyos* After Sunset](complex_zmanim_calendar::ComplexZmanimCalendar::tzeis_90_minutes_zmanis)
218//! ```rust
219//! # use jiff::{tz::TimeZone, Zoned};
220//! # use rust_zmanim::prelude::*;
221//! #
222//! # let date = Zoned::now().date();
223//! # let location = GeoLocation::new(
224//! # 31.778,
225//! # 35.234,
226//! # 754.0,
227//! # TimeZone::get("Asia/Jerusalem").unwrap(),
228//! # )
229//! # .unwrap();
230//! #
231//! # let czc = ComplexZmanimCalendar::new(location, date, UseElevation::No);
232//! #
233//! let tzeis_90_zmanis = czc.tzeis_90_minutes_zmanis().unwrap();
234//! println!(
235//! "Tzeis (90 min zmaniyos): {}",
236//! tzeis_90_zmanis.strftime("%H:%M:%S %Z")
237//! );
238//! ```
239//!
240//! ### `ComplexZmanimCalendar`: [Elevation Modes](complex_zmanim_calendar::UseElevation)
241//! ```rust
242//! # use jiff::{tz::TimeZone, Zoned};
243//! # use rust_zmanim::prelude::*;
244//! #
245//! # let date = Zoned::now().date();
246//! # let location = GeoLocation::new(
247//! # 31.778,
248//! # 35.234,
249//! # 754.0,
250//! # TimeZone::get("Asia/Jerusalem").unwrap(),
251//! # )
252//! # .unwrap();
253//! #
254//! let sea_level = ComplexZmanimCalendar::new(location.clone(), date, UseElevation::No);
255//! let elevated = ComplexZmanimCalendar::new(location, date, UseElevation::HanetzShkia);
256//!
257//! let sea_level_hanetz = sea_level.hanetz().unwrap();
258//! let elevated_hanetz = elevated.hanetz().unwrap();
259//! println!("Sea-level hanetz: {}", sea_level_hanetz.strftime("%H:%M:%S %Z"));
260//! println!("Elevated hanetz: {}", elevated_hanetz.strftime("%H:%M:%S %Z"));
261//! ```
262//!
263//! ### `ComplexZmanimCalendar`: Look Up *Zmanim* by Name with the [Registry](complex_zmanim_calendar::ALL_ZMANIM)
264//! ```rust
265//! # use jiff::{tz::TimeZone, Zoned};
266//! # use rust_zmanim::prelude::*;
267//! #
268//! # let date = Zoned::now().date();
269//! # let location = GeoLocation::new(
270//! # 31.778,
271//! # 35.234,
272//! # 754.0,
273//! # TimeZone::get("Asia/Jerusalem").unwrap(),
274//! # )
275//! # .unwrap();
276//! #
277//! # let czc = ComplexZmanimCalendar::new(location, date, UseElevation::No);
278//! #
279//! let entry = find_zman("sof_zman_shema_gra").unwrap();
280//! match (entry.compute)(&czc) {
281//! Some(ZmanValue::Time(time)) => println!("{}: {}", entry.name, time.strftime("%H:%M:%S %Z")),
282//! Some(ZmanValue::Duration(duration)) => println!("{}: {duration:#}", entry.name),
283//! None => println!("{}: does not occur", entry.name),
284//! }
285//!
286//! // or iterate over every zman
287//! for entry in ALL_ZMANIM {
288//! // ...
289//! }
290//! ```
291//!
292//! ## Notes
293//! - Most APIs return `Option<Zoned>`. A result of `None` means the requested
294//! event does not occur for the requested date/location (common in high
295//! latitudes or for deep-twilight calculations).
296//! - [`ComplexZmanimCalendar`](complex_zmanim_calendar::ComplexZmanimCalendar)
297//! lazily computes and caches the underlying solar events per instance, so
298//! each event is calculated at most once no matter how many *zmanim* are
299//! requested. The `set_date` and `set_geo_location` setters clear the cache.
300//! - This crate uses the solar position algorithm implemented by NOAA, based on
301//! equations from *Astronomical Algorithms* by Jean Meeus. See
302//! [`noaa_calculator`](crate::util::noaa_calculator) for more details
303//! - Returned values can be highly precise, but real-world observations vary
304//! with atmospheric conditions, temperature, pressure, etc.
305//! - For religious practice (*halacha lemaaseh*), **consult competent halachic
306//! guidance**.
307//! - **Elevation based *zmanim* (even sunrise and sunset) should not be used
308//! *lekula* without the guidance of a *posek***. According to Rabbi Dovid
309//! Yehudah Bursztyn in his *Zmanim Kehilchasam*, 7th edition chapter 2,
310//! section 7 (pages 181-182) and section 9 (pages 186-187), no *zmanim*
311//! besides sunrise and sunset should use elevation. However, Rabbi Yechiel
312//! Avrahom Zilber in the *Birur Halacha* Vol. 6 Ch. 58 Pages 34 and 42 is of
313//! the opinion that elevation should be accounted for in *zmanim*
314//! calculations. Related to this, Rabbi Yaakov Karp in *Shimush Zekeinim*,
315//! Ch. 1, page 17 states that obstructing horizons should be factored into
316//! *zmanim* calculations.
317//!
318//! ## Background
319//! This crate is a Rust port which reuses a lot of code and documentation from:
320//! - [KosherJava](https://github.com/KosherJava/zmanim)
321//! - [python-zmanim](https://github.com/pinnymz/python-zmanim)
322//!
323//! See the [KosherJava website](https://kosherjava.com) for additional
324//! background on *zmanim*.
325
326#![warn(missing_docs)]
327#![warn(clippy::unwrap_used)]
328
329pub mod astronomical_calculator;
330pub mod complex_zmanim_calendar;
331pub mod util;
332pub mod zmanim_calculator;
333
334/// A convenience module for glob imports. `use rust_zmanim::prelude::*;`
335pub mod prelude {
336 pub use crate::{
337 astronomical_calculator,
338 complex_zmanim_calendar::*,
339 util::geolocation::GeoLocation,
340 zmanim_calculator::{self, ZmanOffset},
341 };
342}