From 2414ef6b76533d9330ae0090e537e13298a2e1a9 Mon Sep 17 00:00:00 2001 From: Farhan Ahmed Date: Tue, 12 Jan 2021 13:42:35 -0500 Subject: [PATCH] Fixing the issue with the wrong prayer times This was causing the prayer times to be unstable and provide the wrong times for the prayers. The issue was originally reported by @azzamsa and @RaghibHasin. Closes #6 --- Cargo.toml | 2 +- README.md | 2 +- src/astronomy/qiblah.rs | 8 ++++ src/astronomy/solar.rs | 23 +++++---- src/lib.rs | 103 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 125 insertions(+), 13 deletions(-) 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), + } + } }