1use core_maths::CoreFloat;
14use icu_calendar::{
15 options::{DateAddOptions, Overflow},
16 types::DateDuration,
17};
18use jiff::{SignedDuration as Duration, Timestamp, ToSpan, tz::TimeZone};
19
20use crate::{
21 calendar::{HebrewCalendarDate, HebrewHolidayCalendar, holiday::Holiday},
22 zmanim::{
23 ZmanLike, ZmanimCalculator,
24 molad::{is_same_gregorian_day, months_molad},
25 types::error::ZmanimError,
26 },
27};
28
29static CIVIL_ZENITH: f64 = 6.0;
30static NAUTICAL_ZENITH: f64 = 12.0;
31static ASTRONOMICAL_ZENITH: f64 = 18.0;
32
33#[derive(Debug, Clone)]
37pub enum ZmanPrimitive {
38 ElevationAdjustedSunrise,
40 SeaLevelSunrise,
42 ConfiguredSunrise,
44 ConfiguredSunset,
46 SolarTransit,
48 SolarMidnight,
50 ChatzosHayomAsHalfDay,
52 ChatzosHayom,
54 ChatzosHalaylaAsHalfDay,
56 ChatzosHalayla,
58 ElevationAdjustedSunset,
60 SeaLevelSunset,
62 SunriseOffsetByDegrees(f64),
64 SunsetOffsetByDegrees(f64),
66 LocalMeanTime(f64),
68 CandleLighting,
70 Offset(&'static ZmanPrimitive, Duration),
72 ZmanisOffset(&'static ZmanPrimitive, f64),
74 HalfDayBasedOffset(&'static ZmanPrimitive, &'static ZmanPrimitive, f64),
76 Shema(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
78 MinchaGedola(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
80 SamuchLeMinchaKetana(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
82 MinchaKetana(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
84 Tefila(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
86 PlagHamincha(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
88 SofZmanBiurChametz(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
90 SofZmanAchilasChametz(&'static ZmanPrimitive, &'static ZmanPrimitive, bool),
92 TzaisAteretTorah,
94 SofZmanKidushLevana15Days,
96 SofZmanKidushLevanaBetweenMoldos,
99 TchilasZmanKidushLevana3Days,
101 TchilasZmanKidushLevana7Days,
104 BainHashmashosRt2Stars,
106 MinchaGedolaAhavatShalom,
108 MinchaGedolaGraGreaterThan30,
110 MinchaKetanaAhavatShalom,
112 PlagAhavatShalom,
114 BeginCivilTwilight,
116 EndCivilTwilight,
118 BeginNauticalTwilight,
120 EndNauticalTwilight,
122 BeginAstronomicalTwilight,
124 EndAstronomicalTwilight,
126 SunsetOrWesternmostSolarAzimuth,
128 SunriseOrEasternmostSolarAzimuth,
130}
131
132#[cfg(feature = "defmt")]
133impl defmt::Format for ZmanPrimitive {
134 fn format(&self, fmt: defmt::Formatter) {
135 match self {
136 ZmanPrimitive::ElevationAdjustedSunrise => defmt::write!(fmt, "ElevationAdjustedSunrise"),
137 ZmanPrimitive::SeaLevelSunrise => defmt::write!(fmt, "SeaLevelSunrise"),
138 ZmanPrimitive::ConfiguredSunrise => defmt::write!(fmt, "ConfiguredSunrise"),
139 ZmanPrimitive::ConfiguredSunset => defmt::write!(fmt, "ConfiguredSunset"),
140 ZmanPrimitive::SolarTransit => defmt::write!(fmt, "SolarTransit"),
141 ZmanPrimitive::SolarMidnight => defmt::write!(fmt, "SolarMidnight"),
142 ZmanPrimitive::ElevationAdjustedSunset => defmt::write!(fmt, "ElevationAdjustedSunset"),
143 ZmanPrimitive::SeaLevelSunset => defmt::write!(fmt, "SeaLevelSunset"),
144 ZmanPrimitive::SunriseOffsetByDegrees(degrees) => {
145 defmt::write!(fmt, "SunriseOffsetByDegrees({})", degrees)
146 }
147 ZmanPrimitive::SunsetOffsetByDegrees(degrees) => {
148 defmt::write!(fmt, "SunsetOffsetByDegrees({})", degrees)
149 }
150 ZmanPrimitive::LocalMeanTime(hour) => defmt::write!(fmt, "LocalMeanTime({})", hour),
151 ZmanPrimitive::CandleLighting => defmt::write!(fmt, "CandleLighting"),
152 ZmanPrimitive::Offset(primitive, duration) => {
153 defmt::write!(fmt, "Offset({}, {}s)", primitive, duration.as_secs_f64())
154 }
155 ZmanPrimitive::ZmanisOffset(primitive, hours) => {
156 defmt::write!(fmt, "ZmanisOffset({}, {})", primitive, hours)
157 }
158 ZmanPrimitive::HalfDayBasedOffset(start, end, fraction) => {
159 defmt::write!(fmt, "HalfDayBasedOffset({}, {}, {})", start, end, fraction)
160 }
161 ZmanPrimitive::Shema(start, end, fixed) => {
162 defmt::write!(fmt, "Shema({}, {}, {})", start, end, fixed)
163 }
164 ZmanPrimitive::MinchaGedola(start, end, fixed) => {
165 defmt::write!(fmt, "MinchaGedola({}, {}, {})", start, end, fixed)
166 }
167 ZmanPrimitive::SamuchLeMinchaKetana(start, end, fixed) => {
168 defmt::write!(fmt, "SamuchLeMinchaKetana({}, {}, {})", start, end, fixed)
169 }
170 ZmanPrimitive::MinchaKetana(start, end, fixed) => {
171 defmt::write!(fmt, "MinchaKetana({}, {}, {})", start, end, fixed)
172 }
173 ZmanPrimitive::Tefila(start, end, fixed) => {
174 defmt::write!(fmt, "Tefila({}, {}, {})", start, end, fixed)
175 }
176 ZmanPrimitive::PlagHamincha(start, end, fixed) => {
177 defmt::write!(fmt, "PlagHamincha({}, {}, {})", start, end, fixed)
178 }
179 ZmanPrimitive::SofZmanBiurChametz(start, end, fixed) => {
180 defmt::write!(fmt, "SofZmanBiurChametz({}, {}, {})", start, end, fixed)
181 }
182 ZmanPrimitive::SofZmanAchilasChametz(start, end, fixed) => {
183 defmt::write!(fmt, "SofZmanAchilasChametz({}, {}, {})", start, end, fixed)
184 }
185 ZmanPrimitive::TzaisAteretTorah => defmt::write!(fmt, "TzaisAteretTorah"),
186 ZmanPrimitive::SofZmanKidushLevana15Days => {
187 defmt::write!(fmt, "SofZmanKidushLevana15Days")
188 }
189 ZmanPrimitive::SofZmanKidushLevanaBetweenMoldos => {
190 defmt::write!(fmt, "SofZmanKidushLevanaBetweenMoldos")
191 }
192 ZmanPrimitive::TchilasZmanKidushLevana3Days => {
193 defmt::write!(fmt, "TchilasZmanKidushLevana3Days")
194 }
195 ZmanPrimitive::TchilasZmanKidushLevana7Days => {
196 defmt::write!(fmt, "TchilasZmanKidushLevana7Days")
197 }
198 ZmanPrimitive::BainHashmashosRt2Stars => defmt::write!(fmt, "BainHashmashosRt2Stars"),
199 ZmanPrimitive::MinchaGedolaAhavatShalom => defmt::write!(fmt, "MinchaGedolaAhavatShalom"),
200 ZmanPrimitive::MinchaGedolaGraGreaterThan30 => {
201 defmt::write!(fmt, "MinchaGedolaGraGreaterThan30")
202 }
203 ZmanPrimitive::MinchaKetanaAhavatShalom => defmt::write!(fmt, "MinchaKetanaAhavatShalom"),
204 ZmanPrimitive::PlagAhavatShalom => defmt::write!(fmt, "PlagAhavatShalom"),
205 ZmanPrimitive::BeginCivilTwilight => defmt::write!(fmt, "BeginCivilTwilight"),
206 ZmanPrimitive::EndCivilTwilight => defmt::write!(fmt, "EndCivilTwilight"),
207 ZmanPrimitive::BeginNauticalTwilight => defmt::write!(fmt, "BeginNauticalTwilight"),
208 ZmanPrimitive::EndNauticalTwilight => defmt::write!(fmt, "EndNauticalTwilight"),
209 ZmanPrimitive::BeginAstronomicalTwilight => defmt::write!(fmt, "BeginAstronomicalTwilight"),
210 ZmanPrimitive::EndAstronomicalTwilight => defmt::write!(fmt, "EndAstronomicalTwilight"),
211 ZmanPrimitive::SunsetOrWesternmostSolarAzimuth => {
212 defmt::write!(fmt, "SunsetOrWesternmostSolarAzimuth")
213 }
214 ZmanPrimitive::SunriseOrEasternmostSolarAzimuth => {
215 defmt::write!(fmt, "SunriseOrEasternmostSolarAzimuth")
216 }
217 ZmanPrimitive::ChatzosHayomAsHalfDay => defmt::write!(fmt, "ChatzosHayomAsHalfDay"),
218 ZmanPrimitive::ChatzosHayom => defmt::write!(fmt, "ChatzosHayom"),
219 ZmanPrimitive::ChatzosHalaylaAsHalfDay => defmt::write!(fmt, "ChatzosHalaylaAsHalfDay"),
220 ZmanPrimitive::ChatzosHalayla => defmt::write!(fmt, "ChatzosHalayla"),
221 }
222 }
223}
224
225impl ZmanLike for ZmanPrimitive {
226 fn calculate(&self, calculator: &ZmanimCalculator) -> Result<Timestamp, ZmanimError> {
227 match *self {
228 ZmanPrimitive::ConfiguredSunrise => {
229 if calculator.config.use_elevation {
230 ZmanPrimitive::ElevationAdjustedSunrise.calculate(calculator)
231 } else {
232 ZmanPrimitive::SeaLevelSunrise.calculate(calculator)
233 }
234 }
235 ZmanPrimitive::ConfiguredSunset => {
236 if calculator.config.use_elevation {
237 ZmanPrimitive::ElevationAdjustedSunset.calculate(calculator)
238 } else {
239 ZmanPrimitive::SeaLevelSunset.calculate(calculator)
240 }
241 }
242 ZmanPrimitive::ElevationAdjustedSunrise => {
243 crate::zmanim::astronomy::sunrise(calculator.date, &calculator.location, true)
244 }
245 ZmanPrimitive::SeaLevelSunrise => {
246 crate::zmanim::astronomy::sunrise(calculator.date, &calculator.location, false)
247 }
248 ZmanPrimitive::SolarTransit => crate::zmanim::astronomy::solar_noon(calculator.date, &calculator.location),
249 ZmanPrimitive::SolarMidnight => {
250 crate::zmanim::astronomy::solar_midnight(calculator.date, &calculator.location)
251 }
252 ZmanPrimitive::ElevationAdjustedSunset => {
253 crate::zmanim::astronomy::sunset(calculator.date, &calculator.location, true)
254 }
255 ZmanPrimitive::SeaLevelSunset => {
256 crate::zmanim::astronomy::sunset(calculator.date, &calculator.location, false)
257 }
258 ZmanPrimitive::SunriseOffsetByDegrees(degrees) => {
259 crate::zmanim::astronomy::sunrise_offset_by_degrees(calculator.date, &calculator.location, degrees)
260 }
261 ZmanPrimitive::SunsetOffsetByDegrees(degrees) => {
262 crate::zmanim::astronomy::sunset_offset_by_degrees(calculator.date, &calculator.location, degrees)
263 }
264 ZmanPrimitive::LocalMeanTime(hours) => {
265 let date = calculator.date;
266 let location = calculator.location.clone();
267 if !(0.0..24.0).contains(&hours) {
268 return Err(ZmanimError::InvalidHours);
269 }
270
271 let adjusted_date = crate::zmanim::astronomy::adjusted_local_date(date, &location)?;
272 let midnight = adjusted_date.at(0, 0, 0, 0);
273 let lmt_nanos = CoreFloat::round(hours * 3600.0 * 1_000_000_000.0) as i64;
274 let offset_nanos = CoreFloat::round(location.longitude * 240.0 * 1_000_000_000.0) as i64;
275 let lmt_dt = midnight
276 .checked_add(Duration::from_nanos(lmt_nanos))
277 .and_then(|dt| dt.checked_sub(Duration::from_nanos(offset_nanos)))
278 .map_err(|_| ZmanimError::TimeConversionError)?;
279 let utc = lmt_dt
280 .to_zoned(TimeZone::UTC)
281 .map_err(|_| ZmanimError::TimeConversionError)?
282 .timestamp();
283
284 Ok(utc)
285 }
286 ZmanPrimitive::CandleLighting => {
287 let sunset = ZmanPrimitive::SeaLevelSunset.calculate(calculator)?;
295 Ok(sunset - calculator.config.candle_lighting_offset)
296 }
297 ZmanPrimitive::Offset(event, duration) => {
298 let event_time = event.calculate(calculator)?;
299 Ok(event_time + duration)
300 }
301 ZmanPrimitive::ZmanisOffset(event, hours) => {
302 let event_time = event.calculate(calculator)?;
303 let sunrise = calculator.calculate(&ZmanPrimitive::ConfiguredSunrise)?;
304 let sunset = calculator.calculate(&ZmanPrimitive::ConfiguredSunset)?;
305 let shaah_zmanis = sunset.duration_since(sunrise) / 12;
306 let offset = shaah_zmanis.mul_f64(hours);
307
308 Ok(event_time + offset)
309 }
310
311 ZmanPrimitive::HalfDayBasedOffset(event1, event2, hours) => {
312 let event1_time = event1.calculate(calculator)?;
313 let event2_time = event2.calculate(calculator)?;
314 let shaah_zmanis = event2_time.duration_since(event1_time) / 6;
315 let offset = shaah_zmanis.mul_f64(hours);
316 if hours >= 0.0 {
317 Ok(event1_time + offset)
318 } else {
319 Ok(event2_time + offset)
320 }
321 }
322 ZmanPrimitive::Shema(event1, event2, synchronous) => {
323 let event1_time = event1.calculate(calculator)?;
324 let event2_time = event2.calculate(calculator);
325 if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
326 let chatzos = calculator.calculate(&ZmanPrimitive::ChatzosHayom)?;
327 let shaah_zmanis = chatzos.duration_since(event1_time) / 6;
328 let offset = shaah_zmanis.mul_f64(3.0);
329 Ok(event1_time + offset)
330 } else {
331 let event2_time = event2_time?;
332 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
333 let offset = shaah_zmanis.mul_f64(3.0);
334 Ok(event1_time + offset)
335 }
336 }
337 ZmanPrimitive::MinchaGedola(event1, event2, synchronous) => {
338 let event1_time = event1.calculate(calculator);
339 let event2_time = event2.calculate(calculator)?;
340 if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
341 let chatzos = calculator.calculate(&ZmanPrimitive::ChatzosHayom)?;
342 let shaah_zmanis = event2_time.duration_since(chatzos) / 6;
343 let offset = shaah_zmanis.mul_f64(0.5);
344 Ok(chatzos + offset)
345 } else {
346 let event1_time = event1_time?;
347 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
348 let offset = shaah_zmanis.mul_f64(6.5);
349 Ok(event1_time + offset)
350 }
351 }
352 ZmanPrimitive::SamuchLeMinchaKetana(event1, event2, synchronous) => {
353 let event1_time = event1.calculate(calculator);
354 let event2_time = event2.calculate(calculator)?;
355 if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
356 let chatzos = calculator.calculate(&ZmanPrimitive::ChatzosHayom)?;
357 let shaah_zmanis = event2_time.duration_since(chatzos) / 6;
358 let offset = shaah_zmanis.mul_f64(3.0);
359 Ok(chatzos + offset)
360 } else {
361 let event1_time = event1_time?;
362 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
363 let offset = shaah_zmanis.mul_f64(9.0);
364 Ok(event1_time + offset)
365 }
366 }
367 ZmanPrimitive::MinchaKetana(event1, event2, synchronous) => {
368 let event1_time = event1.calculate(calculator);
369 let event2_time = event2.calculate(calculator)?;
370 if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
371 let chatzos = calculator.calculate(&ZmanPrimitive::ChatzosHayom)?;
372 let shaah_zmanis = event2_time.duration_since(chatzos) / 6;
373 let offset = shaah_zmanis.mul_f64(3.5);
374 Ok(chatzos + offset)
375 } else {
376 let event1_time = event1_time?;
377 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
378 let offset = shaah_zmanis.mul_f64(9.5);
379 Ok(event1_time + offset)
380 }
381 }
382 ZmanPrimitive::Tefila(event1, event2, synchronous) => {
383 let event1_time = event1.calculate(calculator)?;
384 let event2_time = event2.calculate(calculator);
385 if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
386 let chatzos = calculator.calculate(&ZmanPrimitive::ChatzosHayom)?;
387 let shaah_zmanis = chatzos.duration_since(event1_time) / 6;
388 let offset = shaah_zmanis.mul_f64(4.0);
389 Ok(event1_time + offset)
390 } else {
391 let event2_time = event2_time?;
392 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
393 let offset = shaah_zmanis.mul_f64(4.0);
394 Ok(event1_time + offset)
395 }
396 }
397 ZmanPrimitive::PlagHamincha(event1, event2, synchronous) => {
398 let event1_time = event1.calculate(calculator);
399 let event2_time = event2.calculate(calculator)?;
400 if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
401 let chatzos = calculator.calculate(&ZmanPrimitive::ChatzosHayom)?;
402 let shaah_zmanis = event2_time.duration_since(chatzos) / 6;
403 let offset = shaah_zmanis.mul_f64(4.75);
404 Ok(chatzos + offset)
405 } else {
406 let event1_time = event1_time?;
407 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
408 let offset = shaah_zmanis.mul_f64(10.75);
409 Ok(event1_time + offset)
410 }
411 }
412 ZmanPrimitive::SofZmanBiurChametz(event1, event2, synchronous) => {
413 if !calculator.date.holidays(false, false).any(|h| h == Holiday::ErevPesach) {
414 return Err(ZmanimError::InvalidForDate);
415 }
416 let primitive = if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
417 ZmanPrimitive::HalfDayBasedOffset(event1, &ZmanPrimitive::ChatzosHayom, 5.0)
418 } else {
419 let event1_time = event1.calculate(calculator)?;
420 let event2_time = event2.calculate(calculator)?;
421 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
422 let offset = shaah_zmanis.mul_f64(5.0);
423 return Ok(event1_time + offset);
424 };
425 primitive.calculate(calculator)
426 }
427 ZmanPrimitive::SofZmanAchilasChametz(event1, event2, synchronous) => {
428 if !calculator.date.holidays(false, false).any(|h| h == Holiday::ErevPesach) {
429 return Err(ZmanimError::InvalidForDate);
430 }
431 let event1_time = event1.calculate(calculator)?;
432 let event2_time = event2.calculate(calculator);
433 if calculator.config.use_astronomical_chatzos_for_other_zmanim && synchronous {
434 let chatzos = calculator.calculate(&ZmanPrimitive::ChatzosHayom)?;
435 let shaah_zmanis = chatzos.duration_since(event1_time) / 6;
436 let offset = shaah_zmanis.mul_f64(4.0);
437 Ok(event1_time + offset)
438 } else {
439 let event2_time = event2_time?;
440 let shaah_zmanis = event2_time.duration_since(event1_time) / 12;
441 let offset = shaah_zmanis.mul_f64(4.0);
442 Ok(event1_time + offset)
443 }
444 }
445 ZmanPrimitive::TzaisAteretTorah => {
446 let sunset = ZmanPrimitive::ConfiguredSunset.calculate(calculator)?;
447 Ok(sunset + calculator.config.ateret_torah_sunset_offset)
448 }
449 ZmanPrimitive::SofZmanKidushLevana15Days => {
450 let tz = calculator
451 .location
452 .timezone
453 .as_ref()
454 .ok_or(ZmanimError::TimeZoneRequired)?;
455 let hebrew = calculator.date.hebrew_date();
456
457 if hebrew.day_of_month().0 < 11 || hebrew.day_of_month().0 > 17 {
458 return Err(ZmanimError::InvalidForDate);
459 }
460 let molad = months_molad(&hebrew)? + Duration::from_hours(24 * 15);
461 if is_same_gregorian_day(&hebrew, &molad, tz) {
462 return Ok(molad);
463 }
464 Err(ZmanimError::InvalidForDate)
465 }
466 ZmanPrimitive::SofZmanKidushLevanaBetweenMoldos => {
467 let tz = calculator
468 .location
469 .timezone
470 .as_ref()
471 .ok_or(ZmanimError::TimeZoneRequired)?;
472 let hebrew = calculator.date.hebrew_date();
473
474 if hebrew.day_of_month().0 < 11 || hebrew.day_of_month().0 > 16 {
475 return Err(ZmanimError::InvalidForDate);
476 }
477 let molad = months_molad(&hebrew)?
478 + Duration::from_hours(24 * 14 + 18)
479 + Duration::from_mins(22)
480 + Duration::from_secs(1)
481 + Duration::from_millis(666);
482 if is_same_gregorian_day(&hebrew, &molad, tz) {
483 return Ok(molad);
484 }
485 Err(ZmanimError::InvalidForDate)
486 }
487 ZmanPrimitive::TchilasZmanKidushLevana3Days => {
488 let tz = calculator
489 .location
490 .timezone
491 .as_ref()
492 .ok_or(ZmanimError::TimeZoneRequired)?;
493 let hebrew = calculator.date.hebrew_date();
494
495 if hebrew.day_of_month().0 > 5 && hebrew.day_of_month().0 < 30 {
496 return Err(ZmanimError::InvalidForDate);
497 }
498 let molad = months_molad(&hebrew)? + Duration::from_hours(72);
499
500 if is_same_gregorian_day(&hebrew, &molad, tz) {
501 return Ok(molad);
502 }
503
504 if hebrew.day_of_month().0 == 30 {
505 let mut add_option = DateAddOptions::default();
506 add_option.overflow = Some(Overflow::Constrain);
507
508 let new = hebrew
509 .try_added_with_options(DateDuration::for_months(1), add_option)
510 .map_err(|_| ZmanimError::TimeConversionError)?;
511 let molad = months_molad(&new)? + Duration::from_hours(72);
512 if is_same_gregorian_day(&hebrew, &molad, tz) {
513 return Ok(molad);
514 }
515 }
516 Err(ZmanimError::InvalidForDate)
517 }
518 ZmanPrimitive::TchilasZmanKidushLevana7Days => {
519 let tz = calculator
520 .location
521 .timezone
522 .as_ref()
523 .ok_or(ZmanimError::TimeZoneRequired)?;
524 let hebrew = calculator.date.hebrew_date();
525
526 if hebrew.day_of_month().0 < 4 || hebrew.day_of_month().0 > 9 {
527 return Err(ZmanimError::InvalidForDate);
528 }
529 let molad = months_molad(&hebrew)? + Duration::from_hours(168);
530 if is_same_gregorian_day(&hebrew, &molad, tz) {
531 return Ok(molad);
532 }
533 Err(ZmanimError::InvalidForDate)
534 }
535 ZmanPrimitive::BainHashmashosRt2Stars => {
536 let alos19_point_8 = ZmanPrimitive::SunriseOffsetByDegrees(19.8).calculate(calculator)?;
537 let sunrise = ZmanPrimitive::ConfiguredSunrise.calculate(calculator)?;
538 let sunset = ZmanPrimitive::ConfiguredSunset.calculate(calculator)?;
539 let time_diff = sunrise.duration_since(alos19_point_8);
540 let offset = time_diff.as_millis() as f64 * (5.0 / 18.0);
541 Ok(sunset + Duration::from_millis(offset as i64))
542 }
543 ZmanPrimitive::MinchaGedolaAhavatShalom => {
544 let chatzos = ZmanPrimitive::ChatzosHayom.calculate(calculator)?;
545 let mincha_gedola_30 = chatzos + Duration::from_mins(30);
546
547 let alos = ZmanPrimitive::SunriseOffsetByDegrees(16.1).calculate(calculator)?;
548 let tzais = ZmanPrimitive::SunsetOffsetByDegrees(3.7).calculate(calculator)?;
549 let shaah_zmanis = tzais.duration_since(alos) / 12;
550 let mincha_alternative = chatzos + (shaah_zmanis / 2);
551 if mincha_gedola_30 > mincha_alternative {
552 Ok(mincha_gedola_30)
553 } else {
554 Ok(mincha_alternative)
555 }
556 }
557 ZmanPrimitive::MinchaGedolaGraGreaterThan30 => {
558 let mincha_gedola_30 = ZmanPrimitive::Offset(&ZmanPrimitive::ChatzosHayom, Duration::from_mins(30))
559 .calculate(calculator)?;
560 let mincha_gedola_gra = ZmanPrimitive::MinchaGedola(
561 &ZmanPrimitive::ConfiguredSunrise,
562 &ZmanPrimitive::ConfiguredSunset,
563 true,
564 )
565 .calculate(calculator)?;
566 Ok(mincha_gedola_30.max(mincha_gedola_gra))
567 }
568 ZmanPrimitive::MinchaKetanaAhavatShalom => {
569 let tzais = ZmanPrimitive::SunsetOffsetByDegrees(3.8).calculate(calculator)?;
570 let alos = ZmanPrimitive::SunriseOffsetByDegrees(16.1).calculate(calculator)?;
571 let shaah_zmanis = tzais.duration_since(alos) / 12;
572 Ok(tzais - (shaah_zmanis * 5 / 2))
573 }
574 ZmanPrimitive::PlagAhavatShalom => {
575 let tzais = ZmanPrimitive::SunsetOffsetByDegrees(3.8).calculate(calculator)?;
576 let alos = ZmanPrimitive::SunriseOffsetByDegrees(16.1).calculate(calculator)?;
577 let shaah_zmanis = tzais.duration_since(alos) / 12;
578 Ok(tzais - (shaah_zmanis * 5 / 4))
579 }
580 ZmanPrimitive::BeginCivilTwilight => {
581 calculator.calculate(&ZmanPrimitive::SunriseOffsetByDegrees(CIVIL_ZENITH))
582 }
583 ZmanPrimitive::EndCivilTwilight => {
584 calculator.calculate(&ZmanPrimitive::SunsetOffsetByDegrees(CIVIL_ZENITH))
585 }
586 ZmanPrimitive::BeginNauticalTwilight => {
587 calculator.calculate(&ZmanPrimitive::SunriseOffsetByDegrees(NAUTICAL_ZENITH))
588 }
589 ZmanPrimitive::EndNauticalTwilight => {
590 calculator.calculate(&ZmanPrimitive::SunsetOffsetByDegrees(NAUTICAL_ZENITH))
591 }
592 ZmanPrimitive::BeginAstronomicalTwilight => {
593 calculator.calculate(&ZmanPrimitive::SunriseOffsetByDegrees(ASTRONOMICAL_ZENITH))
594 }
595 ZmanPrimitive::EndAstronomicalTwilight => {
596 calculator.calculate(&ZmanPrimitive::SunsetOffsetByDegrees(ASTRONOMICAL_ZENITH))
597 }
598 ZmanPrimitive::SunsetOrWesternmostSolarAzimuth => {
599 match ZmanPrimitive::ConfiguredSunset.calculate(calculator) {
600 Ok(sunset) => Ok(sunset),
601 Err(ZmanimError::AllDay | ZmanimError::AllNight) => {
602 crate::zmanim::astronomy::time_at_azimuth(calculator.date, &calculator.location, 270.0)
603 }
604 Err(error) => Err(error),
605 }
606 }
607 ZmanPrimitive::SunriseOrEasternmostSolarAzimuth => {
608 match ZmanPrimitive::ConfiguredSunrise.calculate(calculator) {
609 Ok(sunrise) => Ok(sunrise),
610 Err(ZmanimError::AllDay | ZmanimError::AllNight) => {
611 crate::zmanim::astronomy::time_at_azimuth(calculator.date, &calculator.location, 90.0)
612 }
613 Err(error) => Err(error),
614 }
615 }
616 ZmanPrimitive::ChatzosHayomAsHalfDay => {
617 let sunrise = ZmanPrimitive::SeaLevelSunrise.calculate(calculator)?;
618 let sunset = ZmanPrimitive::SeaLevelSunset.calculate(calculator)?;
619 let diff = sunset.duration_since(sunrise) / 2;
620 Ok(sunrise + diff)
621 }
622 ZmanPrimitive::ChatzosHayom => {
623 if calculator.config.use_astronomical_chatzos {
624 ZmanPrimitive::SolarTransit.calculate(calculator)
625 } else {
626 match ZmanPrimitive::ChatzosHayomAsHalfDay.calculate(calculator) {
627 Ok(chatzos) => Ok(chatzos),
628 Err(_) => ZmanPrimitive::SolarTransit.calculate(calculator),
629 }
630 }
631 }
632 ZmanPrimitive::ChatzosHalaylaAsHalfDay => {
633 let sunset = ZmanPrimitive::SeaLevelSunset.calculate(calculator)?;
634 let mut tomorrow = calculator.clone();
636 tomorrow.date = tomorrow
637 .date
638 .checked_add(1.day())
639 .map_err(|_| ZmanimError::TimeConversionError)?;
640 let sunrise = ZmanPrimitive::SeaLevelSunrise.calculate(&tomorrow)?;
641 let diff = sunrise.duration_since(sunset) / 2;
642 Ok(sunset + diff)
643 }
644 ZmanPrimitive::ChatzosHalayla => {
645 if calculator.config.use_astronomical_chatzos {
646 ZmanPrimitive::SolarMidnight.calculate(calculator)
647 } else {
648 match ZmanPrimitive::ChatzosHalaylaAsHalfDay.calculate(calculator) {
649 Ok(chatzos) => Ok(chatzos),
650 Err(_) => ZmanPrimitive::SolarMidnight.calculate(calculator),
651 }
652 }
653 }
654 }
655 }
656}
657
658#[cfg(feature = "alloc")]
659impl ZmanPrimitive {
661 pub(crate) fn uses_astronomical_chatzos_for_other_zmanim_from_config(&self) -> bool {
662 match self {
663 ZmanPrimitive::Shema(_, _, synchronous)
664 | ZmanPrimitive::MinchaGedola(_, _, synchronous)
665 | ZmanPrimitive::SamuchLeMinchaKetana(_, _, synchronous)
666 | ZmanPrimitive::MinchaKetana(_, _, synchronous)
667 | ZmanPrimitive::Tefila(_, _, synchronous)
668 | ZmanPrimitive::PlagHamincha(_, _, synchronous)
669 | ZmanPrimitive::SofZmanBiurChametz(_, _, synchronous)
670 | ZmanPrimitive::SofZmanAchilasChametz(_, _, synchronous) => *synchronous,
671 ZmanPrimitive::MinchaGedolaGraGreaterThan30 => true,
672 ZmanPrimitive::ElevationAdjustedSunrise
673 | ZmanPrimitive::SeaLevelSunrise
674 | ZmanPrimitive::ConfiguredSunrise
675 | ZmanPrimitive::ConfiguredSunset
676 | ZmanPrimitive::SolarTransit
677 | ZmanPrimitive::SolarMidnight
678 | ZmanPrimitive::ChatzosHayomAsHalfDay
679 | ZmanPrimitive::ChatzosHayom
680 | ZmanPrimitive::ChatzosHalaylaAsHalfDay
681 | ZmanPrimitive::ChatzosHalayla
682 | ZmanPrimitive::ElevationAdjustedSunset
683 | ZmanPrimitive::SeaLevelSunset
684 | ZmanPrimitive::SunriseOffsetByDegrees(_)
685 | ZmanPrimitive::SunsetOffsetByDegrees(_)
686 | ZmanPrimitive::LocalMeanTime(_)
687 | ZmanPrimitive::CandleLighting
688 | ZmanPrimitive::Offset(_, _)
689 | ZmanPrimitive::ZmanisOffset(_, _)
690 | ZmanPrimitive::HalfDayBasedOffset(_, _, _)
691 | ZmanPrimitive::TzaisAteretTorah
692 | ZmanPrimitive::BainHashmashosRt2Stars
693 | ZmanPrimitive::SofZmanKidushLevana15Days
694 | ZmanPrimitive::SofZmanKidushLevanaBetweenMoldos
695 | ZmanPrimitive::TchilasZmanKidushLevana3Days
696 | ZmanPrimitive::TchilasZmanKidushLevana7Days
697 | ZmanPrimitive::MinchaGedolaAhavatShalom
698 | ZmanPrimitive::MinchaKetanaAhavatShalom
699 | ZmanPrimitive::PlagAhavatShalom
700 | ZmanPrimitive::BeginCivilTwilight
701 | ZmanPrimitive::EndCivilTwilight
702 | ZmanPrimitive::BeginNauticalTwilight
703 | ZmanPrimitive::EndNauticalTwilight
704 | ZmanPrimitive::BeginAstronomicalTwilight
705 | ZmanPrimitive::EndAstronomicalTwilight
706 | ZmanPrimitive::SunsetOrWesternmostSolarAzimuth
707 | ZmanPrimitive::SunriseOrEasternmostSolarAzimuth => false,
708 }
709 }
710
711 pub(crate) fn uses_astronomical_chatzos_from_config(&self) -> bool {
712 match self {
713 ZmanPrimitive::ChatzosHayom | ZmanPrimitive::ChatzosHalayla => true,
714 ZmanPrimitive::Offset(event, _) => event.uses_astronomical_chatzos_from_config(),
715 ZmanPrimitive::ElevationAdjustedSunrise
716 | ZmanPrimitive::SeaLevelSunrise
717 | ZmanPrimitive::ConfiguredSunrise
718 | ZmanPrimitive::ConfiguredSunset
719 | ZmanPrimitive::SolarTransit
720 | ZmanPrimitive::SolarMidnight
721 | ZmanPrimitive::ChatzosHayomAsHalfDay
722 | ZmanPrimitive::ChatzosHalaylaAsHalfDay
723 | ZmanPrimitive::ElevationAdjustedSunset
724 | ZmanPrimitive::SeaLevelSunset
725 | ZmanPrimitive::SunriseOffsetByDegrees(_)
726 | ZmanPrimitive::SunsetOffsetByDegrees(_)
727 | ZmanPrimitive::LocalMeanTime(_)
728 | ZmanPrimitive::CandleLighting
729 | ZmanPrimitive::ZmanisOffset(_, _)
730 | ZmanPrimitive::HalfDayBasedOffset(_, _, _)
731 | ZmanPrimitive::Shema(_, _, _)
732 | ZmanPrimitive::MinchaGedola(_, _, _)
733 | ZmanPrimitive::SamuchLeMinchaKetana(_, _, _)
734 | ZmanPrimitive::MinchaKetana(_, _, _)
735 | ZmanPrimitive::Tefila(_, _, _)
736 | ZmanPrimitive::PlagHamincha(_, _, _)
737 | ZmanPrimitive::SofZmanBiurChametz(_, _, _)
738 | ZmanPrimitive::SofZmanAchilasChametz(_, _, _)
739 | ZmanPrimitive::TzaisAteretTorah
740 | ZmanPrimitive::BainHashmashosRt2Stars
741 | ZmanPrimitive::MinchaGedolaGraGreaterThan30
742 | ZmanPrimitive::SofZmanKidushLevana15Days
743 | ZmanPrimitive::SofZmanKidushLevanaBetweenMoldos
744 | ZmanPrimitive::TchilasZmanKidushLevana3Days
745 | ZmanPrimitive::TchilasZmanKidushLevana7Days
746 | ZmanPrimitive::MinchaGedolaAhavatShalom
747 | ZmanPrimitive::MinchaKetanaAhavatShalom
748 | ZmanPrimitive::PlagAhavatShalom
749 | ZmanPrimitive::BeginCivilTwilight
750 | ZmanPrimitive::EndCivilTwilight
751 | ZmanPrimitive::BeginNauticalTwilight
752 | ZmanPrimitive::EndNauticalTwilight
753 | ZmanPrimitive::BeginAstronomicalTwilight
754 | ZmanPrimitive::EndAstronomicalTwilight
755 | ZmanPrimitive::SunsetOrWesternmostSolarAzimuth
756 | ZmanPrimitive::SunriseOrEasternmostSolarAzimuth => false,
757 }
758 }
759
760 pub(crate) fn uses_elevation_from_config(&self) -> bool {
761 match self {
762 ZmanPrimitive::ConfiguredSunrise | ZmanPrimitive::ConfiguredSunset => true,
763 ZmanPrimitive::Offset(event, _) => event.uses_elevation_from_config(),
764 ZmanPrimitive::ZmanisOffset(_, _) => true,
765 ZmanPrimitive::HalfDayBasedOffset(start, end, _)
766 | ZmanPrimitive::Shema(start, end, _)
767 | ZmanPrimitive::MinchaGedola(start, end, _)
768 | ZmanPrimitive::SamuchLeMinchaKetana(start, end, _)
769 | ZmanPrimitive::MinchaKetana(start, end, _)
770 | ZmanPrimitive::Tefila(start, end, _)
771 | ZmanPrimitive::PlagHamincha(start, end, _)
772 | ZmanPrimitive::SofZmanBiurChametz(start, end, _)
773 | ZmanPrimitive::SofZmanAchilasChametz(start, end, _) => {
774 start.uses_elevation_from_config() || end.uses_elevation_from_config()
775 }
776 ZmanPrimitive::TzaisAteretTorah
777 | ZmanPrimitive::BainHashmashosRt2Stars
778 | ZmanPrimitive::MinchaGedolaGraGreaterThan30
779 | ZmanPrimitive::SunsetOrWesternmostSolarAzimuth
780 | ZmanPrimitive::SunriseOrEasternmostSolarAzimuth => true,
781 ZmanPrimitive::ElevationAdjustedSunrise
782 | ZmanPrimitive::SeaLevelSunrise
783 | ZmanPrimitive::SolarTransit
784 | ZmanPrimitive::SolarMidnight
785 | ZmanPrimitive::ChatzosHayomAsHalfDay
786 | ZmanPrimitive::ChatzosHayom
787 | ZmanPrimitive::ChatzosHalaylaAsHalfDay
788 | ZmanPrimitive::ChatzosHalayla
789 | ZmanPrimitive::ElevationAdjustedSunset
790 | ZmanPrimitive::SeaLevelSunset
791 | ZmanPrimitive::SunriseOffsetByDegrees(_)
792 | ZmanPrimitive::SunsetOffsetByDegrees(_)
793 | ZmanPrimitive::LocalMeanTime(_)
794 | ZmanPrimitive::CandleLighting
795 | ZmanPrimitive::SofZmanKidushLevana15Days
796 | ZmanPrimitive::SofZmanKidushLevanaBetweenMoldos
797 | ZmanPrimitive::TchilasZmanKidushLevana3Days
798 | ZmanPrimitive::TchilasZmanKidushLevana7Days
799 | ZmanPrimitive::MinchaGedolaAhavatShalom
800 | ZmanPrimitive::MinchaKetanaAhavatShalom
801 | ZmanPrimitive::PlagAhavatShalom
802 | ZmanPrimitive::BeginCivilTwilight
803 | ZmanPrimitive::EndCivilTwilight
804 | ZmanPrimitive::BeginNauticalTwilight
805 | ZmanPrimitive::EndNauticalTwilight
806 | ZmanPrimitive::BeginAstronomicalTwilight
807 | ZmanPrimitive::EndAstronomicalTwilight => false,
808 }
809 }
810}