diff --git a/Cargo.toml b/Cargo.toml index db1192b..a9b6abd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "salah" -version = "0.4.2" +version = "0.5.0" authors = ["Farhan Ahmed "] edition = "2018" description = "Islamic prayer time library for Rust" diff --git a/README.md b/README.md index 6d91fe8..75e6b1f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Add the following to your `Cargo.toml` file under the `[dependencies]` section: ``` [dependencies] -salah = "0.4.2" +salah = "0.5.0" ``` To get prayer times, use the `PrayerSchedule` struct passing in coordinates, date, and calculation parameters. diff --git a/src/astronomy/qiblah.rs b/src/astronomy/qiblah.rs index b071086..d3dcf0f 100644 --- a/src/astronomy/qiblah.rs +++ b/src/astronomy/qiblah.rs @@ -120,4 +120,12 @@ mod tests { assert_eq!(qiblah.0, 293.02072441441163); } + + #[test] + fn qiblah_direction_from_jakarta_indonesia() { + let jakarta = Coordinates::new(-6.18233995, 106.84287154); + let qiblah = Qiblah::new(jakarta); + + assert_that!(qiblah.0).is_close_to(295.1442983825265, 0.0000001f64); + } } diff --git a/src/astronomy/solar.rs b/src/astronomy/solar.rs index 7b3e3a6..5d9310e 100644 --- a/src/astronomy/solar.rs +++ b/src/astronomy/solar.rs @@ -194,14 +194,8 @@ impl SolarTime { let calculated_seconds = ((value - (calculated_hours + calculated_minutes / 60.0)) * 60.0 * 60.0).floor(); - // Adjust the hour to be within 0..=23, - // wrapping around as needed; otherwise - // chrono method will panic. - let (adjusted_hour, adjusted_date) = if calculated_hours >= 24.0 { - ((calculated_hours - 24.0) as u32, date.tomorrow()) - } else { - (calculated_hours as u32, date.clone()) - }; + let (adjusted_hour, adjusted_date) = + SolarTime::hour_adjustment(calculated_hours, &date); // Round to the nearest minute let adjusted_mins = (calculated_minutes + calculated_seconds / 60.0).round() as u32; @@ -222,6 +216,19 @@ impl SolarTime { adjusted_time } + + fn hour_adjustment(calculated_hours: f64, date: &DateTime) -> (u32, DateTime) { + // Adjust the hour to be within 0..=23, + // wrapping around as needed; otherwise + // chrono method will panic. + if calculated_hours < 0.0 { + ((calculated_hours + 24.0) as u32, date.yesterday()) + } else if calculated_hours >= 24.0 { + ((calculated_hours - 24.0) as u32, date.tomorrow()) + } else { + (calculated_hours as u32, date.clone()) + } + } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index dca4388..4c96140 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,9 +39,9 @@ pub use chrono::{Date, DateTime, Datelike, Duration, Local, TimeZone, Timelike, /// A convenience module appropriate for glob imports (`use salah::prelude::*;`). pub mod prelude { #[doc(no_inline)] - pub use crate::astronomy::unit::{Coordinates, Stride}; + pub use crate::astronomy::qiblah::Qiblah; #[doc(no_inline)] - pub use crate::astronomy::qiblah::Qiblah; + pub use crate::astronomy::unit::{Coordinates, Stride}; #[doc(no_inline)] pub use crate::models::adjustments::{Adjustment, TimeAdjustment}; #[doc(no_inline)] @@ -61,6 +61,8 @@ pub mod prelude { #[cfg(test)] mod tests { use super::*; + use crate::models::high_altitude_rule::HighLatitudeRule; + use chrono::prelude::*; #[test] fn calculate_prayer_times() { @@ -168,7 +170,6 @@ mod tests { #[test] fn calculate_qiyam_times() { let date = Utc.ymd(2015, 7, 12); - // let qiyam_date = Utc.ymd(2015, 7, 13); let params = Configuration::with(Method::NorthAmerica, Madhab::Hanafi); let coordinates = Coordinates::new(35.7750, -78.6336); let result = PrayerSchedule::new() @@ -198,4 +199,100 @@ mod tests { Err(_err) => assert!(false), } } + + #[test] + fn calculate_times_for_singapore() { + let mut params = Configuration::with(Method::Singapore, Madhab::Shafi); + + params.high_latitude_rule = HighLatitudeRule::MiddleOfTheNight; + + // The adjustment below are based on the prayer times that are provided + // on the website (http://www.muis.gov.sg). I don't know the exact + // calculation that this site is using for the prayer times; therefore the + // use of time adjustment. However, these are within the 2 minute variance. + params.method_adjustments = Adjustment::new() + .fajr(1) + .sunrise(1) + .dhuhr(2) + .asr(1) + .maghrib(1) + .isha(1) + .done(); + + let result = PrayerSchedule::new() + .on(Utc.ymd(2021, 1, 13)) + .for_location(Coordinates::new(1.370844612058886, 103.80145644060552)) + .with_configuration(params) + .calculate(); + + match result { + Ok(schedule) => { + let hour = 3600; + let sgt_offset = FixedOffset::east(8 * hour); + let sgt_fajr = schedule.time(Prayer::Fajr).with_timezone(&sgt_offset); + let sgt_sunrise = schedule.time(Prayer::Sunrise).with_timezone(&sgt_offset); + let sgt_dhuhr = schedule.time(Prayer::Dhuhr).with_timezone(&sgt_offset); + let sgt_asr = schedule.time(Prayer::Asr).with_timezone(&sgt_offset); + let sgt_maghrib = schedule.time(Prayer::Maghrib).with_timezone(&sgt_offset); + let sgt_isha = schedule.time(Prayer::Isha).with_timezone(&sgt_offset); + + assert_eq!(sgt_fajr.format("%-l:%M %p").to_string(), "5:50 AM"); + assert_eq!(sgt_sunrise.format("%-l:%M %p").to_string(), "7:13 AM"); + assert_eq!(sgt_dhuhr.format("%-l:%M %p").to_string(), "1:15 PM"); + assert_eq!(sgt_asr.format("%-l:%M %p").to_string(), "4:39 PM"); + assert_eq!(sgt_maghrib.format("%-l:%M %p").to_string(), "7:16 PM"); + assert_eq!(sgt_isha.format("%-l:%M %p").to_string(), "8:30 PM"); + } + Err(_err) => assert!(false), + } + } + + #[test] + fn calculate_times_for_jakarta() { + let mut params = Configuration::with(Method::Egyptian, Madhab::Shafi); + + // The adjustment below are based on the prayer times that are provided + // on the website (https://www.jadwalsholat.org/). I don't know the exact + // calculation that this site is using for the prayer times; therefore the + // use of time adjustment. + // + // It would be a good idea to get some more information on how the Fajr + // and Isha are calculated, since that's where the biggest variance is; + // however, the other times are within the 2 minute variance. + params.method_adjustments = Adjustment::new() + .fajr(-10) + .sunrise(-2) + .dhuhr(2) + .asr(1) + .maghrib(2) + .isha(4) + .done(); + + let result = PrayerSchedule::new() + .on(Utc.ymd(2021, 1, 12)) + .for_location(Coordinates::new(-6.18233995, 106.84287154)) + .with_configuration(params) + .calculate(); + + match result { + Ok(schedule) => { + let hour = 3600; + let wib_offset = FixedOffset::east(7 * hour); + let wib_fajr = schedule.time(Prayer::Fajr).with_timezone(&wib_offset); + let wib_sunrise = schedule.time(Prayer::Sunrise).with_timezone(&wib_offset); + let wib_dhuhr = schedule.time(Prayer::Dhuhr).with_timezone(&wib_offset); + let wib_asr = schedule.time(Prayer::Asr).with_timezone(&wib_offset); + let wib_maghrib = schedule.time(Prayer::Maghrib).with_timezone(&wib_offset); + let wib_isha = schedule.time(Prayer::Isha).with_timezone(&wib_offset); + + assert_eq!(wib_fajr.format("%-l:%M %p").to_string(), "4:15 AM"); + assert_eq!(wib_sunrise.format("%-l:%M %p").to_string(), "5:45 AM"); + assert_eq!(wib_dhuhr.format("%-l:%M %p").to_string(), "12:03 PM"); + assert_eq!(wib_asr.format("%-l:%M %p").to_string(), "3:28 PM"); + assert_eq!(wib_maghrib.format("%-l:%M %p").to_string(), "6:16 PM"); + assert_eq!(wib_isha.format("%-l:%M %p").to_string(), "7:31 PM"); + } + Err(_err) => assert!(false), + } + } }