use crate::astro;
use crate::datetime;
use crate::math;
use chrono::{NaiveDate, NaiveTime, TimeZone, Utc};
use chrono_tz::Tz;
pub mod types;
use types::{Authority, IshaParam, School, Timing};
pub struct PrayerTimes {
tz: Tz,
lat: f64,
lng: f64,
date: NaiveDate,
tz_offset: f64,
jd: f64,
auth: Authority,
school: School,
}
impl PrayerTimes {
pub fn new(lat: f64, lng: f64) -> PrayerTimes {
const DEFAULT_TZ: &str = "America/Toronto";
let tz: Tz = DEFAULT_TZ.parse().expect("Invalid time zone!");
let default_date = tz.from_utc_datetime(&Utc::now().naive_utc()).date_naive();
return PrayerTimes {
tz,
lat,
lng,
date: default_date,
tz_offset: datetime::tz_offset(tz),
jd: astro::julian(default_date),
auth: Authority::ISNA,
school: School::Hanafi,
};
}
pub fn with_date(mut self, date: &NaiveDate) -> Self {
self.date = *date;
self.jd = astro::julian(*date);
return self;
}
pub fn with_timezone(mut self, tz: &Tz) -> Self {
self.tz = *tz;
self.tz_offset = datetime::tz_offset(*tz);
return self;
}
pub fn with_authority(mut self, auth: &Authority) -> Self {
self.auth = *auth;
return self;
}
pub fn with_school(mut self, school: &School) -> Self {
self.school = *school;
return self;
}
fn zenith(&self) -> f64 {
return astro::zenith(self.jd, self.lng, self.tz_offset);
}
pub fn fajr(&self) -> NaiveTime {
let angle = self.auth.fajr_angle();
let hour = astro::horizon_hour_angle(
angle,
self.jd,
self.zenith(),
self.lat,
astro::HorizonDirection::Sunrise,
);
return datetime::hour2time(hour, true).expect("RangeError @ PrayerTime.fajr");
}
pub fn dhuhr(&self) -> NaiveTime {
return datetime::hour2time(self.zenith(), true).expect("RangeError @ PrayerTime.dhuhr");
}
pub fn asr(&self) -> NaiveTime {
let hour = astro::shadow_length_hour(
self.school.shadow_length(),
self.jd,
self.zenith(),
self.lat,
);
return datetime::hour2time(hour, true).expect("RangeError @ PrayerTime.asr");
}
pub fn maghrib(&self) -> NaiveTime {
let hour = astro::horizon_hour_angle(
0.833,
self.jd,
self.zenith(),
self.lat,
astro::HorizonDirection::Sunset,
);
return datetime::hour2time(hour, true).expect("RangeError @ PrayerTime.maghrib");
}
pub fn isha(&self) -> NaiveTime {
let param = self.auth.isha_param();
return match param {
IshaParam::Angle(a) => {
let hour = astro::horizon_hour_angle(
a,
self.jd,
self.zenith(),
self.lat,
astro::HorizonDirection::Sunset,
);
let time = datetime::hour2time(hour, true).expect("RangeError @ PrayerTime.isha");
time
}
IshaParam::Duration(d) => {
let maghrib = datetime::time2hour(self.maghrib());
let sunset =
datetime::hour2time(maghrib, true).expect("RangeError @ PrayerTime.isha");
sunset + d
}
};
}
pub fn sunrise(&self) -> NaiveTime {
let hour = astro::horizon_hour_angle(
0.833,
self.jd,
self.zenith(),
self.lat,
astro::HorizonDirection::Sunrise,
);
return datetime::hour2time(hour, true).expect("RangeError @ PrayerTime.sunrise");
}
pub fn midnight(&self) -> NaiveTime {
let sunrise = datetime::time2hour(self.sunrise());
let sunset = datetime::time2hour(self.maghrib());
let mid = sunset + math::time::normalize_hour(sunrise - sunset) / 2_f64;
return datetime::hour2time(mid, true).expect("RangeError @ PrayerTime.midnight");
}
pub fn timing(&self, timing: &Timing) -> NaiveTime {
match timing {
Timing::Fajr => self.fajr(),
Timing::Sunrise => self.sunrise(),
Timing::Dhuhr => self.dhuhr(),
Timing::Asr => self.asr(),
Timing::Maghrib => self.maghrib(),
Timing::Isha => self.isha(),
Timing::Midnight => self.midnight(),
}
}
}