├── codecov.yml ├── src ├── Adhan.Test │ ├── TimeZoneConverter │ │ ├── Aliases.csv.gz │ │ ├── Mapping.csv.gz │ │ ├── RailsMapping.csv.gz │ │ ├── DataLoader.cs │ │ └── TZConvert.cs │ ├── Data │ │ ├── TimingInfo.cs │ │ ├── TimingParameters.cs │ │ └── TimingFile.cs │ ├── Internal │ │ ├── ValueTypeExtensions.cs │ │ ├── TestUtils.cs │ │ ├── MathTest.cs │ │ └── AstronomicalTest.cs │ ├── CalculationParametersTest.cs │ ├── Adhan.Test.csproj │ ├── QiblaTest.cs │ ├── CalculationMethodTest.cs │ ├── TimingTest.cs │ └── PrayerTimesTest.cs ├── Adhan │ ├── internal │ │ ├── ShadowLength.cs │ │ ├── EnumExtensions.cs │ │ ├── DoubleUtil.cs │ │ ├── MathHelper.cs │ │ ├── DateComponents.cs │ │ ├── QiblaUtil.cs │ │ ├── TimeComponents.cs │ │ ├── CalendarUtil.cs │ │ ├── DateTimeExtensions.cs │ │ ├── CalendricalHelper.cs │ │ ├── SolarCoordinates.cs │ │ ├── SolorTime.cs │ │ └── Astronomical.cs │ ├── Prayer.cs │ ├── NightPortions.cs │ ├── Madhab.cs │ ├── Qibla.cs │ ├── Adhan.csproj │ ├── Coordinates.cs │ ├── HighLatitudeRule.cs │ ├── PrayerAdjustments.cs │ ├── CalculationMethod.cs │ ├── CalculationParameters.cs │ └── PrayerTimes.cs └── Adhan.Samples │ ├── Adhan.Samples.csproj │ └── Program.cs ├── .travis.yml ├── .vscode ├── tasks.json └── launch.json ├── LICENSE ├── shared └── times │ ├── Kuwait City-Kuwait.json │ ├── Singapore-Singapore.json │ ├── Doha-Qatar.json │ ├── Makkah-UmmAlQura.json │ ├── London-MoonsightingCommittee.json │ └── Dubai-Gulf.json ├── adhan-csharp.sln ├── .gitignore └── README.md /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - src\Adhan.Samples -------------------------------------------------------------------------------- /src/Adhan.Test/TimeZoneConverter/Aliases.csv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidpet86/Adhan-csharp/HEAD/src/Adhan.Test/TimeZoneConverter/Aliases.csv.gz -------------------------------------------------------------------------------- /src/Adhan.Test/TimeZoneConverter/Mapping.csv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidpet86/Adhan-csharp/HEAD/src/Adhan.Test/TimeZoneConverter/Mapping.csv.gz -------------------------------------------------------------------------------- /src/Adhan.Test/TimeZoneConverter/RailsMapping.csv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidpet86/Adhan-csharp/HEAD/src/Adhan.Test/TimeZoneConverter/RailsMapping.csv.gz -------------------------------------------------------------------------------- /src/Adhan/internal/ShadowLength.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public enum ShadowLength 6 | { 7 | Single = 1, 8 | Double = 2 9 | } 10 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | mono: none 3 | dotnet: 2.1.505 4 | solution: adhan-csharp.sln 5 | script: 6 | - dotnet restore 7 | - dotnet test 8 | 9 | after_success: 10 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /src/Adhan/Prayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan 4 | { 5 | public enum Prayer 6 | { 7 | NONE, 8 | FAJR, 9 | SUNRISE, 10 | DHUHR, 11 | ASR, 12 | MAGHRIB, 13 | ISHA, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Adhan.Samples/Adhan.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp2.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Adhan/NightPortions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan 4 | { 5 | public class NightPortions 6 | { 7 | public readonly double Fajr; 8 | public readonly double Isha; 9 | 10 | public NightPortions(double fajr, double isha) 11 | { 12 | this.Fajr = fajr; 13 | this.Isha = isha; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Adhan.Test/Data/TimingInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Adhan.Test.Data 6 | { 7 | public class TimingInfo 8 | { 9 | public string Date; 10 | public string Fajr; 11 | public string Sunrise; 12 | public string Dhuhr; 13 | public string Asr; 14 | public string Maghrib; 15 | public string Isha; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Adhan.Test/Data/TimingParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Adhan.Test.Data 6 | { 7 | public class TimingParameters 8 | { 9 | public double Latitude; 10 | public double Longitude; 11 | public string Timezone; 12 | public string Method; 13 | public string Madhab; 14 | public string HighLatitudeRule; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Adhan/Madhab.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan 4 | { 5 | /// 6 | /// Madhab for determining how Asr is calculated 7 | /// 8 | public enum Madhab 9 | { 10 | /// 11 | /// Shafi Madhab 12 | /// 13 | SHAFI, 14 | 15 | /// 16 | /// Hanafi Madhab 17 | /// 18 | HANAFI 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Adhan/Qibla.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Batoulapps.Adhan.Internal; 3 | 4 | namespace Batoulapps.Adhan 5 | { 6 | public class Qibla 7 | { 8 | private readonly static Coordinates MAKKAH = new Coordinates(21.4225241, 39.8261818); 9 | 10 | public readonly double Direction; 11 | 12 | public Qibla(Coordinates coordinates) 13 | { 14 | Direction = QiblaUtil.CalculateQiblaDirection(coordinates); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Adhan/Adhan.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | full 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Adhan/internal/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public static class EnumExtensions 6 | { 7 | public static ShadowLength GetShadowLength(this Madhab madhab) 8 | { 9 | switch (madhab) 10 | { 11 | case Madhab.SHAFI: 12 | return ShadowLength.Single; 13 | case Madhab.HANAFI: 14 | return ShadowLength.Double; 15 | default: 16 | throw new ArgumentException("Invalid Madhab"); 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Adhan/internal/DoubleUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public class DoubleUtil 6 | { 7 | public static double NormalizeWithBound(double value, double max) 8 | { 9 | return value - (max * (Math.Floor(value / max))); 10 | } 11 | 12 | public static double UnwindAngle(double value) 13 | { 14 | return NormalizeWithBound(value, 360); 15 | } 16 | 17 | public static double ClosestAngle(double angle) 18 | { 19 | if (angle >= -180 && angle <= 180) { 20 | return angle; 21 | } 22 | return angle - (360 * Math.Round(angle / 360)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Adhan/internal/MathHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public class MathHelper 6 | { 7 | public static double ToDegrees(double radians) 8 | { 9 | // This method uses double precission internally, 10 | // though it returns single float 11 | // Factor = 180 / pi 12 | return (double)(radians * 57.295779513082320876798154814105); 13 | } 14 | 15 | public static double ToRadians(double degrees) 16 | { 17 | // This method uses double precission internally, 18 | // though it returns single float 19 | // Factor = pi / 180 20 | return (double)(degrees * 0.017453292519943295769236907684886); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Adhan.Test/Internal/ValueTypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Adhan.Test.Internal 6 | { 7 | public static class ValueTypeExtensions 8 | { 9 | public static bool IsBetween(this double value, double minimum, double maximum) 10 | { 11 | return value >= minimum && value <= maximum; 12 | } 13 | 14 | public static bool IsWithin(this double value, double difference, double of) 15 | { 16 | double min = of - difference; 17 | double max = of + difference; 18 | 19 | return value.IsBetween(min, max); 20 | } 21 | 22 | public static bool IsAtMost(this long value, long atMost) 23 | { 24 | return value <= atMost; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Adhan/Coordinates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan 4 | { 5 | /// 6 | /// Coordinates representing a particular place 7 | /// 8 | public class Coordinates 9 | { 10 | /// 11 | /// Latitude 12 | /// 13 | public readonly double Latitude; 14 | 15 | /// 16 | /// Longitude 17 | /// 18 | public readonly double Longitude; 19 | 20 | /// 21 | /// Generate a new Coordinates object 22 | /// 23 | /// the latitude of the place 24 | /// the longitude of the place 25 | public Coordinates(double latitude, double longitude) 26 | { 27 | this.Latitude = latitude; 28 | this.Longitude = longitude; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Adhan/internal/DateComponents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public class DateComponents 6 | { 7 | public readonly int Year; 8 | public readonly int Month; 9 | public readonly int Day; 10 | 11 | /// 12 | /// Convenience method that returns a DateComponents from a given Date 13 | /// 14 | /// the date 15 | /// the DateComponents (according to the default device timezone) 16 | public static DateComponents From(DateTime date) 17 | { 18 | return new DateComponents(date.Year, date.Month, date.Day); 19 | } 20 | 21 | public DateComponents(int year, int month, int day) 22 | { 23 | this.Year = year; 24 | this.Month = month; 25 | this.Day = day; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Adhan.Test/Data/TimingFile.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace Adhan.Test.Data 8 | { 9 | public class TimingFile 10 | { 11 | [JsonProperty("params")] 12 | public TimingParameters Parameters; 13 | public TimingInfo[] Times; 14 | public long Variance; 15 | 16 | public static TimingFile Load(string inputFile) 17 | { 18 | if (File.Exists(inputFile) == false) 19 | { 20 | return null; 21 | } 22 | 23 | TimingFile timingFile = null; 24 | 25 | using (StreamReader file = File.OpenText(inputFile)) 26 | { 27 | JsonSerializer serializer = new JsonSerializer(); 28 | timingFile = (TimingFile)serializer.Deserialize(file, typeof(TimingFile)); 29 | 30 | } 31 | 32 | return timingFile; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/src/Adhan.Test/Adhan.Test.csproj" 11 | ], 12 | "problemMatcher": "$tsc" 13 | }, 14 | { 15 | "label": "publish", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "publish", 20 | "${workspaceFolder}/src/Adhan.Test/Adhan.Test.csproj" 21 | ], 22 | "problemMatcher": "$tsc" 23 | }, 24 | { 25 | "label": "watch", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "watch", 30 | "run", 31 | "${workspaceFolder}/src/Adhan.Test/Adhan.Test.csproj" 32 | ], 33 | "problemMatcher": "$tsc" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /src/Adhan/internal/QiblaUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Batoulapps.Adhan; 3 | 4 | namespace Batoulapps.Adhan.Internal 5 | { 6 | internal class QiblaUtil 7 | { 8 | private readonly static Coordinates MAKKAH = new Coordinates(21.4225241, 39.8261818); 9 | 10 | internal static double CalculateQiblaDirection(Coordinates coordinates) 11 | { 12 | // Equation from "Spherical Trigonometry For the use of colleges and schools" page 50 13 | double longitudeDelta = 14 | MathHelper.ToRadians(MAKKAH.Longitude) - MathHelper.ToRadians(coordinates.Longitude); 15 | double latitudeRadians = MathHelper.ToRadians(coordinates.Latitude); 16 | double term1 = Math.Sin(longitudeDelta); 17 | double term2 = Math.Cos(latitudeRadians) * Math.Tan( MathHelper.ToRadians(MAKKAH.Latitude)); 18 | double term3 = Math.Sin(latitudeRadians) * Math.Cos(longitudeDelta); 19 | 20 | double angle = Math.Atan2(term1, term2 - term3); 21 | return DoubleUtil.UnwindAngle(MathHelper.ToDegrees(angle)); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Adhan/HighLatitudeRule.cs: -------------------------------------------------------------------------------- 1 | namespace Batoulapps.Adhan 2 | { 3 | /// 4 | /// Rules for dealing with Fajr and Isha at places with high latitudes 5 | /// 6 | public enum HighLatitudeRule 7 | { 8 | /// 9 | /// Fajr will never be earlier than the middle of the night, and Isha will never be later than 10 | /// the middle of the night. 11 | /// 12 | /// 13 | MIDDLE_OF_THE_NIGHT, 14 | 15 | /// 16 | /// Fajr will never be earlier than the beginning of the last seventh of the night, and Isha will 17 | /// never be later than the end of hte first seventh of the night. 18 | /// 19 | /// 20 | SEVENTH_OF_THE_NIGHT, 21 | 22 | /// 23 | /// Similar to {@link HighLatitudeRule#SEVENTH_OF_THE_NIGHT}, but instead of 1/7th, the faction 24 | /// of the night used is fajrAngle / 60 and ishaAngle/60. 25 | /// 26 | /// 27 | TWILIGHT_ANGLE 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 davidpet86 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Adhan/internal/TimeComponents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public class TimeComponents 6 | { 7 | public readonly int Hours; 8 | public readonly int Minutes; 9 | public readonly int Seconds; 10 | 11 | public static TimeComponents FromDouble(double value) 12 | { 13 | if (value == Double.MaxValue || Double.IsNaN(value)) 14 | { 15 | return null; 16 | } 17 | 18 | double hours = Math.Floor(value); 19 | double minutes = Math.Floor((value - hours) * 60.0); 20 | double seconds = Math.Floor((value - (hours + minutes / 60.0)) * 60 * 60); 21 | return new TimeComponents((int) hours, (int) minutes, (int) seconds); 22 | } 23 | 24 | private TimeComponents(int hours, int minutes, int seconds) 25 | { 26 | this.Hours = hours; 27 | this.Minutes = minutes; 28 | this.Seconds = seconds; 29 | } 30 | 31 | public DateTime? DateComponents(DateTime date) 32 | { 33 | return date.Date.AddHours(Hours).AddMinutes(Minutes).AddSeconds(Seconds); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/src/Adhan.Test/bin/Debug/netcoreapp2.1/Adhan.Test.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/src/Adhan.Test", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /src/Adhan/internal/CalendarUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public class CalendarUtil 6 | { 7 | public static bool IsLeapYear(int year) 8 | { 9 | return year % 4 == 0 && !(year % 100 == 0 && year % 400 != 0); 10 | } 11 | 12 | /// 13 | /// Date and time with a rounded minute 14 | /// This returns a date with the seconds rounded and added to the minute 15 | /// 16 | /// the date and time 17 | /// the date and time with 0 seconds and minutes including rounded seconds 18 | public static DateTime RoundedMinute(DateTime when) 19 | { 20 | return new DateTime((when - DateTime.MinValue).Round(TimeSpan.FromMinutes(1)).Ticks); 21 | } 22 | 23 | /// 24 | /// Gets a date for the particular date 25 | /// 26 | /// the date components 27 | /// the date with a time set to 00:00:00 at utc 28 | public static DateTime ResolveTime(DateComponents components) { 29 | return new DateTime(components.Year, components.Month, components.Day, 0, 0, 0, DateTimeKind.Utc); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Adhan.Samples/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | using Batoulapps.Adhan; 5 | using Batoulapps.Adhan.Internal; 6 | 7 | namespace Adhan.Samples 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | Coordinates coordinates = new Coordinates(43.61, -79.70); 14 | DateComponents dateComponents = DateComponents.From(DateTime.Now); 15 | CalculationParameters parameters = CalculationMethod.NORTH_AMERICA.GetParameters(); 16 | 17 | string timeZone = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 18 | "Eastern Standard Time" : "America/New_York"; 19 | 20 | TimeZoneInfo easternTime = TimeZoneInfo.FindSystemTimeZoneById(timeZone); 21 | 22 | PrayerTimes prayerTimes = new PrayerTimes(coordinates, dateComponents, parameters); 23 | Console.WriteLine("Fajr : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Fajr, easternTime)); 24 | Console.WriteLine("Sunrise: " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Sunrise, easternTime)); 25 | Console.WriteLine("Dhuhr : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Dhuhr, easternTime)); 26 | Console.WriteLine("Asr : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Asr, easternTime)); 27 | Console.WriteLine("Maghrib: " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Maghrib, easternTime)); 28 | Console.WriteLine("Isha : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Isha, easternTime)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Adhan.Test/CalculationParametersTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | using Batoulapps.Adhan; 4 | using Batoulapps.Adhan.Internal; 5 | using Adhan.Test.Internal; 6 | 7 | namespace Adhan.Test 8 | { 9 | [TestClass] 10 | public class CalculationParametersTest 11 | { 12 | [TestMethod] 13 | public void NightPortionMiddleOfTheNight() 14 | { 15 | CalculationParameters calcParams = new CalculationParameters(18.0, 18.0); 16 | calcParams.HighLatitudeRule = HighLatitudeRule.MIDDLE_OF_THE_NIGHT; 17 | 18 | Assert.IsTrue(calcParams.NightPortions().Fajr.IsWithin(0.001, 0.5)); 19 | Assert.IsTrue(calcParams.NightPortions().Isha.IsWithin(0.001, 0.5)); 20 | } 21 | 22 | [TestMethod] 23 | public void NightPortionSeventhOfTheNight() 24 | { 25 | CalculationParameters calcParams = new CalculationParameters(18.0, 18.0); 26 | calcParams.HighLatitudeRule = HighLatitudeRule.SEVENTH_OF_THE_NIGHT; 27 | 28 | Assert.IsTrue(calcParams.NightPortions().Fajr.IsWithin(0.001, 1.0 / 7.0)); 29 | Assert.IsTrue(calcParams.NightPortions().Isha.IsWithin(0.001, 1.0 / 7.0)); 30 | } 31 | 32 | [TestMethod] 33 | public void NightPortionTwilightAngle() 34 | { 35 | CalculationParameters calcParams = new CalculationParameters(10.0, 15.0); 36 | calcParams.HighLatitudeRule = HighLatitudeRule.TWILIGHT_ANGLE; 37 | 38 | Assert.IsTrue(calcParams.NightPortions().Fajr.IsWithin(0.001, 10.0 / 60.0)); 39 | Assert.IsTrue(calcParams.NightPortions().Isha.IsWithin(0.001, 15.0 / 60.0)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Adhan/internal/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public static class DateTimeExtensions 6 | { 7 | public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType) 8 | { 9 | return new TimeSpan( 10 | Convert.ToInt64(Math.Round( 11 | time.Ticks / (decimal)roundingInterval.Ticks, 12 | roundingType 13 | )) * roundingInterval.Ticks 14 | ); 15 | } 16 | 17 | public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval) { 18 | return Round(time, roundingInterval, MidpointRounding.ToEven); 19 | } 20 | 21 | public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval) { 22 | return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks); 23 | } 24 | 25 | /// 26 | /// Returns the number of milliseconds since 01/01/1970 27 | /// 28 | /// the date time 29 | /// the number of milliseconds since 01/01/1970 30 | public static double GetTime(this DateTime datetime) 31 | { 32 | //DateTime.MinValue is 01/01/01 00:00 so add 1969 years. to get 1/1/1970 33 | return datetime.Subtract(DateTime.MinValue.AddYears(1969)).TotalMilliseconds; 34 | } 35 | 36 | public static bool Before(this DateTime datetime, DateTime compareDateTime) 37 | { 38 | return datetime < compareDateTime; 39 | } 40 | 41 | public static bool After(this DateTime datetime, DateTime compareDateTime) 42 | { 43 | return datetime > compareDateTime; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Adhan.Test/Adhan.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | full 11 | true 12 | true 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Always 41 | 42 | 43 | Always 44 | 45 | 46 | Always 47 | 48 | 49 | Always 50 | 51 | 52 | Always 53 | 54 | 55 | Always 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/Adhan/PrayerAdjustments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan 4 | { 5 | /// 6 | /// Adjustment value for prayer times, in minutes 7 | /// These values are added (or subtracted) from the prayer time that is calculated before 8 | /// returning the result times. 9 | /// 10 | public class PrayerAdjustments 11 | { 12 | /// 13 | /// Fajr offset in minutes 14 | /// 15 | public int Fajr = 0; 16 | 17 | /// 18 | /// Sunrise offset in minutes 19 | /// 20 | public int Sunrise = 0; 21 | 22 | /// 23 | /// Dhuhr offset in minutes 24 | /// 25 | public int Dhuhr = 0; 26 | 27 | /// 28 | /// Asr offset in minutes 29 | /// 30 | public int Asr = 0; 31 | 32 | /// 33 | /// Maghrib offset in minutes 34 | /// 35 | public int Maghrib = 0; 36 | 37 | /// 38 | /// Isha offset in minutes 39 | /// 40 | public int Isha = 0; 41 | 42 | /// 43 | /// Gets a PrayerAdjustments object with all offsets set to 0 44 | /// 45 | public PrayerAdjustments() 46 | { 47 | } 48 | 49 | /// 50 | /// Gets a PrayerAdjustments object to offset prayer times 51 | /// 52 | /// offset from fajr in minutes 53 | /// offset from sunrise in minutes 54 | /// offset from dhuhr in minutes 55 | /// offset from asr in minutes 56 | /// offset from maghrib in minutes 57 | /// offset from isha in minutes 58 | public PrayerAdjustments(int fajr, int sunrise, int dhuhr, int asr, int maghrib, int isha) { 59 | this.Fajr = fajr; 60 | this.Sunrise = sunrise; 61 | this.Dhuhr = dhuhr; 62 | this.Asr = asr; 63 | this.Maghrib = maghrib; 64 | this.Isha = isha; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/Adhan/internal/CalendricalHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace Batoulapps.Adhan.Internal 5 | { 6 | public class CalendricalHelper 7 | { 8 | /// 9 | /// The Julian Day for a given Gregorian date 10 | /// 11 | /// the year 12 | /// the month 13 | /// the day 14 | /// the julian day 15 | public static double JulianDay(int year, int month, int day) 16 | { 17 | return JulianDay(year, month, day, 0.0); 18 | } 19 | 20 | /// 21 | /// The Julian Day for a given date 22 | /// 23 | /// the date 24 | /// the julian day 25 | public static double JulianDay(DateTime date) 26 | { 27 | DateTime calendar = date.ToUniversalTime(); 28 | return JulianDay(calendar.Year, calendar.Month, 29 | calendar.Day, calendar.Hour + calendar.Minute / 60.0); 30 | } 31 | 32 | /// 33 | /// The Julian Day for a given Gregorian date 34 | /// 35 | /// the year 36 | /// the month 37 | /// the day 38 | /// hours 39 | /// the julian day 40 | public static double JulianDay(int year, int month, int day, double hours) 41 | { 42 | /* Equation from Astronomical Algorithms page 60 */ 43 | 44 | // NOTE: Integer conversion is done intentionally for the purpose of decimal truncation 45 | 46 | int Y = month > 2 ? year : year - 1; 47 | int M = month > 2 ? month : month + 12; 48 | double D = day + (hours / 24); 49 | 50 | int A = Y/100; 51 | int B = 2 - A + (A/4); 52 | 53 | int i0 = (int) (365.25 * (Y + 4716)); 54 | int i1 = (int) (30.6001 * (M + 1)); 55 | return i0 + i1 + D + B - 1524.5; 56 | } 57 | 58 | /// 59 | /// Julian century from the epoch. 60 | /// 61 | /// the julian day 62 | /// the julian century from the epoch 63 | public static double JulianCentury(double JD) 64 | { 65 | /* Equation from Astronomical Algorithms page 163 */ 66 | return (JD - 2451545.0) / 36525; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/Adhan/internal/SolarCoordinates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public class SolarCoordinates 6 | { 7 | /// 8 | /// The declination of the sun, the angle between 9 | /// the rays of the Sun and the plane of the Earth's 10 | /// equator, in degrees. 11 | /// 12 | public readonly double Declination; 13 | 14 | /// 15 | /// Right ascension of the Sun, the angular distance on the 16 | /// celestial equator from the vernal equinox to the hour circle, 17 | /// in degrees. 18 | /// 19 | public readonly double RightAscension; 20 | 21 | /// 22 | /// Apparent sidereal time, the hour angle of the vernal 23 | /// equinox, in degrees. 24 | /// 25 | public readonly double ApparentSiderealTime; 26 | 27 | public SolarCoordinates(double julianDay) 28 | { 29 | double T = CalendricalHelper.JulianCentury(julianDay); 30 | double L0 = Astronomical.MeanSolarLongitude(/* julianCentury */ T); 31 | double Lp = Astronomical.MeanLunarLongitude(/* julianCentury */ T); 32 | double Ω = Astronomical.AscendingLunarNodeLongitude(/* julianCentury */ T); 33 | double λ = MathHelper.ToRadians( 34 | Astronomical.ApparentSolarLongitude(/* julianCentury*/ T, /* meanLongitude */ L0)); 35 | 36 | double θ0 = Astronomical.MeanSiderealTime(/* julianCentury */ T); 37 | double ΔΨ = Astronomical.NutationInLongitude(/* julianCentury */ T, /* solarLongitude */ L0, 38 | /* lunarLongitude */ Lp, /* ascendingNode */ Ω); 39 | double Δε = Astronomical.NutationInObliquity(/* julianCentury */ T, /* solarLongitude */ L0, 40 | /* lunarLongitude */ Lp, /* ascendingNode */ Ω); 41 | 42 | double ε0 = Astronomical.MeanObliquityOfTheEcliptic(/* julianCentury */ T); 43 | double εapp = MathHelper.ToRadians(Astronomical.ApparentObliquityOfTheEcliptic( 44 | /* julianCentury */ T, /* meanObliquityOfTheEcliptic */ ε0)); 45 | 46 | /* Equation from Astronomical Algorithms page 165 */ 47 | this.Declination = MathHelper.ToDegrees(Math.Asin(Math.Sin(εapp) * Math.Sin(λ))); 48 | 49 | /* Equation from Astronomical Algorithms page 165 */ 50 | this.RightAscension = DoubleUtil.UnwindAngle( 51 | MathHelper.ToDegrees(Math.Atan2(Math.Cos(εapp) * Math.Sin(λ), Math.Cos(λ)))); 52 | 53 | /* Equation from Astronomical Algorithms page 88 */ 54 | this.ApparentSiderealTime = θ0 + (((ΔΨ * 3600) * Math.Cos(MathHelper.ToRadians(ε0 + Δε))) / 3600); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Adhan.Test/Internal/TestUtils.cs: -------------------------------------------------------------------------------- 1 | using Batoulapps.Adhan.Internal; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace Adhan.Test.Internal 8 | { 9 | public class TestUtils 10 | { 11 | /// 12 | /// Gets a TimeZoneInfo object based on the current OS Platform 13 | /// 14 | /// A IANA standard time zone string 15 | /// TimeZoneInfo object if matched, otherwise null 16 | public static TimeZoneInfo GetTimeZone(string ianaTimezone) 17 | { 18 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 19 | { 20 | bool result = TimeZoneConverter.TZConvert.TryIanaToWindows(ianaTimezone, out string windowsTimeZoneId); 21 | if (result == false) 22 | { 23 | return null; 24 | } 25 | 26 | return TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZoneId); 27 | } 28 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 29 | { 30 | return TimeZoneInfo.FindSystemTimeZoneById(ianaTimezone); 31 | } 32 | 33 | return null; 34 | } 35 | 36 | public static DateTime MakeDate(int year, int month, int day) 37 | { 38 | return MakeDate(year, month, day, 0, 0, 0); 39 | } 40 | 41 | public static DateTime MakeDate(int year, int month, int day, int hour, int minute) 42 | { 43 | return MakeDate(year, month, day, hour, minute, 0); 44 | } 45 | 46 | public static DateTime MakeDate(int year, int month, int day, int hour, int minute, int second) 47 | { 48 | return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc); 49 | } 50 | 51 | public static int GetDayOfYear(DateTime date) 52 | { 53 | return date.DayOfYear; 54 | } 55 | 56 | public static DateComponents GetDateComponents(String date) 57 | { 58 | string[] pieces = date.Split("-"); 59 | int year = int.Parse(pieces[0]); 60 | int month = int.Parse(pieces[1]); 61 | int day = int.Parse(pieces[2]); 62 | return new DateComponents(year, month, day); 63 | } 64 | 65 | public static DateTime AddSeconds(DateTime date, int seconds) 66 | { 67 | return date.AddSeconds(seconds); 68 | } 69 | 70 | public static DateTime MakeDateWithOffset(int year, int month, int day, int numberOfDayOffset) 71 | { 72 | DateTime calendar = new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc); 73 | return calendar.AddDays(numberOfDayOffset); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Adhan/internal/SolorTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | public class SolarTime 6 | { 7 | public readonly double Transit; 8 | public readonly double Sunrise; 9 | public readonly double Sunset; 10 | 11 | private readonly Coordinates observer; 12 | private readonly SolarCoordinates solar; 13 | private readonly SolarCoordinates prevSolar; 14 | private readonly SolarCoordinates nextSolar; 15 | private double approximateTransit; 16 | 17 | public SolarTime(DateTime today, Coordinates coordinates) 18 | { 19 | DateTime calendar = today.ToUniversalTime(); 20 | DateTime tomorrow = calendar.AddDays(1); 21 | DateTime yesterday = calendar.AddDays(-1); 22 | 23 | this.prevSolar = new SolarCoordinates(CalendricalHelper.JulianDay(yesterday)); 24 | this.solar = new SolarCoordinates(CalendricalHelper.JulianDay(today)); 25 | this.nextSolar = new SolarCoordinates(CalendricalHelper.JulianDay(tomorrow)); 26 | 27 | this.approximateTransit = Astronomical.ApproximateTransit(coordinates.Longitude, 28 | solar.ApparentSiderealTime, solar.RightAscension); 29 | double solarAltitude = -50.0 / 60.0; 30 | 31 | this.observer = coordinates; 32 | this.Transit = Astronomical.CorrectedTransit(this.approximateTransit, coordinates.Longitude, 33 | solar.ApparentSiderealTime, solar.RightAscension, prevSolar.RightAscension, 34 | nextSolar.RightAscension); 35 | this.Sunrise = Astronomical.CorrectedHourAngle(this.approximateTransit, solarAltitude, 36 | coordinates, false, solar.ApparentSiderealTime, solar.RightAscension, 37 | prevSolar.RightAscension, nextSolar.RightAscension, solar.Declination, 38 | prevSolar.Declination, nextSolar.Declination); 39 | this.Sunset = Astronomical.CorrectedHourAngle(this.approximateTransit, solarAltitude, 40 | coordinates, true, solar.ApparentSiderealTime, solar.RightAscension, 41 | prevSolar.RightAscension, nextSolar.RightAscension, solar.Declination, 42 | prevSolar.Declination, nextSolar.Declination); 43 | } 44 | 45 | public double HourAngle(double angle, bool afterTransit) 46 | { 47 | return Astronomical.CorrectedHourAngle(this.approximateTransit, angle, this.observer, 48 | afterTransit, this.solar.ApparentSiderealTime, this.solar.RightAscension, 49 | this.prevSolar.RightAscension, this.nextSolar.RightAscension, this.solar.Declination, 50 | this.prevSolar.Declination, this.nextSolar.Declination); 51 | } 52 | 53 | public double Afternoon(ShadowLength shadowLength) 54 | { 55 | // TODO (from Swift version) source shadow angle calculation 56 | double tangent = Math.Abs(observer.Latitude - solar.Declination); 57 | double inverse = (double) shadowLength + Math.Tan(MathHelper.ToRadians(tangent)); 58 | double angle = MathHelper.ToDegrees(Math.Atan(1.0 / inverse)); 59 | 60 | return HourAngle(angle, true); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/Adhan.Test/QiblaTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | using Batoulapps.Adhan; 4 | using Batoulapps.Adhan.Internal; 5 | using Adhan.Test.Internal; 6 | 7 | namespace Adhan.Test 8 | { 9 | [TestClass] 10 | public class QiblaTest 11 | { 12 | [TestMethod] 13 | public void QiblaTestNAWashingtonDC() 14 | { 15 | Coordinates washingtonDC = new Coordinates(38.9072, -77.0369); 16 | Assert.IsTrue((new Qibla(washingtonDC).Direction).IsWithin(0.001, 56.560)); 17 | } 18 | 19 | [TestMethod] 20 | public void QiblaTestNANYC() 21 | { 22 | Coordinates nyc = new Coordinates(40.7128, -74.0059); 23 | Assert.IsTrue((new Qibla(nyc).Direction).IsWithin(0.001, 58.481)); 24 | } 25 | 26 | [TestMethod] 27 | public void QiblaTestNASanFrancisco() 28 | { 29 | Coordinates sanFrancisco = new Coordinates(37.7749, -122.4194); 30 | Assert.IsTrue((new Qibla(sanFrancisco).Direction).IsWithin(0.001, 18.843)); 31 | } 32 | 33 | [TestMethod] 34 | public void QiblaTestNAAnchorage() 35 | { 36 | Coordinates anchorage = new Coordinates(61.2181, -149.9003); 37 | Assert.IsTrue((new Qibla(anchorage).Direction).IsWithin(0.001, 350.883)); 38 | } 39 | 40 | [TestMethod] 41 | public void QiblaTestSouthPacificSydney() 42 | { 43 | Coordinates sydney = new Coordinates(-33.8688, 151.2093); 44 | Assert.IsTrue((new Qibla(sydney).Direction).IsWithin(0.001, 277.499)); 45 | } 46 | 47 | [TestMethod] 48 | public void QiblaTestSouthPacificAuckland() 49 | { 50 | Coordinates auckland = new Coordinates(-36.8485, 174.7633); 51 | Assert.IsTrue((new Qibla(auckland).Direction).IsWithin(0.001, 261.197)); 52 | } 53 | 54 | [TestMethod] 55 | public void QiblaTestEuropeLondon() 56 | { 57 | Coordinates london = new Coordinates(51.5074, -0.1278); 58 | Assert.IsTrue((new Qibla(london).Direction).IsWithin(0.001, 118.987)); 59 | } 60 | 61 | [TestMethod] 62 | public void QiblaTestEuropeParis() 63 | { 64 | Coordinates paris = new Coordinates(48.8566, 2.3522); 65 | Assert.IsTrue((new Qibla(paris).Direction).IsWithin(0.001, 119.163)); 66 | } 67 | 68 | [TestMethod] 69 | public void QiblaTestEuropeOslo() 70 | { 71 | Coordinates oslo = new Coordinates(59.9139, 10.7522); 72 | Assert.IsTrue((new Qibla(oslo).Direction).IsWithin(0.001, 139.027)); 73 | } 74 | 75 | [TestMethod] 76 | public void QiblaTestAsiaIslamabad() 77 | { 78 | Coordinates islamabad = new Coordinates(33.7294, 73.0931); 79 | Assert.IsTrue((new Qibla(islamabad).Direction).IsWithin(0.001, 255.882)); 80 | } 81 | 82 | [TestMethod] 83 | public void QiblaTestAsiaTokyo() 84 | { 85 | Coordinates tokyo = new Coordinates(35.6895, 139.6917); 86 | Assert.IsTrue((new Qibla(tokyo).Direction).IsWithin(0.001, 293.021)); 87 | } 88 | 89 | [TestMethod] 90 | public void QiblaTestAfricaCapeTown() 91 | { 92 | Coordinates capeTown = new Coordinates(33.9249, 18.4241); 93 | Assert.IsTrue((new Qibla(capeTown).Direction).IsWithin(0.001, 118.004)); 94 | } 95 | 96 | [TestMethod] 97 | public void QiblaTestAfricaCairo() 98 | { 99 | Coordinates cairo = new Coordinates(30.0444, 31.2357); 100 | Assert.IsTrue((new Qibla(cairo).Direction).IsWithin(0.001, 136.137)); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /shared/times/Kuwait City-Kuwait.json: -------------------------------------------------------------------------------- 1 | { 2 | "params": { 3 | "latitude": 29.370865, 4 | "longitude": 47.979139, 5 | "timezone": "Asia/Kuwait", 6 | "method": "Kuwait", 7 | "madhab": "Shafi", 8 | "highLatitudeRule": "MiddleOfTheNight" 9 | }, 10 | "source": [ 11 | "https://itunes.apple.com/us/app/kuwait-prayer-times/id723108544?mt=12", 12 | "adjusted +/- 2 minutes", 13 | "1/1 0518 0642 1151 1442 1701 1823", 14 | "1/2 0516 0638 1202 1505 1726 1845", 15 | "1/3 0456 0615 1201 1519 1747 1904", 16 | "1/4 0418 0538 1152 1523 1806 1924", 17 | "1/5 0341 0507 1145 1521 1824 1947", 18 | "1/6 0315 0449 1146 1520 1843 2013", 19 | "1/7 0316 0452 1152 1526 1852 2023", 20 | "1/8 0339 0508 1154 1530 1840 2006", 21 | "1/9 0403 0525 1148 1522 1811 1930", 22 | "1/10 0422 0541 1138 1502 1734 1851", 23 | "1/11 0440 0601 1132 1439 1702 1820", 24 | "1/12 0501 0624 1137 1430 1649 1811" 25 | ], 26 | "variance": 2, 27 | "times": [ 28 | { 29 | "date": "2016-01-01", 30 | "fajr": "5:18 AM", 31 | "sunrise": "6:42 AM", 32 | "dhuhr": "11:51 AM", 33 | "asr": "2:42 PM", 34 | "maghrib": "5:01 PM", 35 | "isha": "6:23 PM" 36 | }, 37 | { 38 | "date": "2016-02-01", 39 | "fajr": "5:16 AM", 40 | "sunrise": "6:38 AM", 41 | "dhuhr": "12:02 PM", 42 | "asr": "3:05 PM", 43 | "maghrib": "5:26 PM", 44 | "isha": "6:45 PM" 45 | }, 46 | { 47 | "date": "2016-03-01", 48 | "fajr": "4:56 AM", 49 | "sunrise": "6:15 AM", 50 | "dhuhr": "12:01 PM", 51 | "asr": "3:19 PM", 52 | "maghrib": "5:47 PM", 53 | "isha": "7:04 PM" 54 | }, 55 | { 56 | "date": "2016-04-01", 57 | "fajr": "4:18 AM", 58 | "sunrise": "5:38 AM", 59 | "dhuhr": "11:52 AM", 60 | "asr": "3:23 PM", 61 | "maghrib": "6:06 PM", 62 | "isha": "7:24 PM" 63 | }, 64 | { 65 | "date": "2016-05-01", 66 | "fajr": "3:41 AM", 67 | "sunrise": "5:07 AM", 68 | "dhuhr": "11:45 AM", 69 | "asr": "3:21 PM", 70 | "maghrib": "6:24 PM", 71 | "isha": "7:47 PM" 72 | }, 73 | { 74 | "date": "2016-06-01", 75 | "fajr": "3:15 AM", 76 | "sunrise": "4:49 AM", 77 | "dhuhr": "11:46 AM", 78 | "asr": "3:20 PM", 79 | "maghrib": "6:43 PM", 80 | "isha": "8:13 PM" 81 | }, 82 | { 83 | "date": "2016-07-01", 84 | "fajr": "3:16 AM", 85 | "sunrise": "4:52 AM", 86 | "dhuhr": "11:52 AM", 87 | "asr": "3:26 PM", 88 | "maghrib": "6:52 PM", 89 | "isha": "8:23 PM" 90 | }, 91 | { 92 | "date": "2016-08-01", 93 | "fajr": "3:39 AM", 94 | "sunrise": "5:08 AM", 95 | "dhuhr": "11:54 AM", 96 | "asr": "3:30 PM", 97 | "maghrib": "6:40 PM", 98 | "isha": "8:06 PM" 99 | }, 100 | { 101 | "date": "2016-09-01", 102 | "fajr": "4:03 AM", 103 | "sunrise": "5:25 AM", 104 | "dhuhr": "11:48 AM", 105 | "asr": "3:22 PM", 106 | "maghrib": "6:11 PM", 107 | "isha": "7:30 PM" 108 | }, 109 | { 110 | "date": "2016-10-01", 111 | "fajr": "4:22 AM", 112 | "sunrise": "5:41 AM", 113 | "dhuhr": "11:38 AM", 114 | "asr": "3:02 PM", 115 | "maghrib": "5:34 PM", 116 | "isha": "6:51 PM" 117 | }, 118 | { 119 | "date": "2016-11-01", 120 | "fajr": "4:40 AM", 121 | "sunrise": "6:01 AM", 122 | "dhuhr": "11:32 AM", 123 | "asr": "2:39 PM", 124 | "maghrib": "5:02 PM", 125 | "isha": "6:20 PM" 126 | }, 127 | { 128 | "date": "2016-12-01", 129 | "fajr": "5:01 AM", 130 | "sunrise": "6:24 AM", 131 | "dhuhr": "11:37 AM", 132 | "asr": "2:30 PM", 133 | "maghrib": "4:49 PM", 134 | "isha": "6:11 PM" 135 | } 136 | ] 137 | } -------------------------------------------------------------------------------- /shared/times/Singapore-Singapore.json: -------------------------------------------------------------------------------- 1 | { 2 | "params": { 3 | "latitude": 1.283333, 4 | "longitude": 103.833333, 5 | "timezone": "Asia/Singapore", 6 | "method": "Singapore", 7 | "madhab": "Shafi", 8 | "highLatitudeRule": "MiddleOfTheNight" 9 | }, 10 | "source": [ 11 | "http://www.muis.gov.sg", 12 | "adjusted +/- 2 minutes", 13 | "1/1/2017 5:44-1 7:07-1 1:10-1 4:34-1 7:10-1 8:25-1", 14 | "1/2/2017 5:57-1 7:17-1 1:20-1 4:41 7:21-1 8:33-1", 15 | "1/3/2017 5:58-1 7:15-1 1:19-1 4:31 7:20 8:30-1", 16 | "1/4/2017 5:48 7:05 1:10-1 4:15 7:13-1 8:22-1", 17 | "1/5/2017 5:38-1 6:58-1 1:03 4:22 7:07 8:18", 18 | "1/6/2017 5:34 6:57 1:04 4:29-1 7:10-2 8:23", 19 | "1/7/2017 5:39 7:03 1:10 4:35 7:15-1 8:30-1", 20 | "1/8/2017 5:46-1 7:06 1:13-1 4:34 7:17-1 8:29", 21 | "1/9/2017 5:44-1 7:01 1:06 4:17 7:09 8:19-1", 22 | "1/10/2017 5:35 6:52-1 12:56-1 4:03-1 6:58-1 8:07-1", 23 | "1/11/2017 5:27 6:47-1 12:50-1 4:09 6:51-1 8:02-1", 24 | "1/12/2017 5:30-1 6:53-1 12:55 4:19-1 6:56-1 8:10" 25 | ], 26 | "times": [ 27 | { 28 | "date": "2016-01-01", 29 | "fajr": "5:43 AM", 30 | "sunrise": "7:06 AM", 31 | "dhuhr": "1:09 PM", 32 | "asr": "4:33 PM", 33 | "maghrib": "7:09 PM", 34 | "isha": "8:24 PM" 35 | }, 36 | { 37 | "date": "2016-02-01", 38 | "fajr": "5:56 AM", 39 | "sunrise": "7:16 AM", 40 | "dhuhr": "1:19 PM", 41 | "asr": "4:41 PM", 42 | "maghrib": "7:20 PM", 43 | "isha": "8:32 PM" 44 | }, 45 | { 46 | "date": "2016-03-01", 47 | "fajr": "5:57 AM", 48 | "sunrise": "7:14 AM", 49 | "dhuhr": "1:18 PM", 50 | "asr": "4:31 PM", 51 | "maghrib": "7:20 PM", 52 | "isha": "8:29 PM" 53 | }, 54 | { 55 | "date": "2016-04-01", 56 | "fajr": "5:48 AM", 57 | "sunrise": "7:05 AM", 58 | "dhuhr": "1:09 PM", 59 | "asr": "4:15 PM", 60 | "maghrib": "7:12 PM", 61 | "isha": "8:21 PM" 62 | }, 63 | { 64 | "date": "2016-05-01", 65 | "fajr": "5:37 AM", 66 | "sunrise": "6:57 AM", 67 | "dhuhr": "1:03 PM", 68 | "asr": "4:22 PM", 69 | "maghrib": "7:07 PM", 70 | "isha": "8:18 PM" 71 | }, 72 | { 73 | "date": "2016-06-01", 74 | "fajr": "5:34 AM", 75 | "sunrise": "6:57 AM", 76 | "dhuhr": "1:04 PM", 77 | "asr": "4:28 PM", 78 | "maghrib": "7:08 PM", 79 | "isha": "8:23 PM" 80 | }, 81 | { 82 | "date": "2016-07-01", 83 | "fajr": "5:39 AM", 84 | "sunrise": "7:03 AM", 85 | "dhuhr": "1:10 PM", 86 | "asr": "4:35 PM", 87 | "maghrib": "7:14 PM", 88 | "isha": "8:29 PM" 89 | }, 90 | { 91 | "date": "2016-08-01", 92 | "fajr": "5:45 AM", 93 | "sunrise": "7:06 AM", 94 | "dhuhr": "1:12 PM", 95 | "asr": "4:34 PM", 96 | "maghrib": "7:16 PM", 97 | "isha": "8:29 PM" 98 | }, 99 | { 100 | "date": "2016-09-01", 101 | "fajr": "5:43 AM", 102 | "sunrise": "7:01 AM", 103 | "dhuhr": "1:06 PM", 104 | "asr": "4:17 PM", 105 | "maghrib": "7:09 PM", 106 | "isha": "8:18 PM" 107 | }, 108 | { 109 | "date": "2016-10-01", 110 | "fajr": "5:35 AM", 111 | "sunrise": "6:51 AM", 112 | "dhuhr": "12:55 PM", 113 | "asr": "4:02 PM", 114 | "maghrib": "6:57 PM", 115 | "isha": "8:06 PM" 116 | }, 117 | { 118 | "date": "2016-11-01", 119 | "fajr": "5:27 AM", 120 | "sunrise": "6:46 AM", 121 | "dhuhr": "12:49 PM", 122 | "asr": "4:09 PM", 123 | "maghrib": "6:50 PM", 124 | "isha": "8:01 PM" 125 | }, 126 | { 127 | "date": "2016-12-01", 128 | "fajr": "5:29 AM", 129 | "sunrise": "6:52 AM", 130 | "dhuhr": "12:55 PM", 131 | "asr": "4:18 PM", 132 | "maghrib": "6:55 PM", 133 | "isha": "8:10 PM" 134 | } 135 | ] 136 | } -------------------------------------------------------------------------------- /shared/times/Doha-Qatar.json: -------------------------------------------------------------------------------- 1 | { 2 | "params": { 3 | "latitude": 25.283897, 4 | "longitude": 51.528770, 5 | "timezone": "Asia/Riyadh", 6 | "method": "Qatar", 7 | "madhab": "Shafi", 8 | "highLatitudeRule": "MiddleOfTheNight" 9 | }, 10 | "source": [ 11 | "http://www.islam.gov.qa/", 12 | "adjusted +/- 2 minutes", 13 | "Janurary(1) 4:58 6:19 11:37 2:35 4:56-1 6:26-1", 14 | "February(2) 4:59 6:17 11:47 2:56 5:19-1 6:49-1", 15 | "March(3) 4:41 5:57 11:46 3:07 5:37-1 7:07-1", 16 | "April(4) 4:09-1 5:26-1 11:38 3:06+1 5:51 7:21", 17 | "May(5) 3:37-1 4:58 11:31 3:00 6:05 7:35", 18 | "June(6) 3:16-1 4:44-1 11:32 2:56 6:21-1 7:51-1", 19 | "July(7) 3:18 4:47 11:38 3:01 6:29-1 7:59-1", 20 | "August(8) 3:36+1 5:01 11:40 3:07 6:21-2 7:51-2", 21 | "September(9) 3:56 5:14+1 11:34 3:03 5:54-1 7:24-1", 22 | "October(10) 4:10 5:26 11:24-1 2:48-1 5:22-2 6:52-2", 23 | "November(11) 4:24 5:41+1 11:17 2:29 4:54-1 6:24-1", 24 | "December(12) 4:41+1 6:02+1 11:23 2:23 4:44-1 6:14-1" 25 | ], 26 | "times": [ 27 | { 28 | "date": "2016-01-01", 29 | "fajr": "4:58 AM", 30 | "sunrise": "6:19 AM", 31 | "dhuhr": "11:37 AM", 32 | "asr": "2:35 PM", 33 | "maghrib": "4:55 PM", 34 | "isha": "6:25 PM" 35 | }, 36 | { 37 | "date": "2016-02-01", 38 | "fajr": "4:59 AM", 39 | "sunrise": "6:17 AM", 40 | "dhuhr": "11:47 AM", 41 | "asr": "2:56 PM", 42 | "maghrib": "5:18 PM", 43 | "isha": "6:48 PM" 44 | }, 45 | { 46 | "date": "2016-03-01", 47 | "fajr": "4:41 AM", 48 | "sunrise": "5:57 AM", 49 | "dhuhr": "11:46 AM", 50 | "asr": "3:07 PM", 51 | "maghrib": "5:36 PM", 52 | "isha": "7:06 PM" 53 | }, 54 | { 55 | "date": "2016-04-01", 56 | "fajr": "4:08 AM", 57 | "sunrise": "5:25 AM", 58 | "dhuhr": "11:38 AM", 59 | "asr": "3:07 PM", 60 | "maghrib": "5:51 PM", 61 | "isha": "7:21 PM" 62 | }, 63 | { 64 | "date": "2016-05-01", 65 | "fajr": "3:36 AM", 66 | "sunrise": "4:58 AM", 67 | "dhuhr": "11:31 AM", 68 | "asr": "3:00 PM", 69 | "maghrib": "6:05 PM", 70 | "isha": "7:35 PM" 71 | }, 72 | { 73 | "date": "2016-06-01", 74 | "fajr": "3:15 AM", 75 | "sunrise": "4:43 AM", 76 | "dhuhr": "11:32 AM", 77 | "asr": "2:56 PM", 78 | "maghrib": "6:20 PM", 79 | "isha": "7:50 PM" 80 | }, 81 | { 82 | "date": "2016-07-01", 83 | "fajr": "3:18 AM", 84 | "sunrise": "4:47 AM", 85 | "dhuhr": "11:38 AM", 86 | "asr": "3:01 PM", 87 | "maghrib": "6:28 PM", 88 | "isha": "7:58 PM" 89 | }, 90 | { 91 | "date": "2016-08-01", 92 | "fajr": "3:37 AM", 93 | "sunrise": "5:01 AM", 94 | "dhuhr": "11:40 AM", 95 | "asr": "3:07 PM", 96 | "maghrib": "6:19 PM", 97 | "isha": "7:49 PM" 98 | }, 99 | { 100 | "date": "2016-09-01", 101 | "fajr": "3:56 AM", 102 | "sunrise": "5:15 AM", 103 | "dhuhr": "11:34 AM", 104 | "asr": "3:03 PM", 105 | "maghrib": "5:53 PM", 106 | "isha": "7:23 PM" 107 | }, 108 | { 109 | "date": "2016-10-01", 110 | "fajr": "4:10 AM", 111 | "sunrise": "5:26 AM", 112 | "dhuhr": "11:23 AM", 113 | "asr": "2:47 PM", 114 | "maghrib": "5:20 PM", 115 | "isha": "6:50 PM" 116 | }, 117 | { 118 | "date": "2016-11-01", 119 | "fajr": "4:24 AM", 120 | "sunrise": "5:42 AM", 121 | "dhuhr": "11:17 AM", 122 | "asr": "2:29 PM", 123 | "maghrib": "4:53 PM", 124 | "isha": "6:23 PM" 125 | }, 126 | { 127 | "date": "2016-12-01", 128 | "fajr": "4:42 AM", 129 | "sunrise": "6:03 AM", 130 | "dhuhr": "11:23 AM", 131 | "asr": "2:23 PM", 132 | "maghrib": "4:43 PM", 133 | "isha": "6:13 PM" 134 | } 135 | ] 136 | } -------------------------------------------------------------------------------- /shared/times/Makkah-UmmAlQura.json: -------------------------------------------------------------------------------- 1 | { 2 | "params": { 3 | "latitude": 21.427009, 4 | "longitude": 39.828685, 5 | "timezone": "Asia/Riyadh", 6 | "method": "UmmAlQura", 7 | "madhab": "Shafi", 8 | "highLatitudeRule": "MiddleOfTheNight" 9 | }, 10 | "source": [ 11 | "http://www.ummulqura.org.sa/Index.aspx", 12 | "adjusted +/- 1 minute", 13 | "1/5/15 - 25/3/1437 Mecca 05:39-1 06:59+1 12:26 03:32-1 05:53-1 07:23-1", 14 | "2/4/16 - 25/4/1437 Mecca 05:39 06:57 12:35 03:49-1 06:13-1 07:43-1", 15 | "3/5/16 - 25/5/1437 Mecca 05:22 06:37+1 12:33-1 03:55 06:27 07:57", 16 | "4/3/16 - 25/6/1437 Mecca 04:55 06:11+1 12:24 03:50 06:37-1 08:07-1", 17 | "5/2/16 - 25/7/1437 Mecca 04:27+1 05:48+1 12:18 03:39 06:47 08:17", 18 | "6/4/16 - 28/8/1437 Mecca 04:11 05:37+1 12:20-1 03:37-1 07:01 08:31", 19 | "7/8/16 - 3/10/1437 Mecca 04:18 05:44+1 12:26 03:43 07:07 08:37", 20 | "8/2/16 - 28/10/1437 Mecca 04:32-1 05:54 12:27 03:46 07:00-1 08:30-1", 21 | "9/4/16 - 3/12/1437 Mecca 04:48-1 06:04+1 12:20 03:45-1 06:34 08:04", 22 | "10/6/16 - 5/1/1438 Mecca 04:58-1 06:13 12:09 03:32-1 06:04 07:34", 23 | "11/5/16 - 5/2/1438 Mecca 05:09-1 06:26 12:05-1 03:19-1 05:43-1 07:13-1", 24 | "12/4/16 - 5/3/1438 Mecca 05:24-1 06:44 12:12-1 03:17 05:38 07:08" 25 | ], 26 | "times": [ 27 | { 28 | "date": "2016-01-05", 29 | "fajr": "5:38 AM", 30 | "sunrise": "7:00 AM", 31 | "dhuhr": "12:26 PM", 32 | "asr": "3:31 PM", 33 | "maghrib": "5:52 PM", 34 | "isha": "7:22 PM" 35 | }, 36 | { 37 | "date": "2016-02-04", 38 | "fajr": "5:39 AM", 39 | "sunrise": "6:57 AM", 40 | "dhuhr": "12:35 PM", 41 | "asr": "3:48 PM", 42 | "maghrib": "6:12 PM", 43 | "isha": "7:42 PM" 44 | }, 45 | { 46 | "date": "2016-03-05", 47 | "fajr": "5:22 AM", 48 | "sunrise": "6:38 AM", 49 | "dhuhr": "12:32 PM", 50 | "asr": "3:55 PM", 51 | "maghrib": "6:27 PM", 52 | "isha": "7:57 PM" 53 | }, 54 | { 55 | "date": "2016-04-03", 56 | "fajr": "4:55 AM", 57 | "sunrise": "6:12 AM", 58 | "dhuhr": "12:24 PM", 59 | "asr": "3:50 PM", 60 | "maghrib": "6:36 PM", 61 | "isha": "8:06 PM" 62 | }, 63 | { 64 | "date": "2016-05-02", 65 | "fajr": "4:28 AM", 66 | "sunrise": "5:49 AM", 67 | "dhuhr": "12:18 PM", 68 | "asr": "3:39 PM", 69 | "maghrib": "6:47 PM", 70 | "isha": "8:17 PM" 71 | }, 72 | { 73 | "date": "2016-06-04", 74 | "fajr": "4:11 AM", 75 | "sunrise": "5:38 AM", 76 | "dhuhr": "12:19 PM", 77 | "asr": "3:36 PM", 78 | "maghrib": "7:01 PM", 79 | "isha": "8:31 PM" 80 | }, 81 | { 82 | "date": "2016-07-08", 83 | "fajr": "4:18 AM", 84 | "sunrise": "5:45 AM", 85 | "dhuhr": "12:26 PM", 86 | "asr": "3:43 PM", 87 | "maghrib": "7:07 PM", 88 | "isha": "8:37 PM" 89 | }, 90 | { 91 | "date": "2016-08-02", 92 | "fajr": "4:31 AM", 93 | "sunrise": "5:54 AM", 94 | "dhuhr": "12:27 PM", 95 | "asr": "3:46 PM", 96 | "maghrib": "6:59 PM", 97 | "isha": "8:29 PM" 98 | }, 99 | { 100 | "date": "2016-09-04", 101 | "fajr": "4:47 AM", 102 | "sunrise": "6:05 AM", 103 | "dhuhr": "12:20 PM", 104 | "asr": "3:44 PM", 105 | "maghrib": "6:34 PM", 106 | "isha": "8:04 PM" 107 | }, 108 | { 109 | "date": "2016-10-06", 110 | "fajr": "4:57 AM", 111 | "sunrise": "6:13 AM", 112 | "dhuhr": "12:09 PM", 113 | "asr": "3:31 PM", 114 | "maghrib": "6:04 PM", 115 | "isha": "7:34 PM" 116 | }, 117 | { 118 | "date": "2016-11-05", 119 | "fajr": "5:08 AM", 120 | "sunrise": "6:26 AM", 121 | "dhuhr": "12:04 PM", 122 | "asr": "3:18 PM", 123 | "maghrib": "5:42 PM", 124 | "isha": "7:12 PM" 125 | }, 126 | { 127 | "date": "2016-12-04", 128 | "fajr": "5:23 AM", 129 | "sunrise": "6:44 AM", 130 | "dhuhr": "12:11 PM", 131 | "asr": "3:17 PM", 132 | "maghrib": "5:38 PM", 133 | "isha": "7:08 PM" 134 | } 135 | ] 136 | } -------------------------------------------------------------------------------- /shared/times/London-MoonsightingCommittee.json: -------------------------------------------------------------------------------- 1 | { 2 | "params": { 3 | "latitude": 51.507194, 4 | "longitude": -0.116711, 5 | "timezone": "Europe/London", 6 | "method": "MoonsightingCommittee", 7 | "madhab": "Hanafi", 8 | "highLatitudeRule": "MiddleOfTheNight" 9 | }, 10 | "source": [ 11 | "http://www.moonsighting.com/pray.php", 12 | "adjusted +/- 1 minute", 13 | "Jan 01 (Fri) 06:25 am 08:06 am 12:09 pm 02:15 pm 04:05 pm 05:38 pm", 14 | "Feb 01 (Mon) 06:02 am 07:40 am 12:19 pm 03:00 pm+1 04:52 pm 06:18 pm", 15 | "Mar 01 (Tue) 05:10 am 06:45 am 12:18 pm 03:48 pm+1 05:44 pm 07:03 pm", 16 | "Apr 01 (Fri) 04:59 am 06:35 am 01:09 pm 05:31 pm+1 07:37 pm 08:49 pm", 17 | "May 01 (Sun) 03:47 am 05:32 am 01:02 pm+1 06:05 pm 08:27 pm 09:32 pm", 18 | "Jun 01 (Wed) 02:55 am 04:49 am 01:03 pm 06:31 pm 09:12 pm 10:23 pm", 19 | "Jul 01 (Fri) 02:51 am 04:48 am 01:09 pm 06:41 pm-1 09:24 pm 10:38 pm", 20 | "Aug 01 (Mon) 03:38 am-1 05:25 am 01:12 pm 06:23 pm 08:51 pm 09:56 pm", 21 | "Sep 01 (Thu) 04:35 am 06:14 am 01:05 pm 05:40 pm-1 07:49 pm 08:58 pm", 22 | "Oct 01 (Sat) 05:28 am 07:02 am 12:55 pm 04:43 pm-1 06:40 pm 07:57 pm", 23 | "Nov 01 (Tue) 05:17 am+1 06:55 am 11:49 am 02:44 pm-1 04:36 pm 05:59 pm+1", 24 | "Dec 01 (Thu) 06:04 am 07:44 am 11:55 am 02:08 pm-1 03:58 pm 05:29 pm" 25 | ], 26 | "times": [ 27 | { 28 | "date": "2016-01-01", 29 | "fajr": "6:25 AM", 30 | "sunrise": "8:06 AM", 31 | "dhuhr": "12:09 PM", 32 | "asr": "2:15 PM", 33 | "maghrib": "4:05 PM", 34 | "isha": "5:38 PM" 35 | }, 36 | { 37 | "date": "2016-02-01", 38 | "fajr": "6:02 AM", 39 | "sunrise": "7:40 AM", 40 | "dhuhr": "12:19 PM", 41 | "asr": "3:01 PM", 42 | "maghrib": "4:52 PM", 43 | "isha": "6:18 PM" 44 | }, 45 | { 46 | "date": "2016-03-01", 47 | "fajr": "5:10 AM", 48 | "sunrise": "6:45 AM", 49 | "dhuhr": "12:18 PM", 50 | "asr": "3:49 PM", 51 | "maghrib": "5:44 PM", 52 | "isha": "7:03 PM" 53 | }, 54 | { 55 | "date": "2016-04-01", 56 | "fajr": "4:59 AM", 57 | "sunrise": "6:35 AM", 58 | "dhuhr": "1:09 PM", 59 | "asr": "5:32 PM", 60 | "maghrib": "7:37 PM", 61 | "isha": "8:49 PM" 62 | }, 63 | { 64 | "date": "2016-05-01", 65 | "fajr": "3:47 AM", 66 | "sunrise": "5:32 AM", 67 | "dhuhr": "1:03 PM", 68 | "asr": "6:05 PM", 69 | "maghrib": "8:27 PM", 70 | "isha": "9:32 PM" 71 | }, 72 | { 73 | "date": "2016-06-01", 74 | "fajr": "2:55 AM", 75 | "sunrise": "4:49 AM", 76 | "dhuhr": "1:03 PM", 77 | "asr": "6:31 PM", 78 | "maghrib": "9:12 PM", 79 | "isha": "10:23 PM" 80 | }, 81 | { 82 | "date": "2016-07-01", 83 | "fajr": "2:51 AM", 84 | "sunrise": "4:48 AM", 85 | "dhuhr": "1:09 PM", 86 | "asr": "6:40 PM", 87 | "maghrib": "9:24 PM", 88 | "isha": "10:38 PM" 89 | }, 90 | { 91 | "date": "2016-08-01", 92 | "fajr": "3:37 AM", 93 | "sunrise": "5:25 AM", 94 | "dhuhr": "1:12 PM", 95 | "asr": "6:23 PM", 96 | "maghrib": "8:51 PM", 97 | "isha": "9:56 PM" 98 | }, 99 | { 100 | "date": "2016-09-01", 101 | "fajr": "4:35 AM", 102 | "sunrise": "6:14 AM", 103 | "dhuhr": "1:05 PM", 104 | "asr": "5:39 PM", 105 | "maghrib": "7:49 PM", 106 | "isha": "8:58 PM" 107 | }, 108 | { 109 | "date": "2016-10-01", 110 | "fajr": "5:28 AM", 111 | "sunrise": "7:02 AM", 112 | "dhuhr": "12:55 PM", 113 | "asr": "4:42 PM", 114 | "maghrib": "6:40 PM", 115 | "isha": "7:57 PM" 116 | }, 117 | { 118 | "date": "2016-11-01", 119 | "fajr": "5:18 AM", 120 | "sunrise": "6:55 AM", 121 | "dhuhr": "11:49 AM", 122 | "asr": "2:43 PM", 123 | "maghrib": "4:36 PM", 124 | "isha": "6:00 PM" 125 | }, 126 | { 127 | "date": "2016-12-01", 128 | "fajr": "6:04 AM", 129 | "sunrise": "7:44 AM", 130 | "dhuhr": "11:55 AM", 131 | "asr": "2:07 PM", 132 | "maghrib": "3:58 PM", 133 | "isha": "5:29 PM" 134 | } 135 | ] 136 | } -------------------------------------------------------------------------------- /shared/times/Dubai-Gulf.json: -------------------------------------------------------------------------------- 1 | { 2 | "params": { 3 | "latitude": 25.263056, 4 | "longitude": 55.297222, 5 | "timezone": "Asia/Dubai", 6 | "method": "Dubai", 7 | "madhab": "Shafi", 8 | "highLatitudeRule": "MiddleOfTheNight" 9 | }, 10 | "source": [ 11 | "https://www.awqaf.gov.ae/en/Pages/PrayerTimes.aspx", 12 | "01/01/2018 13 RABI AL-THANI 1439 5:42 AM 7:01 AM 12:25 PM 3:24 PM 5:44 PM 7:03 PM", 13 | "01/02/2018 15 JUMADA AL-AWWAL 1439 5:42 AM 6:58 AM 12:35 PM 3:44 PM 6:07 PM 7:22 PM", 14 | "01/03/2018 13 JUMADA AL-THANI 1439 5:25 AM 6:39 AM 12:34 PM 3:54 PM 6:24 PM 7:38 PM", 15 | "01/04/2018 14 RAJAB 1439 4:53 AM 6:08 AM 12:25 PM 3:54 PM 6:38 PM 7:53 PM", 16 | "01/05/2018 15 SHAABAN 1439 4:21 AM 5:40 AM 12:19 PM 3:47 PM 6:52 PM 8:12 PM", 17 | "01/06/2018 16 RAMADAN 1439 4 AM 5:25 AM 12:19 PM 3:43 PM 7:08 PM 8:33 PM", 18 | "01/07/2018 17 SHAWWAL 1439 4:02 AM 5:29 AM 12:25 PM 3:48 PM 7:16 PM 8:43 PM", 19 | "01/08/2018 19 DHU AL-QIDAH 1439 4:21 AM 5:43 AM 12:28 PM 3:55 PM 7:07 PM 8:29 PM", 20 | "01/09/2018 21 DHU AL-HIJJAH 1439 4:40 AM 5:56 AM 12:22 PM 3:51 PM 6:41 PM 7:57 PM", 21 | "01/10/2018 22 MUHARRAM 1440 4:54 AM 6:08 AM 12:11 PM 3:35 PM 6:09 PM 7:23 PM", 22 | "01/11/2018 24 SAFAR 1440 5:08 AM 6:23 AM 12:05 PM 3:17 PM 5:42 PM 6:57 PM", 23 | "01/12/2018 24 RABI AL-AWWAL 1440 5:25 AM 6:43 AM 12:11 PM 3:11 PM 5:32 PM 6:50 PM" 24 | ], 25 | "variance": 1, 26 | "times": [ 27 | { 28 | "date": "2016-01-01", 29 | "fajr": "5:42 AM", 30 | "sunrise": "7:01 AM", 31 | "dhuhr": "12:25 PM", 32 | "asr": "3:24 PM", 33 | "maghrib": "5:44 PM", 34 | "isha": "7:03 PM" 35 | }, 36 | { 37 | "date": "2016-02-01", 38 | "fajr": "5:42 AM", 39 | "sunrise": "6:58 AM", 40 | "dhuhr": "12:35 PM", 41 | "asr": "3:44 PM", 42 | "maghrib": "6:07 PM", 43 | "isha": "7:22 PM" 44 | }, 45 | { 46 | "date": "2016-03-01", 47 | "fajr": "5:25 AM", 48 | "sunrise": "6:39 AM", 49 | "dhuhr": "12:34 PM", 50 | "asr": "3:54 PM", 51 | "maghrib": "6:24 PM", 52 | "isha": "7:38 PM" 53 | }, 54 | { 55 | "date": "2016-04-01", 56 | "fajr": "4:53 AM", 57 | "sunrise": "6:08 AM", 58 | "dhuhr": "12:25 PM", 59 | "asr": "3:54 PM", 60 | "maghrib": "6:38 PM", 61 | "isha": "7:53 PM" 62 | }, 63 | { 64 | "date": "2016-05-01", 65 | "fajr": "4:21 AM", 66 | "sunrise": "5:40 AM", 67 | "dhuhr": "12:19 PM", 68 | "asr": "3:47 PM", 69 | "maghrib": "6:52 PM", 70 | "isha": "8:12 PM" 71 | }, 72 | { 73 | "date": "2016-06-01", 74 | "fajr": "4:00 AM", 75 | "sunrise": "5:25 AM", 76 | "dhuhr": "12:19 PM", 77 | "asr": "3:43 PM", 78 | "maghrib": "7:08 PM", 79 | "isha": "8:33 PM" 80 | }, 81 | { 82 | "date": "2016-07-01", 83 | "fajr": "4:02 AM", 84 | "sunrise": "5:29 AM", 85 | "dhuhr": "12:25 PM", 86 | "asr": "3:48 PM", 87 | "maghrib": "7:16 PM", 88 | "isha": "8:43 PM" 89 | }, 90 | { 91 | "date": "2016-08-01", 92 | "fajr": "4:21 AM", 93 | "sunrise": "5:43 AM", 94 | "dhuhr": "12:28 PM", 95 | "asr": "3:55 PM", 96 | "maghrib": "7:07 PM", 97 | "isha": "8:29 PM" 98 | }, 99 | { 100 | "date": "2016-09-01", 101 | "fajr": "4:40 AM", 102 | "sunrise": "5:56 AM", 103 | "dhuhr": "12:22 PM", 104 | "asr": "3:51 PM", 105 | "maghrib": "6:41 PM", 106 | "isha": "7:57 PM" 107 | }, 108 | { 109 | "date": "2016-10-01", 110 | "fajr": "4:54 AM", 111 | "sunrise": "6:08 AM", 112 | "dhuhr": "12:11 PM", 113 | "asr": "3:35 PM", 114 | "maghrib": "6:09 PM", 115 | "isha": "7:23 PM" 116 | }, 117 | { 118 | "date": "2015-11-01", 119 | "fajr": "5:08 AM", 120 | "sunrise": "6:23 AM", 121 | "dhuhr": "12:05 PM", 122 | "asr": "3:17 PM", 123 | "maghrib": "5:42 PM", 124 | "isha": "6:57 PM" 125 | }, 126 | { 127 | "date": "2015-12-01", 128 | "fajr": "5:25 AM", 129 | "sunrise": "6:43 AM", 130 | "dhuhr": "12:11 PM", 131 | "asr": "3:11 PM", 132 | "maghrib": "5:32 PM", 133 | "isha": "6:50 PM" 134 | } 135 | ] 136 | } -------------------------------------------------------------------------------- /adhan-csharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B23B2D4D-36E4-4DBD-889E-BB67675869C0}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adhan", "src\Adhan\Adhan.csproj", "{0BB66425-AFF0-4066-AF94-AB2735854E01}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adhan.Test", "src\Adhan.Test\Adhan.Test.csproj", "{0179B04D-DF47-4B34-AC40-C36AA9F7D629}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adhan.Samples", "src\Adhan.Samples\Adhan.Samples.csproj", "{8EF933CC-E213-4563-991F-124F532E92AF}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Debug|x64.ActiveCfg = Debug|Any CPU 27 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Debug|x64.Build.0 = Debug|Any CPU 28 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Debug|x86.ActiveCfg = Debug|Any CPU 29 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Debug|x86.Build.0 = Debug|Any CPU 30 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Release|x64.ActiveCfg = Release|Any CPU 33 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Release|x64.Build.0 = Release|Any CPU 34 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Release|x86.ActiveCfg = Release|Any CPU 35 | {0BB66425-AFF0-4066-AF94-AB2735854E01}.Release|x86.Build.0 = Release|Any CPU 36 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Debug|x64.Build.0 = Debug|Any CPU 40 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Debug|x86.Build.0 = Debug|Any CPU 42 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Release|x64.ActiveCfg = Release|Any CPU 45 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Release|x64.Build.0 = Release|Any CPU 46 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Release|x86.ActiveCfg = Release|Any CPU 47 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629}.Release|x86.Build.0 = Release|Any CPU 48 | {8EF933CC-E213-4563-991F-124F532E92AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {8EF933CC-E213-4563-991F-124F532E92AF}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {8EF933CC-E213-4563-991F-124F532E92AF}.Debug|x64.ActiveCfg = Debug|Any CPU 51 | {8EF933CC-E213-4563-991F-124F532E92AF}.Debug|x64.Build.0 = Debug|Any CPU 52 | {8EF933CC-E213-4563-991F-124F532E92AF}.Debug|x86.ActiveCfg = Debug|Any CPU 53 | {8EF933CC-E213-4563-991F-124F532E92AF}.Debug|x86.Build.0 = Debug|Any CPU 54 | {8EF933CC-E213-4563-991F-124F532E92AF}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {8EF933CC-E213-4563-991F-124F532E92AF}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {8EF933CC-E213-4563-991F-124F532E92AF}.Release|x64.ActiveCfg = Release|Any CPU 57 | {8EF933CC-E213-4563-991F-124F532E92AF}.Release|x64.Build.0 = Release|Any CPU 58 | {8EF933CC-E213-4563-991F-124F532E92AF}.Release|x86.ActiveCfg = Release|Any CPU 59 | {8EF933CC-E213-4563-991F-124F532E92AF}.Release|x86.Build.0 = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(SolutionProperties) = preSolution 62 | HideSolutionNode = FALSE 63 | EndGlobalSection 64 | GlobalSection(NestedProjects) = preSolution 65 | {0BB66425-AFF0-4066-AF94-AB2735854E01} = {B23B2D4D-36E4-4DBD-889E-BB67675869C0} 66 | {0179B04D-DF47-4B34-AC40-C36AA9F7D629} = {B23B2D4D-36E4-4DBD-889E-BB67675869C0} 67 | {8EF933CC-E213-4563-991F-124F532E92AF} = {B23B2D4D-36E4-4DBD-889E-BB67675869C0} 68 | EndGlobalSection 69 | GlobalSection(ExtensibilityGlobals) = postSolution 70 | SolutionGuid = {AFE1D13D-52DB-4523-B6B3-571BCA44E623} 71 | EndGlobalSection 72 | EndGlobal 73 | -------------------------------------------------------------------------------- /src/Adhan.Test/Internal/MathTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | using Batoulapps.Adhan; 4 | using Batoulapps.Adhan.Internal; 5 | using System.IO; 6 | using Adhan.Test.Data; 7 | using System; 8 | using Adhan.Test.Internal; 9 | 10 | namespace Adhan.Test.Internal 11 | { 12 | [TestClass] 13 | public class MathTest 14 | { 15 | [TestMethod] 16 | public void AngleConversion() 17 | { 18 | Assert.IsTrue(MathHelper.ToDegrees(Math.PI).IsWithin(0.00001, 180.0)); 19 | Assert.IsTrue(MathHelper.ToDegrees(Math.PI / 2).IsWithin(0.00001, 90.0)); 20 | } 21 | 22 | [TestMethod] 23 | public void Normalizing() 24 | { 25 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(2.0, -5).IsWithin(0.00001, -3)); 26 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(-4.0, -5.0).IsWithin(0.00001, -4)); 27 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(-6.0, -5.0).IsWithin(0.00001, -1)); 28 | 29 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(-1.0, 24).IsWithin(0.00001, 23)); 30 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(1.0, 24.0).IsWithin(0.00001, 1)); 31 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(49.0, 24).IsWithin(0.00001, 1)); 32 | 33 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(361.0, 360).IsWithin(0.00001, 1)); 34 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(360.0, 360).IsWithin(0.00001, 0)); 35 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(259.0, 360).IsWithin(0.00001, 259)); 36 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(2592.0, 360).IsWithin(0.00001, 72)); 37 | 38 | Assert.IsTrue(DoubleUtil.UnwindAngle(-45.0).IsWithin(0.00001, 315)); 39 | Assert.IsTrue(DoubleUtil.UnwindAngle(361.0).IsWithin(0.00001, 1)); 40 | Assert.IsTrue(DoubleUtil.UnwindAngle(360.0).IsWithin(0.00001, 0)); 41 | Assert.IsTrue(DoubleUtil.UnwindAngle(259.0).IsWithin(0.00001, 259)); 42 | Assert.IsTrue(DoubleUtil.UnwindAngle(2592.0).IsWithin(0.00001, 72)); 43 | 44 | Assert.IsTrue(DoubleUtil.NormalizeWithBound(360.1, 360).IsWithin(0.01, 0.1)); 45 | } 46 | 47 | [TestMethod] 48 | public void ClosestAngle() 49 | { 50 | Assert.IsTrue(DoubleUtil.ClosestAngle(360.0).IsWithin(0.000001, 0)); 51 | Assert.IsTrue(DoubleUtil.ClosestAngle(361.0).IsWithin(0.000001, 1)); 52 | Assert.IsTrue(DoubleUtil.ClosestAngle(1.0).IsWithin(0.000001, 1)); 53 | Assert.IsTrue(DoubleUtil.ClosestAngle(-1.0).IsWithin(0.000001, -1)); 54 | Assert.IsTrue(DoubleUtil.ClosestAngle(-181.0).IsWithin(0.000001, 179)); 55 | Assert.IsTrue(DoubleUtil.ClosestAngle(180.0).IsWithin(0.000001, 180)); 56 | Assert.IsTrue(DoubleUtil.ClosestAngle(359.0).IsWithin(0.000001, -1)); 57 | Assert.IsTrue(DoubleUtil.ClosestAngle(-359.0).IsWithin(0.000001, 1)); 58 | Assert.IsTrue(DoubleUtil.ClosestAngle(1261.0).IsWithin(0.000001, -179)); 59 | Assert.IsTrue(DoubleUtil.ClosestAngle(-360.1).IsWithin(0.01, -0.1)); 60 | } 61 | 62 | [TestMethod] 63 | public void TimeComponent() 64 | { 65 | TimeComponents comps1 = TimeComponents.FromDouble(15.199); 66 | Assert.IsNotNull(comps1); 67 | Assert.IsTrue(comps1.Hours == 15); 68 | Assert.IsTrue(comps1.Minutes == 11); 69 | Assert.IsTrue(comps1.Seconds == 56); 70 | 71 | TimeComponents comps2 = TimeComponents.FromDouble(1.0084); 72 | Assert.IsNotNull(comps2); 73 | Assert.IsTrue(comps2.Hours == 1); 74 | Assert.IsTrue(comps2.Minutes == 0); 75 | Assert.IsTrue(comps2.Seconds == 30); 76 | 77 | TimeComponents comps3 = TimeComponents.FromDouble(1.0083); 78 | Assert.IsNotNull(comps3); 79 | Assert.IsTrue(comps3.Hours == 1); 80 | Assert.IsTrue(comps3.Minutes == 0); 81 | 82 | TimeComponents comps4 = TimeComponents.FromDouble(2.1); 83 | Assert.IsNotNull(comps4); 84 | Assert.IsTrue(comps4.Hours == 2); 85 | Assert.IsTrue(comps4.Minutes == 6); 86 | 87 | TimeComponents comps5 = TimeComponents.FromDouble(3.5); 88 | Assert.IsNotNull(comps5); 89 | Assert.IsTrue(comps5.Hours == 3); 90 | Assert.IsTrue(comps5.Minutes == 30); 91 | } 92 | 93 | [TestMethod] 94 | public void MinuteRounding() 95 | { 96 | DateTime comps1 = TestUtils.MakeDate(2015, 1, 1, 10, 2, 29); 97 | DateTime rounded1 = CalendarUtil.RoundedMinute(comps1); 98 | Assert.IsTrue(rounded1.Minute == 2); 99 | Assert.IsTrue(rounded1.Second == 0); 100 | 101 | DateTime comps2 = TestUtils.MakeDate(2015, 1, 1, 10, 2, 31); 102 | DateTime rounded2 = CalendarUtil.RoundedMinute(comps2); 103 | Assert.IsTrue(rounded2.Minute == 3); 104 | Assert.IsTrue(rounded2.Second == 0); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Adhan/CalculationMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan 4 | { 5 | /// 6 | /// Standard calculation methods for calculating prayer times 7 | /// 8 | public enum CalculationMethod 9 | { 10 | /// 11 | /// Muslim World League 12 | /// Uses Fajr angle of 18 and an Isha angle of 17 13 | /// 14 | /// 15 | MUSLIM_WORLD_LEAGUE, 16 | 17 | /// 18 | /// Egyptian General Authority of Survey 19 | /// Uses Fajr angle of 19.5 and an Isha angle of 17.5 20 | /// 21 | /// 22 | EGYPTIAN, 23 | 24 | /// 25 | /// University of Islamic Sciences, Karachi 26 | /// Uses Fajr angle of 18 and an Isha angle of 18 27 | /// 28 | /// 29 | KARACHI, 30 | 31 | /// 32 | /// Umm al-Qura University, Makkah 33 | /// Uses a Fajr angle of 18.5 and an Isha angle of 90. Note: You should add a +30 minute custom 34 | /// adjustment of Isha during Ramadan. 35 | /// 36 | /// 37 | UMM_AL_QURA, 38 | 39 | /// 40 | /// The Gulf Region 41 | /// Uses Fajr and Isha angles of 18.2 degrees. 42 | /// 43 | /// 44 | DUBAI, 45 | 46 | /// 47 | /// Moonsighting Committee 48 | /// Uses a Fajr angle of 18 and an Isha angle of 18. Also uses seasonal adjustment values. 49 | /// 50 | /// 51 | MOON_SIGHTING_COMMITTEE, 52 | 53 | /// 54 | /// Referred to as the ISNA method 55 | /// This method is included for completeness, but is not recommended. 56 | /// Uses a Fajr angle of 15 and an Isha angle of 15. 57 | /// 58 | /// 59 | NORTH_AMERICA, 60 | 61 | /// 62 | /// Kuwait 63 | /// Uses a Fajr angle of 18 and an Isha angle of 17.5 64 | /// 65 | /// 66 | KUWAIT, 67 | 68 | /// 69 | /// Qatar 70 | /// Modified version of Umm al-Qura that uses a Fajr angle of 18. 71 | /// 72 | /// 73 | QATAR, 74 | 75 | /// 76 | /// Singapore 77 | /// Uses a Fajr angle of 20 and an Isha angle of 18 78 | /// 79 | /// 80 | SINGAPORE, 81 | 82 | /// 83 | /// The default value for {@link CalculationParameters#method} when initializing a 84 | /// {@link CalculationParameters} object. Sets a Fajr angle of 0 and an Isha angle of 0. 85 | /// 86 | /// 87 | OTHER 88 | } 89 | 90 | public static class CalculationMethodExtensions 91 | { 92 | public static CalculationParameters GetParameters(this CalculationMethod method) 93 | { 94 | switch(method) 95 | { 96 | case CalculationMethod.MUSLIM_WORLD_LEAGUE: 97 | return new CalculationParameters(18.0, 17.0, method) 98 | .withMethodAdjustments(new PrayerAdjustments(0, 0, 1, 0, 0, 0)); 99 | case CalculationMethod.EGYPTIAN: 100 | return new CalculationParameters(20.0, 18.0, method) 101 | .withMethodAdjustments(new PrayerAdjustments(0, 0, 1, 0, 0, 0)); 102 | case CalculationMethod.KARACHI: 103 | return new CalculationParameters(18.0, 18.0, method) 104 | .withMethodAdjustments(new PrayerAdjustments(0, 0, 1, 0, 0, 0)); 105 | case CalculationMethod.UMM_AL_QURA: 106 | return new CalculationParameters(18.5, 90, method); 107 | case CalculationMethod.DUBAI: 108 | return new CalculationParameters(18.2, 18.2, method) 109 | .withMethodAdjustments(new PrayerAdjustments(0, -3, 3, 3, 3, 0)); 110 | case CalculationMethod.MOON_SIGHTING_COMMITTEE: 111 | return new CalculationParameters(18.0, 18.0, method) 112 | .withMethodAdjustments(new PrayerAdjustments(0, 0, 5, 0, 3, 0)); 113 | case CalculationMethod.NORTH_AMERICA: 114 | return new CalculationParameters(15.0, 15.0, method) 115 | .withMethodAdjustments(new PrayerAdjustments(0, 0, 1, 0, 0, 0)); 116 | case CalculationMethod.KUWAIT: 117 | return new CalculationParameters(18.0, 17.5, method); 118 | case CalculationMethod.QATAR: 119 | return new CalculationParameters(18.0, 90, method); 120 | case CalculationMethod.SINGAPORE: 121 | return new CalculationParameters(20.0, 18.0, method) 122 | .withMethodAdjustments(new PrayerAdjustments(0, 0, 1, 0, 0, 0)); 123 | case CalculationMethod.OTHER: 124 | return new CalculationParameters(0.0, 0.0, method); 125 | default: 126 | throw new ArgumentException("Invalid CalculationMethod"); 127 | } 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /src/Adhan.Test/TimeZoneConverter/DataLoader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Resources; 7 | 8 | namespace Adhan.Test.TimeZoneConverter 9 | { 10 | internal static class DataLoader 11 | { 12 | public static void Populate(IDictionary ianaMap, IDictionary windowsMap, IDictionary railsMap, IDictionary> inverseRailsMap) 13 | { 14 | var mapping = GetEmbeddedData("Adhan.Test.TimeZoneConverter.Mapping.csv.gz"); 15 | var aliases = GetEmbeddedData("Adhan.Test.TimeZoneConverter.Aliases.csv.gz"); 16 | var railsMapping = GetEmbeddedData("Adhan.Test.TimeZoneConverter.RailsMapping.csv.gz"); 17 | 18 | var links = new Dictionary(); 19 | foreach (var link in aliases) 20 | { 21 | var parts = link.Split(','); 22 | var value = parts[0]; 23 | foreach (var key in parts[1].Split()) 24 | links.Add(key, value); 25 | } 26 | 27 | var similarIanaZones = new Dictionary>(); 28 | foreach (var item in mapping) 29 | { 30 | var parts = item.Split(','); 31 | var windowsZone = parts[0]; 32 | var territory = parts[1]; 33 | var ianaZones = parts[2].Split(); 34 | 35 | // Create the Windows map entry 36 | if (!links.TryGetValue(ianaZones[0], out var value)) 37 | value = ianaZones[0]; 38 | 39 | var key = $"{territory}|{windowsZone}"; 40 | windowsMap.Add(key, value); 41 | 42 | // Create the IANA map entries 43 | foreach (var ianaZone in ianaZones) 44 | { 45 | if (!ianaMap.ContainsKey(ianaZone)) 46 | ianaMap.Add(ianaZone, windowsZone); 47 | } 48 | 49 | if (ianaZones.Length > 1) 50 | { 51 | foreach (var ianaZone in ianaZones) 52 | similarIanaZones.Add(ianaZone, ianaZones.Except(new[] { ianaZone }).ToArray()); 53 | } 54 | } 55 | 56 | // Expand the IANA map to include all links 57 | foreach (var link in links) 58 | { 59 | if (ianaMap.ContainsKey(link.Key)) 60 | continue; 61 | 62 | ianaMap.Add(link.Key, ianaMap[link.Value]); 63 | } 64 | 65 | foreach (var item in railsMapping) 66 | { 67 | var parts = item.Split(','); 68 | var railsZone = parts[0].Trim('"'); 69 | var ianaZone = parts[1].Trim('"'); 70 | railsMap.Add(railsZone, ianaZone); 71 | } 72 | 73 | foreach (var grouping in railsMap.GroupBy(x => x.Value, x => x.Key)) 74 | { 75 | inverseRailsMap.Add(grouping.Key, grouping.ToList()); 76 | } 77 | 78 | // Expand the Inverse Rails map to include similar IANA zones 79 | foreach (var ianaZone in ianaMap.Keys) 80 | { 81 | if (inverseRailsMap.ContainsKey(ianaZone) || links.ContainsKey(ianaZone)) 82 | continue; 83 | 84 | if (similarIanaZones.TryGetValue(ianaZone, out var similarZones)) 85 | { 86 | foreach (var otherZone in similarZones) 87 | { 88 | if (inverseRailsMap.TryGetValue(otherZone, out var railsZones)) 89 | { 90 | inverseRailsMap.Add(ianaZone, railsZones); 91 | break; 92 | } 93 | } 94 | } 95 | } 96 | 97 | // Expand the Inverse Rails map to include links 98 | foreach (var link in links) 99 | { 100 | if (inverseRailsMap.ContainsKey(link.Key)) 101 | continue; 102 | 103 | if (inverseRailsMap.TryGetValue(link.Value, out var railsZone)) 104 | inverseRailsMap.Add(link.Key, railsZone); 105 | } 106 | 107 | 108 | } 109 | 110 | private static IEnumerable GetEmbeddedData(string resourceName) 111 | { 112 | #if NET35 || NET40 113 | var assembly = typeof(DataLoader).Assembly; 114 | #else 115 | var assembly = typeof(DataLoader).GetTypeInfo().Assembly; 116 | #endif 117 | using (var compressedStream = assembly.GetManifestResourceStream(resourceName) ?? throw new MissingManifestResourceException()) 118 | using (var stream = new GZipStream(compressedStream, CompressionMode.Decompress)) 119 | using (var reader = new StreamReader(stream)) 120 | { 121 | string line; 122 | while ((line = reader.ReadLine()) != null) 123 | { 124 | yield return line; 125 | } 126 | } 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /src/Adhan.Test/CalculationMethodTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | using Batoulapps.Adhan; 4 | using Batoulapps.Adhan.Internal; 5 | using Adhan.Test.Internal; 6 | 7 | namespace Adhan.Test 8 | { 9 | [TestClass] 10 | public class CalculationMethodTest 11 | { 12 | [TestMethod] 13 | public void CalcuateMethodMuslimWorldLeague() 14 | { 15 | CalculationParameters calcParams = CalculationMethod.MUSLIM_WORLD_LEAGUE.GetParameters(); 16 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 18)); 17 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 17)); 18 | Assert.AreEqual(calcParams.IshaInterval, 0); 19 | Assert.AreEqual(calcParams.Method, CalculationMethod.MUSLIM_WORLD_LEAGUE); 20 | } 21 | 22 | [TestMethod] 23 | public void CalcuateMethodEgyptian() 24 | { 25 | CalculationParameters calcParams = CalculationMethod.EGYPTIAN.GetParameters(); 26 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 20)); 27 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 18)); 28 | Assert.AreEqual(calcParams.IshaInterval, 0); 29 | Assert.AreEqual(calcParams.Method, CalculationMethod.EGYPTIAN); 30 | } 31 | 32 | [TestMethod] 33 | public void CalcuateMethodKarachi() 34 | { 35 | CalculationParameters calcParams = CalculationMethod.KARACHI.GetParameters(); 36 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 18)); 37 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 18)); 38 | Assert.AreEqual(calcParams.IshaInterval, 0); 39 | Assert.AreEqual(calcParams.Method, CalculationMethod.KARACHI); 40 | } 41 | 42 | [TestMethod] 43 | public void CalcuateMethodUmmAlQura() 44 | { 45 | CalculationParameters calcParams = CalculationMethod.UMM_AL_QURA.GetParameters(); 46 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 18.5)); 47 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 0)); 48 | Assert.AreEqual(calcParams.IshaInterval, 90); 49 | Assert.AreEqual(calcParams.Method, CalculationMethod.UMM_AL_QURA); 50 | } 51 | 52 | [TestMethod] 53 | public void CalcuateMethodDubai() 54 | { 55 | CalculationParameters calcParams = CalculationMethod.DUBAI.GetParameters(); 56 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 18.2)); 57 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 18.2)); 58 | Assert.AreEqual(calcParams.IshaInterval, 0); 59 | Assert.AreEqual(calcParams.Method, CalculationMethod.DUBAI); 60 | } 61 | 62 | [TestMethod] 63 | public void CalcuateMethodMoonSightingCommittee() 64 | { 65 | CalculationParameters calcParams = CalculationMethod.MOON_SIGHTING_COMMITTEE.GetParameters(); 66 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 18)); 67 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 18)); 68 | Assert.AreEqual(calcParams.IshaInterval, 0); 69 | Assert.AreEqual(calcParams.Method, CalculationMethod.MOON_SIGHTING_COMMITTEE); 70 | } 71 | 72 | [TestMethod] 73 | public void CalcuateMethodNorthAmerica() 74 | { 75 | CalculationParameters calcParams = CalculationMethod.NORTH_AMERICA.GetParameters(); 76 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 15)); 77 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 15)); 78 | Assert.AreEqual(calcParams.IshaInterval, 0); 79 | Assert.AreEqual(calcParams.Method, CalculationMethod.NORTH_AMERICA); 80 | } 81 | 82 | [TestMethod] 83 | public void CalcuateMethodKuwait() 84 | { 85 | CalculationParameters calcParams = CalculationMethod.KUWAIT.GetParameters(); 86 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 18)); 87 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 17.5)); 88 | Assert.AreEqual(calcParams.IshaInterval, 0); 89 | Assert.AreEqual(calcParams.Method, CalculationMethod.KUWAIT); 90 | } 91 | 92 | [TestMethod] 93 | public void CalcuateMethodQatar() 94 | { 95 | CalculationParameters calcParams = CalculationMethod.QATAR.GetParameters(); 96 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 18)); 97 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 0)); 98 | Assert.AreEqual(calcParams.IshaInterval, 90); 99 | Assert.AreEqual(calcParams.Method, CalculationMethod.QATAR); 100 | } 101 | 102 | [TestMethod] 103 | public void CalcuateMethodOther() 104 | { 105 | CalculationParameters calcParams = CalculationMethod.OTHER.GetParameters(); 106 | Assert.IsTrue(calcParams.FajrAngle.IsWithin(0.000001, 0)); 107 | Assert.IsTrue(calcParams.IshaAngle.IsWithin(0.000001, 0)); 108 | Assert.AreEqual(calcParams.IshaInterval, 0); 109 | Assert.AreEqual(calcParams.Method, CalculationMethod.OTHER); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Adhan/CalculationParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan 4 | { 5 | /// 6 | /// Parameters used for PrayerTime calculation customization 7 | /// 8 | /// Note that, for many cases, you can use {@link CalculationMethod#getParameters()} to get a 9 | /// pre-computed set of calculation parameters depending on one of the available 10 | /// {@link CalculationMethod}. 11 | /// 12 | public class CalculationParameters 13 | { 14 | /// 15 | /// The method used to do the calculation 16 | /// 17 | public CalculationMethod Method = CalculationMethod.OTHER; 18 | 19 | /// 20 | /// The angle of the sun used to calculate fajr 21 | /// 22 | public double FajrAngle; 23 | 24 | /// 25 | /// The angle of the sun used to calculate isha 26 | /// 27 | public double IshaAngle; 28 | 29 | /// 30 | /// Minutes after Maghrib (if set, the time for Isha will be Maghrib plus IshaInterval) 31 | /// 32 | public int IshaInterval = 0; 33 | 34 | /// 35 | /// The madhab used to calculate Asr 36 | /// 37 | public Madhab Madhab = Madhab.SHAFI; 38 | 39 | /// 40 | /// Rules for placing bounds on Fajr and Isha for high latitude areas 41 | /// 42 | public HighLatitudeRule HighLatitudeRule = HighLatitudeRule.MIDDLE_OF_THE_NIGHT; 43 | 44 | /// 45 | /// Used to optionally add or subtract a set amount of time from each prayer time 46 | /// 47 | /// 48 | public PrayerAdjustments Adjustments = new PrayerAdjustments(); 49 | 50 | /// 51 | /// Used for method adjustments 52 | /// 53 | /// 54 | public PrayerAdjustments MethodAdjustments = new PrayerAdjustments(); 55 | 56 | /// 57 | /// Generate CalculationParameters from angles 58 | /// 59 | /// the angle for calculating fajr 60 | /// the angle for calculating isha 61 | public CalculationParameters(double fajrAngle, double ishaAngle) 62 | { 63 | this.FajrAngle = fajrAngle; 64 | this.IshaAngle = ishaAngle; 65 | } 66 | 67 | /// 68 | /// Generate CalculationParameters from fajr angle and isha interval 69 | /// 70 | /// the angle for calculating fajr 71 | /// the amount of time after maghrib to have isha 72 | public CalculationParameters(double fajrAngle, int ishaInterval) 73 | { 74 | this.FajrAngle = fajrAngle; 75 | this.IshaAngle = 0.0; 76 | this.IshaInterval = ishaInterval; 77 | } 78 | 79 | /// 80 | /// Generate CalculationParameters from angles and a calculation method 81 | /// 82 | /// the angle for calculating fajr 83 | /// ishaAngle the angle for calculating isha 84 | /// the calculation method to use 85 | public CalculationParameters(double fajrAngle, double ishaAngle, CalculationMethod method) 86 | { 87 | this.FajrAngle = fajrAngle; 88 | this.IshaAngle = ishaAngle; 89 | this.Method = method; 90 | } 91 | 92 | /// 93 | /// Generate CalculationParameters from fajr angle, isha interval, and calculation method 94 | /// 95 | /// the angle for calculating fajr 96 | /// the amount of time after maghrib to have isha 97 | /// the calculation method to use 98 | public CalculationParameters(double fajrAngle, int ishaInterval, CalculationMethod method) 99 | { 100 | this.FajrAngle = fajrAngle; 101 | this.IshaAngle = 0.0; 102 | this.IshaInterval = ishaInterval; 103 | this.Method = method; 104 | } 105 | 106 | /// 107 | /// Set the method adjustments for the current calculation parameters 108 | /// 109 | /// the prayer adjustments 110 | /// this calculation parameters instance 111 | public CalculationParameters withMethodAdjustments(PrayerAdjustments adjustments) 112 | { 113 | this.MethodAdjustments = adjustments; 114 | return this; 115 | } 116 | 117 | public NightPortions NightPortions() 118 | { 119 | switch(this.HighLatitudeRule) 120 | { 121 | case HighLatitudeRule.MIDDLE_OF_THE_NIGHT: 122 | return new NightPortions(1.0 / 2.0, 1.0 / 2.0); 123 | case HighLatitudeRule.SEVENTH_OF_THE_NIGHT: 124 | return new NightPortions(1.0 / 7.0, 1.0 / 7.0); 125 | case HighLatitudeRule.TWILIGHT_ANGLE: 126 | return new NightPortions(this.FajrAngle / 60.0, this.IshaAngle / 60.0); 127 | default: 128 | throw new ArgumentException("Invalid high latitude rule"); 129 | } 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /src/Adhan.Test/TimingTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | using Batoulapps.Adhan; 4 | using Batoulapps.Adhan.Internal; 5 | using System.IO; 6 | using Adhan.Test.Data; 7 | using System; 8 | using Adhan.Test.Internal; 9 | 10 | namespace Adhan.Test 11 | { 12 | [TestClass] 13 | public class TimingTest 14 | { 15 | private const string timingPath = "../../../../../shared/times/"; 16 | 17 | [TestMethod] 18 | public void TestTimes() 19 | { 20 | string[] files = Directory.GetFiles(timingPath, "*.json"); 21 | foreach(string file in files) 22 | { 23 | TestTimingFile(file); 24 | } 25 | } 26 | 27 | private void TestTimingFile(string file) 28 | { 29 | TimingFile timingFile = TimingFile.Load(file); 30 | Assert.IsNotNull(timingFile); 31 | 32 | Coordinates coordinates = new Coordinates(timingFile.Parameters.Latitude, timingFile.Parameters.Longitude); 33 | CalculationParameters parameters = ParseParameters(timingFile.Parameters); 34 | 35 | foreach(TimingInfo info in timingFile.Times) 36 | { 37 | DateComponents dateComponents = TestUtils.GetDateComponents(info.Date); 38 | PrayerTimes prayerTimes = new PrayerTimes(coordinates, dateComponents, parameters); 39 | 40 | long fajrDifference = GetDifferenceInMinutes(prayerTimes.Fajr, info.Date, info.Fajr, timingFile.Parameters.Timezone); 41 | Assert.IsTrue(fajrDifference.IsAtMost(timingFile.Variance)); 42 | 43 | long sunriseDifference = GetDifferenceInMinutes(prayerTimes.Sunrise, info.Date, info.Sunrise, timingFile.Parameters.Timezone); 44 | Assert.IsTrue(sunriseDifference.IsAtMost(timingFile.Variance)); 45 | 46 | long dhuhrDifference = GetDifferenceInMinutes(prayerTimes.Dhuhr, info.Date, info.Dhuhr, timingFile.Parameters.Timezone); 47 | Assert.IsTrue(dhuhrDifference.IsAtMost(timingFile.Variance)); 48 | 49 | long asrDifference = GetDifferenceInMinutes(prayerTimes.Asr, info.Date, info.Asr, timingFile.Parameters.Timezone); 50 | Assert.IsTrue(asrDifference.IsAtMost(timingFile.Variance)); 51 | 52 | long maghribDifference = GetDifferenceInMinutes(prayerTimes.Maghrib, info.Date, info.Maghrib, timingFile.Parameters.Timezone); 53 | Assert.IsTrue(maghribDifference.IsAtMost(timingFile.Variance)); 54 | 55 | long ishaDifference = GetDifferenceInMinutes(prayerTimes.Isha, info.Date, info.Isha, timingFile.Parameters.Timezone); 56 | Assert.IsTrue(ishaDifference.IsAtMost(timingFile.Variance)); 57 | } 58 | } 59 | 60 | private long GetDifferenceInMinutes(DateTime prayerTime, string jsonDate, string jsonTime, string ianaTimezone) 61 | { 62 | TimeZoneInfo timezone = TestUtils.GetTimeZone(ianaTimezone); 63 | if (timezone == null) 64 | { 65 | return 0; 66 | } 67 | 68 | DateTime parsedDate = DateTime.Parse($"{jsonDate} {jsonTime}"); 69 | DateTime utcDateTime = TimeZoneInfo.ConvertTimeToUtc(parsedDate, timezone); 70 | 71 | return (long) prayerTime.Subtract(utcDateTime).TotalMinutes; 72 | } 73 | 74 | private CalculationParameters ParseParameters(TimingParameters timingParameters) 75 | { 76 | CalculationMethod method; 77 | switch (timingParameters.Method) 78 | { 79 | case "MuslimWorldLeague": 80 | { 81 | method = CalculationMethod.MUSLIM_WORLD_LEAGUE; 82 | break; 83 | } 84 | case "Egyptian": 85 | { 86 | method = CalculationMethod.EGYPTIAN; 87 | break; 88 | } 89 | case "Karachi": 90 | { 91 | method = CalculationMethod.KARACHI; 92 | break; 93 | } 94 | case "UmmAlQura": 95 | { 96 | method = CalculationMethod.UMM_AL_QURA; 97 | break; 98 | } 99 | case "Dubai": 100 | { 101 | method = CalculationMethod.DUBAI; 102 | break; 103 | } 104 | case "MoonsightingCommittee": 105 | { 106 | method = CalculationMethod.MOON_SIGHTING_COMMITTEE; 107 | break; 108 | } 109 | case "NorthAmerica": 110 | { 111 | method = CalculationMethod.NORTH_AMERICA; 112 | break; 113 | } 114 | case "Kuwait": 115 | { 116 | method = CalculationMethod.KUWAIT; 117 | break; 118 | } 119 | case "Qatar": 120 | { 121 | method = CalculationMethod.QATAR; 122 | break; 123 | } 124 | case "Singapore": 125 | { 126 | method = CalculationMethod.SINGAPORE; 127 | break; 128 | } 129 | default: 130 | { 131 | method = CalculationMethod.OTHER; 132 | break; 133 | } 134 | } 135 | 136 | CalculationParameters parameters = method.GetParameters(); 137 | if ("Shafi".Equals(timingParameters.Madhab)) 138 | { 139 | parameters.Madhab = Madhab.SHAFI; 140 | } 141 | else if ("Hanafi".Equals(timingParameters.Madhab)) 142 | { 143 | parameters.Madhab = Madhab.HANAFI; 144 | } 145 | 146 | if ("SeventhOfTheNight".Equals(timingParameters.HighLatitudeRule)) 147 | { 148 | parameters.HighLatitudeRule = HighLatitudeRule.SEVENTH_OF_THE_NIGHT; 149 | } 150 | else if ("TwilightAngle".Equals(timingParameters.HighLatitudeRule)) 151 | { 152 | parameters.HighLatitudeRule = HighLatitudeRule.TWILIGHT_ANGLE; 153 | } 154 | else 155 | { 156 | parameters.HighLatitudeRule = HighLatitudeRule.MIDDLE_OF_THE_NIGHT; 157 | } 158 | 159 | return parameters; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adhan CSharp 2 | 3 | [![badge-travis][]][travis] 4 | 5 | Adhan CSharp is a well tested and well documented library for calculating Islamic prayer times. Adhan CSharp is written to be compatible with .NET Core. It has a small method overhead, and has no external dependencies. 6 | 7 | All astronomical calculations are high precision equations directly from the book [“Astronomical Algorithms” by Jean Meeus](http://www.willbell.com/math/mc1.htm). This book is recommended by the Astronomical Applications Department of the U.S. Naval Observatory and the Earth System Research Laboratory of the National Oceanic and Atmospheric Administration. 8 | 9 | Implementations of Adhan in other languages can be found in the parent repo [Adhan](https://github.com/batoulapps/Adhan). 10 | 11 | ## Usage 12 | 13 | ### Visual Studio Code 14 | 15 | Open Visual Studio Code. Choose File Open Folder and select the folder where the cloned repo exists (e.g. c:\repos\adhan-csharp). 16 | From a Terminal window (CTRL-SHIFT-`), run the following command: 17 | 18 | ``` 19 | dotnet build 20 | ``` 21 | 22 | ### Initialization parameters 23 | 24 | #### Coordinates 25 | 26 | Create a `Coordinates` object with the latitude and longitude for the location you want prayer times for. 27 | 28 | ```java 29 | Coordinates coordinates = new Coordinates(35.78056, -78.6389); 30 | ``` 31 | 32 | #### Date 33 | 34 | The date parameter passed in should be an instance of the `DateComponents` object. The year, month, and day values need to be populated. All other values will be ignored. The year, month and day values should be for the local date that you want prayer times for. These date values are expected to be for the Gregorian calendar. There's also a convenience method for converting a `System.DateTime` to `DateComponents`. 35 | 36 | ```csharp 37 | DateComponents date = new DateComponents(2015, 11, 1); 38 | DateComponents date = DateComponents.From(DateTime.Now); 39 | ``` 40 | 41 | #### Calculation parameters 42 | 43 | The rest of the needed information is contained within the `CalculationParameters` class. Instead of manually initializing this class, it is recommended to use one of the pre-populated instances in the `CalculationMethod` class. You can then further customize the calculation parameters if needed. 44 | 45 | ```csharp 46 | CalculationParameters calcParams = 47 | CalculationMethod.MUSLIM_WORLD_LEAGUE.GetParameters(); 48 | calcParams.Madhab = Madhab.HANAFI; 49 | calcParams.Adjustments.Fajr = 2; 50 | ``` 51 | 52 | | Parameter | Description | 53 | | --------- | ----------- | 54 | | `Method` | CalculationMethod name | 55 | | `FajrAngle` | Angle of the sun used to calculate Fajr | 56 | | `IshaAngle` | Angle of the sun used to calculate Isha | 57 | | `IshaInterval` | Minutes after Maghrib (if set, the time for Isha will be Maghrib plus ishaInterval) | 58 | | `Madhab` | Value from the Madhab object, used to calculate Asr | 59 | | `HighLatitudeRule` | Value from the HighLatitudeRule object, used to set a minimum time for Fajr and a max time for Isha | 60 | | `Adjustments` | Custom prayer time adjustments in minutes for each prayer time | 61 | 62 | **CalculationMethod** 63 | 64 | | Value | Description | 65 | | ----- | ----------- | 66 | | `MUSLIM_WORLD_LEAGUE` | Muslim World League. Fajr angle: 18, Isha angle: 17 | 67 | | `EGYPTIAN` | Egyptian General Authority of Survey. Fajr angle: 19.5, Isha angle: 17.5 | 68 | | `KARACHI` | University of Islamic Sciences, Karachi. Fajr angle: 18, Isha angle: 18 | 69 | | `UMM_AL_QURA` | Umm al-Qura University, Makkah. Fajr angle: 18, Isha interval: 90. *Note: you should add a +30 minute custom adjustment for Isha during Ramadan.* | 70 | | `DUBAI` | Method used in UAE. Fajr and Isha angles of 18.2 degrees. | 71 | | `QATAR` | Modified version of Umm al-Qura used in Qatar. Fajr angle: 18, Isha interval: 90. | 72 | | `KUWAIT` | Method used by the country of Kuwait. Fajr angle: 18, Isha angle: 17.5 | 73 | | `MOONSIGHTING_COMMITTEE` | Moonsighting Committee. Fajr angle: 18, Isha angle: 18. Also uses seasonal adjustment values. | 74 | | `SINGAPORE` | Method used by Singapore. Fajr angle: 20, Isha angle: 18. | 75 | | `NORTH_AMERICA` | Referred to as the ISNA method. This method is included for completeness but is not recommended. Fajr angle: 15, Isha angle: 15 | 76 | | `KUWAIT` | Kuwait. Fajr angle: 18, Isha angle: 17.5 | 77 | | `OTHER` | Fajr angle: 0, Isha angle: 0. This is the default value for `method` when initializing a `CalculationParameters` object. | 78 | 79 | **Madhab** 80 | 81 | | Value | Description | 82 | | ----- | ----------- | 83 | | `SHAFI` | Earlier Asr time | 84 | | `HANAFI` | Later Asr time | 85 | 86 | **HighLatitudeRule** 87 | 88 | | Value | Description | 89 | | ----- | ----------- | 90 | | `MIDDLE_OF_THE_NIGHT` | Fajr will never be earlier than the middle of the night and Isha will never be later than the middle of the night | 91 | | `SEVENTH_OF_THE_NIGHT` | Fajr will never be earlier than the beginning of the last seventh of the night and Isha will never be later than the end of the first seventh of the night | 92 | | `TWILIGHT_ANGLE` | Similar to `SEVENTH_OF_THE_NIGHT`, but instead of 1/7, the fraction of the night used is fajrAngle/60 and ishaAngle/60 | 93 | 94 | 95 | ### Prayer Times 96 | 97 | Once the `PrayerTimes` object has been initialized it will contain values for all five prayer times and the time for sunrise. The prayer times will be DateTime object instances initialized with UTC values. To display these times for the local timezone, a formatting and timezone conversion formatter should be used, for example `TimeZoneInfo`. 98 | 99 | ```csharp 100 | TimeZoneInfo easternTime = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); 101 | TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Fajr, easternTime) 102 | ``` 103 | 104 | ### Qibla 105 | 106 | As of version 1.1.0, this library provides a `Qibla` class for getting the qibla for a given location. 107 | 108 | ```java 109 | Coordinates coordinates = new Coordinates(latitude, longitude); 110 | Qibla qibla = new Qibla(coordinates); 111 | // qibla.direction is the qibla direction 112 | ``` 113 | 114 | ## Full Example 115 | 116 | See an example in the `Adhan.Samples` module. 117 | 118 | ```csharp 119 | static void Main(string[] args) 120 | { 121 | Coordinates coordinates = new Coordinates(43.61, -79.70); 122 | DateComponents dateComponents = DateComponents.From(DateTime.Now); 123 | CalculationParameters parameters = CalculationMethod.NORTH_AMERICA.GetParameters(); 124 | 125 | TimeZoneInfo easternTime = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); 126 | 127 | PrayerTimes prayerTimes = new PrayerTimes(coordinates, dateComponents, parameters); 128 | Console.WriteLine("Fajr : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Fajr, easternTime)); 129 | Console.WriteLine("Sunrise: " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Sunrise, easternTime)); 130 | Console.WriteLine("Dhuhr : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Dhuhr, easternTime)); 131 | Console.WriteLine("Asr : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Asr, easternTime)); 132 | Console.WriteLine("Maghrib: " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Maghrib, easternTime)); 133 | Console.WriteLine("Isha : " + TimeZoneInfo.ConvertTimeFromUtc(prayerTimes.Isha, easternTime)); 134 | } 135 | ``` 136 | 137 | [badge-travis]: https://travis-ci.org/davidpet86/Adhan-csharp.svg?branch=master 138 | [travis]: https://travis-ci.org/davidpet86/Adhan-csharp 139 | -------------------------------------------------------------------------------- /src/Adhan.Test/PrayerTimesTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | using Batoulapps.Adhan; 5 | using Batoulapps.Adhan.Internal; 6 | using Adhan.Test.Internal; 7 | 8 | namespace Adhan.Test 9 | { 10 | [TestClass] 11 | public class PrayerTimesTest 12 | { 13 | [TestMethod] 14 | public void DaysSinceSolstice() 15 | { 16 | DaysSinceSolsticeTest(11, /* year */ 2016, /* month */ 1, /* day */ 1, /* latitude */ 1); 17 | DaysSinceSolsticeTest(10, /* year */ 2015, /* month */ 12, /* day */ 31, /* latitude */ 1); 18 | DaysSinceSolsticeTest(10, /* year */ 2016, /* month */ 12, /* day */ 31, /* latitude */ 1); 19 | DaysSinceSolsticeTest(0, /* year */ 2016, /* month */ 12, /* day */ 21, /* latitude */ 1); 20 | DaysSinceSolsticeTest(1, /* year */ 2016, /* month */ 12, /* day */ 22, /* latitude */ 1); 21 | DaysSinceSolsticeTest(71, /* year */ 2016, /* month */ 3, /* day */ 1, /* latitude */ 1); 22 | DaysSinceSolsticeTest(70, /* year */ 2015, /* month */ 3, /* day */ 1, /* latitude */ 1); 23 | DaysSinceSolsticeTest(365, /* year */ 2016, /* month */ 12, /* day */ 20, /* latitude */ 1); 24 | DaysSinceSolsticeTest(364, /* year */ 2015, /* month */ 12, /* day */ 20, /* latitude */ 1); 25 | 26 | DaysSinceSolsticeTest(0, /* year */ 2015, /* month */ 6, /* day */ 21, /* latitude */ -1); 27 | DaysSinceSolsticeTest(0, /* year */ 2016, /* month */ 6, /* day */ 21, /* latitude */ -1); 28 | DaysSinceSolsticeTest(364, /* year */ 2015, /* month */ 6, /* day */ 20, /* latitude */ -1); 29 | DaysSinceSolsticeTest(365, /* year */ 2016, /* month */ 6, /* day */ 20, /* latitude */ -1); 30 | } 31 | 32 | 33 | [TestMethod] 34 | public void PrayerTimesAmericaNewYork() 35 | { 36 | TimeZoneInfo timezone = TestUtils.GetTimeZone("America/New_York"); 37 | 38 | DateComponents date = new DateComponents(2015, 7, 12); 39 | CalculationParameters calcParams = CalculationMethod.NORTH_AMERICA.GetParameters(); 40 | calcParams.Madhab = Madhab.HANAFI; 41 | 42 | Coordinates coordinates = new Coordinates(35.7750, -78.6336); 43 | PrayerTimes prayerTimes = new PrayerTimes(coordinates, date, calcParams); 44 | 45 | Assert.IsTrue(GetHour(prayerTimes.Fajr, timezone) == "04:42 AM"); 46 | Assert.IsTrue(GetHour(prayerTimes.Sunrise, timezone) == "06:08 AM"); 47 | Assert.IsTrue(GetHour(prayerTimes.Dhuhr, timezone) == "01:21 PM"); 48 | Assert.IsTrue(GetHour(prayerTimes.Asr, timezone) == "06:22 PM"); 49 | Assert.IsTrue(GetHour(prayerTimes.Maghrib, timezone) == "08:32 PM"); 50 | Assert.IsTrue(GetHour(prayerTimes.Isha, timezone) == "09:57 PM"); 51 | } 52 | 53 | [TestMethod] 54 | public void OffsetTests() 55 | { 56 | TimeZoneInfo timezone = TestUtils.GetTimeZone("America/New_York"); 57 | 58 | DateComponents date = new DateComponents(2015, 12, 1); 59 | Coordinates coordinates = new Coordinates(35.7750, -78.6336); 60 | CalculationParameters parameters = CalculationMethod.MUSLIM_WORLD_LEAGUE.GetParameters(); 61 | 62 | PrayerTimes prayerTimes = new PrayerTimes(coordinates, date, parameters); 63 | Assert.IsTrue(GetHour(prayerTimes.Fajr, timezone) == "05:35 AM"); 64 | Assert.IsTrue(GetHour(prayerTimes.Sunrise, timezone) == "07:06 AM"); 65 | Assert.IsTrue(GetHour(prayerTimes.Dhuhr, timezone) == "12:05 PM"); 66 | Assert.IsTrue(GetHour(prayerTimes.Asr, timezone) == "02:42 PM"); 67 | Assert.IsTrue(GetHour(prayerTimes.Maghrib, timezone) == "05:01 PM"); 68 | Assert.IsTrue(GetHour(prayerTimes.Isha, timezone) == "06:26 PM"); 69 | 70 | parameters.Adjustments.Fajr = 10; 71 | parameters.Adjustments.Sunrise = 10; 72 | parameters.Adjustments.Dhuhr = 10; 73 | parameters.Adjustments.Asr = 10; 74 | parameters.Adjustments.Maghrib = 10; 75 | parameters.Adjustments.Isha = 10; 76 | 77 | prayerTimes = new PrayerTimes(coordinates, date, parameters); 78 | Assert.IsTrue(GetHour(prayerTimes.Fajr, timezone) == "05:45 AM"); 79 | Assert.IsTrue(GetHour(prayerTimes.Sunrise, timezone) == "07:16 AM"); 80 | Assert.IsTrue(GetHour(prayerTimes.Dhuhr, timezone) == "12:15 PM"); 81 | Assert.IsTrue(GetHour(prayerTimes.Asr, timezone) == "02:52 PM"); 82 | Assert.IsTrue(GetHour(prayerTimes.Maghrib, timezone) == "05:11 PM"); 83 | Assert.IsTrue(GetHour(prayerTimes.Isha, timezone) == "06:36 PM"); 84 | 85 | parameters.Adjustments = new PrayerAdjustments(); 86 | prayerTimes = new PrayerTimes(coordinates, date, parameters); 87 | Assert.IsTrue(GetHour(prayerTimes.Fajr, timezone) == "05:35 AM"); 88 | Assert.IsTrue(GetHour(prayerTimes.Sunrise, timezone) == "07:06 AM"); 89 | Assert.IsTrue(GetHour(prayerTimes.Dhuhr, timezone) == "12:05 PM"); 90 | Assert.IsTrue(GetHour(prayerTimes.Asr, timezone) == "02:42 PM"); 91 | Assert.IsTrue(GetHour(prayerTimes.Maghrib, timezone) == "05:01 PM"); 92 | Assert.IsTrue(GetHour(prayerTimes.Isha, timezone) == "06:26 PM"); 93 | } 94 | 95 | [TestMethod] 96 | public void MoonsightingMethod() 97 | { 98 | TimeZoneInfo timezone = TestUtils.GetTimeZone("America/New_York"); 99 | 100 | DateComponents date = new DateComponents(2016, 1, 31); 101 | Coordinates coordinates = new Coordinates(35.7750, -78.6336); 102 | PrayerTimes prayerTimes = new PrayerTimes( 103 | coordinates, date, CalculationMethod.MOON_SIGHTING_COMMITTEE.GetParameters()); 104 | 105 | Assert.IsTrue(GetHour(prayerTimes.Fajr, timezone) == "05:48 AM"); 106 | Assert.IsTrue(GetHour(prayerTimes.Sunrise, timezone) == "07:16 AM"); 107 | Assert.IsTrue(GetHour(prayerTimes.Dhuhr, timezone) == "12:33 PM"); 108 | Assert.IsTrue(GetHour(prayerTimes.Asr, timezone) == "03:20 PM"); 109 | Assert.IsTrue(GetHour(prayerTimes.Maghrib, timezone) == "05:43 PM"); 110 | Assert.IsTrue(GetHour(prayerTimes.Isha, timezone) == "07:05 PM"); 111 | } 112 | 113 | [TestMethod] 114 | public void MoonsightingMethodHighLat() 115 | { 116 | TimeZoneInfo timezone = TestUtils.GetTimeZone("Europe/Oslo"); 117 | 118 | // Values from http://www.moonsighting.com/pray.php 119 | DateComponents date = new DateComponents(2016, 1, 1); 120 | CalculationParameters parameters = CalculationMethod.MOON_SIGHTING_COMMITTEE.GetParameters(); 121 | parameters.Madhab = Madhab.HANAFI; 122 | Coordinates coordinates = new Coordinates(59.9094, 10.7349); 123 | 124 | PrayerTimes prayerTimes = new PrayerTimes(coordinates, date, parameters); 125 | 126 | Assert.IsTrue(GetHour(prayerTimes.Fajr, timezone) == "07:34 AM"); 127 | Assert.IsTrue(GetHour(prayerTimes.Sunrise, timezone) == "09:19 AM"); 128 | Assert.IsTrue(GetHour(prayerTimes.Dhuhr, timezone) == "12:25 PM"); 129 | Assert.IsTrue(GetHour(prayerTimes.Asr, timezone) == "01:36 PM"); 130 | Assert.IsTrue(GetHour(prayerTimes.Maghrib, timezone) == "03:25 PM"); 131 | Assert.IsTrue(GetHour(prayerTimes.Isha, timezone) == "05:02 PM"); 132 | } 133 | 134 | [TestMethod] 135 | public void TimeForPrayer() 136 | { 137 | DateComponents components = new DateComponents(2016, 7, 1); 138 | CalculationParameters parameters = CalculationMethod.MUSLIM_WORLD_LEAGUE.GetParameters(); 139 | parameters.Madhab = Madhab.HANAFI; 140 | parameters.HighLatitudeRule = HighLatitudeRule.TWILIGHT_ANGLE; 141 | Coordinates coordinates = new Coordinates(59.9094, 10.7349); 142 | 143 | PrayerTimes p = new PrayerTimes(coordinates, components, parameters); 144 | Assert.IsTrue(p.Fajr == p.TimeForPrayer(Prayer.FAJR)); 145 | Assert.IsTrue(p.Sunrise == p.TimeForPrayer(Prayer.SUNRISE)); 146 | Assert.IsTrue(p.Dhuhr == p.TimeForPrayer(Prayer.DHUHR)); 147 | Assert.IsTrue(p.Asr == p.TimeForPrayer(Prayer.ASR)); 148 | Assert.IsTrue(p.Maghrib == p.TimeForPrayer(Prayer.MAGHRIB)); 149 | Assert.IsTrue(p.Isha == p.TimeForPrayer(Prayer.ISHA)); 150 | Assert.IsNull(p.TimeForPrayer(Prayer.NONE)); 151 | } 152 | 153 | [TestMethod] 154 | public void CurrentPrayer() 155 | { 156 | DateComponents components = new DateComponents(2015, 9, 1); 157 | CalculationParameters parameters = CalculationMethod.KARACHI.GetParameters(); 158 | parameters.Madhab = Madhab.HANAFI; 159 | parameters.HighLatitudeRule = HighLatitudeRule.TWILIGHT_ANGLE; 160 | Coordinates coordinates = new Coordinates(33.720817, 73.090032); 161 | 162 | PrayerTimes p = new PrayerTimes(coordinates, components, parameters); 163 | 164 | Assert.IsTrue(p.CurrentPrayer(TestUtils.AddSeconds(p.Fajr, -1)) == Prayer.NONE); 165 | Assert.IsTrue(p.CurrentPrayer(p.Fajr) == Prayer.FAJR); 166 | Assert.IsTrue(p.CurrentPrayer(TestUtils.AddSeconds(p.Fajr, 1)) == Prayer.FAJR); 167 | Assert.IsTrue(p.CurrentPrayer(TestUtils.AddSeconds(p.Sunrise, 1)) == Prayer.SUNRISE); 168 | Assert.IsTrue(p.CurrentPrayer(TestUtils.AddSeconds(p.Dhuhr, 1)) == Prayer.DHUHR); 169 | Assert.IsTrue(p.CurrentPrayer(TestUtils.AddSeconds(p.Asr, 1)) == Prayer.ASR); 170 | Assert.IsTrue(p.CurrentPrayer(TestUtils.AddSeconds(p.Maghrib, 1)) == Prayer.MAGHRIB); 171 | Assert.IsTrue(p.CurrentPrayer(TestUtils.AddSeconds(p.Isha, 1)) == Prayer.ISHA); 172 | } 173 | 174 | [TestMethod] 175 | public void NextPrayer() 176 | { 177 | DateComponents components = new DateComponents(2015, 9, 1); 178 | CalculationParameters parameters = CalculationMethod.KARACHI.GetParameters(); 179 | parameters.Madhab = Madhab.HANAFI; 180 | parameters.HighLatitudeRule = HighLatitudeRule.TWILIGHT_ANGLE; 181 | Coordinates coordinates = new Coordinates(33.720817, 73.090032); 182 | 183 | PrayerTimes p = new PrayerTimes(coordinates, components, parameters); 184 | 185 | Assert.IsTrue(p.NextPrayer(TestUtils.AddSeconds(p.Fajr, -1)) == Prayer.FAJR); 186 | Assert.IsTrue(p.NextPrayer(p.Fajr) == Prayer.SUNRISE); 187 | Assert.IsTrue(p.NextPrayer(TestUtils.AddSeconds(p.Fajr, 1)) == Prayer.SUNRISE); 188 | Assert.IsTrue(p.NextPrayer(TestUtils.AddSeconds(p.Sunrise, 1)) == Prayer.DHUHR); 189 | Assert.IsTrue(p.NextPrayer(TestUtils.AddSeconds(p.Dhuhr, 1)) == Prayer.ASR); 190 | Assert.IsTrue(p.NextPrayer(TestUtils.AddSeconds(p.Asr, 1)) == Prayer.MAGHRIB); 191 | Assert.IsTrue(p.NextPrayer(TestUtils.AddSeconds(p.Maghrib, 1)) == Prayer.ISHA); 192 | Assert.IsTrue(p.NextPrayer(TestUtils.AddSeconds(p.Isha, 1)) == Prayer.NONE); 193 | } 194 | 195 | private void DaysSinceSolsticeTest(int value, int year, int month, int day, double latitude) 196 | { 197 | // For Northern Hemisphere start from December 21 198 | // (DYY=0 for December 21, and counting forward, DYY=11 for January 1 and so on). 199 | // For Southern Hemisphere start from June 21 200 | // (DYY=0 for June 21, and counting forward) 201 | DateTime date = TestUtils.MakeDate(year, month, day); 202 | int dayOfYear = TestUtils.GetDayOfYear(date); 203 | Assert.IsTrue(PrayerTimes.DaysSinceSolstice(dayOfYear, date.Year, latitude) == value); 204 | } 205 | 206 | private string GetHour(DateTime inputDateTime, TimeZoneInfo destinationTimeZoneInfo) 207 | { 208 | DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(inputDateTime, destinationTimeZoneInfo); 209 | return localDateTime.ToString("hh:mm tt"); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/Adhan/PrayerTimes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Batoulapps.Adhan.Internal; 3 | 4 | namespace Batoulapps.Adhan 5 | { 6 | public class PrayerTimes 7 | { 8 | public readonly DateTime Fajr; 9 | public readonly DateTime Sunrise; 10 | public readonly DateTime Dhuhr; 11 | public readonly DateTime Asr; 12 | public readonly DateTime Maghrib; 13 | public readonly DateTime Isha; 14 | 15 | /// 16 | /// Calculate PrayerTimes 17 | /// 18 | /// the coordinates of the location 19 | /// the date components for that location 20 | /// he parameters for the calculation 21 | public PrayerTimes(Coordinates coordinates, DateComponents date, CalculationParameters parameters) 22 | : this (coordinates, CalendarUtil.ResolveTime(date), parameters) 23 | {} 24 | 25 | private PrayerTimes(Coordinates coordinates, DateTime date, CalculationParameters parameters) 26 | { 27 | DateTime? tempFajr = null; 28 | DateTime? tempSunrise = null; 29 | DateTime? tempDhuhr = null; 30 | DateTime? tempAsr = null; 31 | DateTime? tempMaghrib = null; 32 | DateTime? tempIsha = null; 33 | 34 | //DateTime calendar = date.ToUniversalTime(); 35 | int year = date.Year; 36 | int dayOfYear = date.DayOfYear; 37 | 38 | SolarTime solarTime = new SolarTime(date, coordinates); 39 | 40 | TimeComponents timeComponents = TimeComponents.FromDouble(solarTime.Transit); 41 | DateTime? transit = timeComponents?.DateComponents(date); 42 | 43 | timeComponents = TimeComponents.FromDouble(solarTime.Sunrise); 44 | DateTime? sunriseComponents = timeComponents?.DateComponents(date); 45 | 46 | timeComponents = TimeComponents.FromDouble(solarTime.Sunset); 47 | DateTime? sunsetComponents = timeComponents?.DateComponents(date); 48 | 49 | bool error = transit == null || sunriseComponents == null || sunsetComponents == null; 50 | 51 | if (!error) 52 | { 53 | tempDhuhr = transit; 54 | tempSunrise = sunriseComponents; 55 | tempMaghrib = sunsetComponents; 56 | 57 | timeComponents = TimeComponents.FromDouble( 58 | solarTime.Afternoon(parameters.Madhab.GetShadowLength())); 59 | 60 | if (timeComponents != null) 61 | { 62 | tempAsr = timeComponents.DateComponents(date); 63 | } 64 | 65 | // get night length 66 | DateTime tomorrowSunrise = sunriseComponents.Value.AddDays(1); 67 | double night = tomorrowSunrise.GetTime() - sunsetComponents.Value.GetTime(); 68 | 69 | timeComponents = TimeComponents 70 | .FromDouble(solarTime.HourAngle(-parameters.FajrAngle, false)); 71 | 72 | if (timeComponents != null) 73 | { 74 | tempFajr = timeComponents.DateComponents(date); 75 | } 76 | 77 | if (parameters.Method == CalculationMethod.MOON_SIGHTING_COMMITTEE && 78 | coordinates.Latitude >= 55) 79 | { 80 | tempFajr = sunriseComponents.Value.AddSeconds(-1 * (int) (night / 7000)); 81 | } 82 | 83 | NightPortions nightPortions = parameters.NightPortions(); 84 | 85 | DateTime safeFajr; 86 | if (parameters.Method == CalculationMethod.MOON_SIGHTING_COMMITTEE) 87 | { 88 | safeFajr = SeasonAdjustedMorningTwilight(coordinates.Latitude, dayOfYear, year, sunriseComponents.Value); 89 | } 90 | else 91 | { 92 | double portion = nightPortions.Fajr; 93 | long nightFraction = (long) (portion * night / 1000); 94 | safeFajr = sunriseComponents.Value.AddSeconds(-1 * (int) nightFraction); 95 | } 96 | 97 | if (tempFajr == null || tempFajr.Value.Before(safeFajr)) { 98 | tempFajr = safeFajr; 99 | } 100 | 101 | // Isha calculation with check against safe value 102 | if (parameters.IshaInterval > 0) 103 | { 104 | tempIsha = tempMaghrib.Value.AddSeconds(parameters.IshaInterval * 60); 105 | } 106 | else 107 | { 108 | timeComponents = TimeComponents.FromDouble( 109 | solarTime.HourAngle(-parameters.IshaAngle, true)); 110 | 111 | if (timeComponents != null) 112 | { 113 | tempIsha = timeComponents.DateComponents(date); 114 | } 115 | 116 | if (parameters.Method == CalculationMethod.MOON_SIGHTING_COMMITTEE && 117 | coordinates.Latitude >= 55) 118 | { 119 | long nightFraction = (long) night / 7000; 120 | tempIsha = sunsetComponents.Value.AddSeconds(nightFraction); 121 | } 122 | 123 | DateTime safeIsha; 124 | if (parameters.Method == CalculationMethod.MOON_SIGHTING_COMMITTEE) 125 | { 126 | safeIsha = PrayerTimes.SeasonAdjustedEveningTwilight( 127 | coordinates.Latitude, dayOfYear, year, sunsetComponents.Value); 128 | } 129 | else 130 | { 131 | double portion = nightPortions.Isha; 132 | long nightFraction = (long) (portion * night / 1000); 133 | safeIsha = sunsetComponents.Value.AddSeconds(nightFraction); 134 | } 135 | 136 | if (tempIsha == null || (tempIsha.Value.After(safeIsha))) 137 | { 138 | tempIsha = safeIsha; 139 | } 140 | } 141 | } 142 | 143 | if (error || tempAsr == null) 144 | { 145 | // if we don't have all prayer times then initialization failed 146 | this.Fajr = DateTime.MinValue; 147 | this.Sunrise = DateTime.MinValue; 148 | this.Dhuhr = DateTime.MinValue; 149 | this.Asr = DateTime.MinValue; 150 | this.Maghrib = DateTime.MinValue; 151 | this.Isha = DateTime.MinValue; 152 | } 153 | else 154 | { 155 | // Assign final times to public struct members with all offsets 156 | this.Fajr = CalendarUtil.RoundedMinute(tempFajr.Value.AddMinutes(parameters.Adjustments.Fajr + parameters.MethodAdjustments.Fajr)); 157 | this.Sunrise = CalendarUtil.RoundedMinute(tempSunrise.Value.AddMinutes(parameters.Adjustments.Sunrise + parameters.MethodAdjustments.Sunrise)); 158 | this.Dhuhr = CalendarUtil.RoundedMinute(tempDhuhr.Value.AddMinutes(parameters.Adjustments.Dhuhr + parameters.MethodAdjustments.Dhuhr)); 159 | this.Asr = CalendarUtil.RoundedMinute(tempAsr.Value.AddMinutes(parameters.Adjustments.Asr + parameters.MethodAdjustments.Asr)); 160 | this.Maghrib = CalendarUtil.RoundedMinute(tempMaghrib.Value.AddMinutes(parameters.Adjustments.Maghrib + parameters.MethodAdjustments.Maghrib)); 161 | this.Isha = CalendarUtil.RoundedMinute(tempIsha.Value.AddMinutes(parameters.Adjustments.Isha + parameters.MethodAdjustments.Isha)); 162 | } 163 | } 164 | 165 | public Prayer CurrentPrayer() 166 | { 167 | return CurrentPrayer(new DateTime()); 168 | } 169 | 170 | public Prayer CurrentPrayer(DateTime time) 171 | { 172 | double when = time.GetTime(); 173 | 174 | if (this.Isha.GetTime() - when <= 0) 175 | { 176 | return Prayer.ISHA; 177 | } 178 | else if (this.Maghrib.GetTime() - when <= 0) 179 | { 180 | return Prayer.MAGHRIB; 181 | } 182 | else if (this.Asr.GetTime() - when <= 0) 183 | { 184 | return Prayer.ASR; 185 | } 186 | else if (this.Dhuhr.GetTime() - when <= 0) 187 | { 188 | return Prayer.DHUHR; 189 | } 190 | else if (this.Sunrise.GetTime() - when <= 0) 191 | { 192 | return Prayer.SUNRISE; 193 | } 194 | else if (this.Fajr.GetTime() - when <= 0) 195 | { 196 | return Prayer.FAJR; 197 | } 198 | else 199 | { 200 | return Prayer.NONE; 201 | } 202 | } 203 | 204 | public Prayer NextPrayer() 205 | { 206 | return NextPrayer(new DateTime()); 207 | } 208 | 209 | public Prayer NextPrayer(DateTime time) 210 | { 211 | double when = time.GetTime(); 212 | 213 | if (this.Isha.GetTime() - when <= 0) 214 | { 215 | return Prayer.NONE; 216 | } 217 | else if (this.Maghrib.GetTime() - when <= 0) 218 | { 219 | return Prayer.ISHA; 220 | } 221 | else if (this.Asr.GetTime() - when <= 0) 222 | { 223 | return Prayer.MAGHRIB; 224 | } 225 | else if (this.Dhuhr.GetTime() - when <= 0) 226 | { 227 | return Prayer.ASR; 228 | } 229 | else if (this.Sunrise.GetTime() - when <= 0) 230 | { 231 | return Prayer.DHUHR; 232 | } 233 | else if (this.Fajr.GetTime() - when <= 0) 234 | { 235 | return Prayer.SUNRISE; 236 | } 237 | else 238 | { 239 | return Prayer.FAJR; 240 | } 241 | } 242 | 243 | public DateTime? TimeForPrayer(Prayer prayer) 244 | { 245 | switch (prayer) { 246 | case Prayer.FAJR: 247 | return this.Fajr; 248 | case Prayer.SUNRISE: 249 | return this.Sunrise; 250 | case Prayer.DHUHR: 251 | return this.Dhuhr; 252 | case Prayer.ASR: 253 | return this.Asr; 254 | case Prayer.MAGHRIB: 255 | return this.Maghrib; 256 | case Prayer.ISHA: 257 | return this.Isha; 258 | case Prayer.NONE: 259 | default: 260 | return null; 261 | } 262 | } 263 | 264 | private static DateTime SeasonAdjustedMorningTwilight( 265 | double latitude, int day, int year, DateTime sunrise) 266 | { 267 | double a = 75 + ((28.65 / 55.0) * Math.Abs(latitude)); 268 | double b = 75 + ((19.44 / 55.0) * Math.Abs(latitude)); 269 | double c = 75 + ((32.74 / 55.0) * Math.Abs(latitude)); 270 | double d = 75 + ((48.10 / 55.0) * Math.Abs(latitude)); 271 | 272 | double adjustment; 273 | int dyy = PrayerTimes.DaysSinceSolstice(day, year, latitude); 274 | if ( dyy < 91) 275 | { 276 | adjustment = a + ( b - a ) / 91.0 * dyy; 277 | } 278 | else if ( dyy < 137) 279 | { 280 | adjustment = b + ( c - b ) / 46.0 * ( dyy - 91 ); 281 | } 282 | else if ( dyy < 183 ) 283 | { 284 | adjustment = c + ( d - c ) / 46.0 * ( dyy - 137 ); 285 | } 286 | else if ( dyy < 229 ) 287 | { 288 | adjustment = d + ( c - d ) / 46.0 * ( dyy - 183 ); 289 | } 290 | else if ( dyy < 275 ) 291 | { 292 | adjustment = c + ( b - c ) / 46.0 * ( dyy - 229 ); 293 | } else 294 | { 295 | adjustment = b + ( a - b ) / 91.0 * ( dyy - 275 ); 296 | } 297 | 298 | return sunrise.AddSeconds(-(int) Math.Round(adjustment * 60.0)); 299 | } 300 | 301 | private static DateTime SeasonAdjustedEveningTwilight( 302 | double latitude, int day, int year, DateTime sunset) 303 | { 304 | double a = 75 + ((25.60 / 55.0) * Math.Abs(latitude)); 305 | double b = 75 + ((2.050 / 55.0) * Math.Abs(latitude)); 306 | double c = 75 - ((9.210 / 55.0) * Math.Abs(latitude)); 307 | double d = 75 + ((6.140 / 55.0) * Math.Abs(latitude)); 308 | 309 | double adjustment; 310 | int dyy = PrayerTimes.DaysSinceSolstice(day, year, latitude); 311 | if ( dyy < 91) 312 | { 313 | adjustment = a + ( b - a ) / 91.0 * dyy; 314 | } 315 | else if ( dyy < 137) 316 | { 317 | adjustment = b + ( c - b ) / 46.0 * ( dyy - 91 ); 318 | } 319 | else if ( dyy < 183 ) 320 | { 321 | adjustment = c + ( d - c ) / 46.0 * ( dyy - 137 ); 322 | } 323 | else if ( dyy < 229 ) 324 | { 325 | adjustment = d + ( c - d ) / 46.0 * ( dyy - 183 ); 326 | } 327 | else if ( dyy < 275 ) 328 | { 329 | adjustment = c + ( b - c ) / 46.0 * ( dyy - 229 ); 330 | } 331 | else 332 | { 333 | adjustment = b + ( a - b ) / 91.0 * ( dyy - 275 ); 334 | } 335 | 336 | return sunset.AddSeconds((int) Math.Round(adjustment * 60.0)); 337 | } 338 | 339 | public static int DaysSinceSolstice(int dayOfYear, int year, double latitude) 340 | { 341 | int daysSinceSolistice; 342 | int northernOffset = 10; 343 | bool isLeapYear = DateTime.IsLeapYear(year); 344 | int southernOffset = isLeapYear ? 173 : 172; 345 | int daysInYear = isLeapYear ? 366 : 365; 346 | 347 | if (latitude >= 0) 348 | { 349 | daysSinceSolistice = dayOfYear + northernOffset; 350 | if (daysSinceSolistice >= daysInYear) 351 | { 352 | daysSinceSolistice = daysSinceSolistice - daysInYear; 353 | } 354 | } 355 | else 356 | { 357 | daysSinceSolistice = dayOfYear - southernOffset; 358 | if (daysSinceSolistice < 0) 359 | { 360 | daysSinceSolistice = daysSinceSolistice + daysInYear; 361 | } 362 | } 363 | 364 | return daysSinceSolistice; 365 | } 366 | } 367 | } -------------------------------------------------------------------------------- /src/Adhan.Test/Internal/AstronomicalTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | using Batoulapps.Adhan; 4 | using Batoulapps.Adhan.Internal; 5 | using System.IO; 6 | using Adhan.Test.Data; 7 | using System; 8 | using Adhan.Test.Internal; 9 | 10 | namespace Adhan.Test.Internal 11 | { 12 | [TestClass] 13 | public class AstronomicalTest 14 | { 15 | [TestMethod] 16 | public void SolarCoordinates() 17 | { 18 | 19 | // values from Astronomical Algorithms page 165 20 | 21 | double jd = CalendricalHelper.JulianDay(/* year */ 1992, /* month */ 10, /* day */ 13); 22 | SolarCoordinates solar = new SolarCoordinates(/* JulianDay */ jd); 23 | 24 | double T = CalendricalHelper.JulianCentury(/* JulianDay */ jd); 25 | double L0 = Astronomical.MeanSolarLongitude(/* julianCentury */ T); 26 | double ε0 = Astronomical.MeanObliquityOfTheEcliptic(/* julianCentury */ T); 27 | double εapp = Astronomical.ApparentObliquityOfTheEcliptic( 28 | /* julianCentury */ T, /* meanObliquityOfTheEcliptic */ ε0); 29 | double M = Astronomical.MeanSolarAnomaly(/* julianCentury */ T); 30 | double C = Astronomical.SolarEquationOfTheCenter( 31 | /* julianCentury */ T, /* meanAnomaly */ M); 32 | double λ = Astronomical.ApparentSolarLongitude( 33 | /* julianCentury */ T, /* meanLongitude */ L0); 34 | double δ = solar.Declination; 35 | double α = DoubleUtil.UnwindAngle(solar.RightAscension); 36 | 37 | Assert.IsTrue(T.IsWithin(0.00000000001, (-0.072183436))); 38 | Assert.IsTrue(L0.IsWithin(0.00001, (201.80720))); 39 | Assert.IsTrue(ε0.IsWithin(0.00001, (23.44023))); 40 | Assert.IsTrue(εapp.IsWithin(0.00001, (23.43999))); 41 | Assert.IsTrue(M.IsWithin(0.00001, (278.99397))); 42 | Assert.IsTrue(C.IsWithin(0.00001, (-1.89732))); 43 | 44 | // lower accuracy than desired 45 | Assert.IsTrue(λ.IsWithin(0.00002, (199.90895))); 46 | Assert.IsTrue(δ.IsWithin(0.00001, (-7.78507))); 47 | Assert.IsTrue(α.IsWithin(0.00001, (198.38083))); 48 | 49 | // values from Astronomical Algorithms page 88 50 | 51 | jd = CalendricalHelper.JulianDay(/* year */ 1987, /* month */ 4, /* day */ 10); 52 | solar = new SolarCoordinates(/* JulianDay */ jd); 53 | T = CalendricalHelper.JulianCentury(/* JulianDay */ jd); 54 | 55 | double θ0 = Astronomical.MeanSiderealTime(/* julianCentury */ T); 56 | double θapp = solar.ApparentSiderealTime; 57 | double Ω = Astronomical.AscendingLunarNodeLongitude(/* julianCentury */ T); 58 | ε0 = Astronomical.MeanObliquityOfTheEcliptic(/* julianCentury */ T); 59 | L0 = Astronomical.MeanSolarLongitude(/* julianCentury */ T); 60 | double Lp = Astronomical.MeanLunarLongitude(/* julianCentury */ T); 61 | double ΔΨ = Astronomical.NutationInLongitude(/* julianCentury */ T, 62 | /* solarLongitude */ L0, /* lunarLongitude */ Lp, /* ascendingNode */ Ω); 63 | double Δε = Astronomical.NutationInObliquity(/* julianCentury */ T, 64 | /* solarLongitude */ L0, /* lunarLongitude */ Lp, /* ascendingNode */ Ω); 65 | double ε = ε0 + Δε; 66 | 67 | Assert.IsTrue(θ0.IsWithin(0.000001, (197.693195))); 68 | Assert.IsTrue(θapp.IsWithin(0.0001, (197.6922295833))); 69 | 70 | // values from Astronomical Algorithms page 148 71 | 72 | Assert.IsTrue(Ω.IsWithin(0.0001, (11.2531))); 73 | Assert.IsTrue(ΔΨ.IsWithin(0.0001, (-0.0010522))); 74 | Assert.IsTrue(Δε.IsWithin(0.00001, (0.0026230556))); 75 | Assert.IsTrue(ε0.IsWithin(0.000001, (23.4409463889))); 76 | Assert.IsTrue(ε.IsWithin(0.00001, (23.4435694444))); 77 | } 78 | 79 | [TestMethod] 80 | public void RightAscensionEdgeCase() 81 | { 82 | SolarTime previousTime = null; 83 | Coordinates coordinates = new Coordinates(35 + 47.0 / 60.0, -78 - 39.0 / 60.0); 84 | for (int i = 0; i < 365; i++) 85 | { 86 | SolarTime time = new SolarTime( 87 | TestUtils.MakeDateWithOffset(2016, 1, 1, i), coordinates); 88 | if (i > 0) 89 | { 90 | // transit from one day to another should not differ more than one minute 91 | Assert.IsTrue(Math.Abs(time.Transit - previousTime.Transit) < (1.0 / 60.0)); 92 | 93 | // sunrise and sunset from one day to another should not differ more than two minutes 94 | Assert.IsTrue(Math.Abs(time.Sunrise - previousTime.Sunrise) < (2.0 / 60.0)); 95 | Assert.IsTrue(Math.Abs(time.Sunset - previousTime.Sunset) < (2.0 / 60.0)); 96 | } 97 | previousTime = time; 98 | } 99 | } 100 | 101 | [TestMethod] 102 | public void AltitudeOfCelestialBody() 103 | { 104 | double φ = 38 + (55 / 60.0) + (17.0 / 3600); 105 | double δ = -6 - (43 / 60.0) - (11.61 / 3600); 106 | double H = 64.352133; 107 | double h = Astronomical.AltitudeOfCelestialBody( 108 | /* observerLatitude */ φ, /* declination */ δ, /* localHourAngle */ H); 109 | Assert.IsTrue(h.IsWithin(0.0001, 15.1249)); 110 | } 111 | 112 | [TestMethod] 113 | public void TransitAndHourAngle() 114 | { 115 | // values from Astronomical Algorithms page 103 116 | double longitude = -71.0833; 117 | double Θ = 177.74208; 118 | double α1 = 40.68021; 119 | double α2 = 41.73129; 120 | double α3 = 42.78204; 121 | double m0 = Astronomical.ApproximateTransit(longitude, 122 | /* siderealTime */ Θ, /* rightAscension */ α2); 123 | 124 | Assert.IsTrue(m0.IsWithin(0.00001, 0.81965)); 125 | 126 | double transit = Astronomical.CorrectedTransit( 127 | /* approximateTransit */ m0, longitude, /* siderealTime */ Θ, 128 | /* rightAscension */ α2, /* previousRightAscension */ α1, 129 | /* nextRightAscension */ α3) / 24; 130 | 131 | Assert.IsTrue(transit.IsWithin(0.00001, 0.81980)); 132 | 133 | double δ1 = 18.04761; 134 | double δ2 = 18.44092; 135 | double δ3 = 18.82742; 136 | 137 | double rise = Astronomical.CorrectedHourAngle(/* approximateTransit */ m0, 138 | /* angle */ -0.5667, new Coordinates(/* latitude */ 42.3333, longitude), 139 | /* afterTransit */ false, /* siderealTime */ Θ, 140 | /* rightAscension */ α2, /* previousRightAscension */ α1, 141 | /* nextRightAscension */ α3, /* declination */ δ2, 142 | /* previousDeclination */ δ1, /* nextDeclination */ δ3) / 24; 143 | Assert.IsTrue(rise.IsWithin(0.00001, 0.51766)); 144 | } 145 | 146 | [TestMethod] 147 | public void SolarTime() 148 | { 149 | /* 150 | * Comparison values generated from 151 | * http://aa.usno.navy.mil/rstt/onedaytable?form=1&ID=AA&year=2015&month=7&day=12&state=NC&place=raleigh 152 | */ 153 | 154 | Coordinates coordinates = new Coordinates(35 + 47.0 / 60.0, -78 - 39.0 / 60.0); 155 | SolarTime solar = new SolarTime(TestUtils.MakeDate(2015, 7, 12), coordinates); 156 | 157 | double transit = solar.Transit; 158 | double sunrise = solar.Sunrise; 159 | double sunset = solar.Sunset; 160 | double twilightStart = solar.HourAngle(-6, /* afterTransit */ false); 161 | double twilightEnd = solar.HourAngle(-6, /* afterTransit */ true); 162 | double invalid = solar.HourAngle(-36, /* afterTransit */ true); 163 | Assert.IsTrue(TimeString(twilightStart) == "9:38"); 164 | Assert.IsTrue(TimeString(sunrise) == "10:08"); 165 | Assert.IsTrue(TimeString(transit) == "17:20"); 166 | Assert.IsTrue(TimeString(sunset) == "24:32"); 167 | Assert.IsTrue(TimeString(twilightEnd) == "25:02"); 168 | Assert.IsTrue(TimeString(invalid) == ""); 169 | } 170 | 171 | [TestMethod] 172 | public void CalendricalDate() 173 | { 174 | // generated from http://aa.usno.navy.mil/data/docs/RS_OneYear.php for KUKUIHAELE, HAWAII 175 | Coordinates coordinates = new Coordinates( 176 | /* latitude */ 20 + 7.0 / 60.0, /* longitude */ -155.0 - 34.0 / 60.0); 177 | SolarTime day1solar = new SolarTime(TestUtils.MakeDate(2015, 4, /* day */ 2), coordinates); 178 | SolarTime day2solar = new SolarTime(TestUtils.MakeDate(2015, 4, 3), coordinates); 179 | 180 | double day1 = day1solar.Sunrise; 181 | double day2 = day2solar.Sunrise; 182 | 183 | Assert.IsTrue(TimeString(day1) == "16:15"); 184 | Assert.IsTrue(TimeString(day2) == "16:14"); 185 | } 186 | 187 | [TestMethod] 188 | public void Interpolation() 189 | { 190 | // values from Astronomical Algorithms page 25 191 | double interpolatedValue = Astronomical.Interpolate(/* value */ 0.877366, 192 | /* previousValue */ 0.884226, /* nextValue */ 0.870531, /* factor */ 4.35 / 24); 193 | Assert.IsTrue(interpolatedValue.IsWithin(0.000001, 0.876125)); 194 | 195 | double i1 = Astronomical.Interpolate( 196 | /* value */ 1, /* previousValue */ -1, /* nextValue */ 3, /* factor */ 0.6); 197 | Assert.IsTrue(i1.IsWithin(0.000001, 2.2)); 198 | } 199 | 200 | [TestMethod] 201 | public void AngleInterpolation() 202 | { 203 | double i1 = Astronomical.InterpolateAngles(/* value */ 1, /* previousValue */ -1, 204 | /* nextValue */ 3, /* factor */ 0.6); 205 | Assert.IsTrue(i1.IsWithin(0.000001, 2.2)); 206 | 207 | double i2 = Astronomical.InterpolateAngles(/* value */ 1, /* previousValue */ 359, 208 | /* nextValue */ 3, /* factor */ 0.6); 209 | Assert.IsTrue(i2.IsWithin(0.000001, 2.2)); 210 | } 211 | 212 | [TestMethod] 213 | public void JulianDay() 214 | { 215 | /* 216 | * Comparison values generated from http://aa.usno.navy.mil/data/docs/JulianDate.php 217 | */ 218 | 219 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2010, /* month */ 1, /* day */ 2) 220 | .IsWithin(0.00001, 2455198.500000)); 221 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2011, /* month */ 2, /* day */ 4) 222 | .IsWithin(0.00001, 2455596.500000)); 223 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2012, /* month */ 3, /* day */ 6) 224 | .IsWithin(0.00001, 2455992.500000)); 225 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2013, /* month */ 4, /* day */ 8) 226 | .IsWithin(0.00001, 2456390.500000)); 227 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2014, /* month */ 5, /* day */ 10) 228 | .IsWithin(0.00001, 2456787.500000)); 229 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2015, /* month */ 6, /* day */ 12) 230 | .IsWithin(0.00001, 2457185.500000)); 231 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2016, /* month */ 7, /* day */ 14) 232 | .IsWithin(0.00001, 2457583.500000)); 233 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2017, /* month */ 8, /* day */ 16) 234 | .IsWithin(0.00001, 2457981.500000)); 235 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2018, /* month */ 9, /* day */ 18) 236 | .IsWithin(0.00001, 2458379.500000)); 237 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2019, /* month */ 10, /* day */ 20) 238 | .IsWithin(0.00001, 2458776.500000)); 239 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2020, /* month */ 11, /* day */ 22) 240 | .IsWithin(0.00001, 2459175.500000)); 241 | Assert.IsTrue(CalendricalHelper.JulianDay(/* year */ 2021, /* month */ 12, /* day */ 24) 242 | .IsWithin(0.00001, 2459572.500000)); 243 | 244 | double jdVal = 2457215.67708333; 245 | Assert.IsTrue( 246 | CalendricalHelper.JulianDay(/* year */ 2015, /* month */ 7, /* day */ 12, /* hours */ 4.25) 247 | .IsWithin(0.000001, jdVal)); 248 | 249 | DateTime components = TestUtils.MakeDate(/* year */ 2015, /* month */ 7, /* day */ 12, 250 | /* hour */ 4, /* minute */ 15); 251 | Assert.IsTrue(CalendricalHelper.JulianDay(components).IsWithin(0.000001, jdVal)); 252 | 253 | Assert.IsTrue(CalendricalHelper 254 | .JulianDay(/* year */ 2015, /* month */ 7, /* day */ 12, /* hours */ 8.0) 255 | .IsWithin(0.000001, 2457215.833333)); 256 | Assert.IsTrue(CalendricalHelper 257 | .JulianDay(/* year */ 1992, /* month */ 10, /* day */ 13, /* hours */ 0.0) 258 | .IsWithin(0.000001, 2448908.5)); 259 | } 260 | 261 | [TestMethod] 262 | public void JulianHours() 263 | { 264 | double j1 = CalendricalHelper.JulianDay(/* year */ 2010, /* month */ 1, /* day */ 3); 265 | double j2 = CalendricalHelper.JulianDay(/* year */ 2010, 266 | /* month */ 1, /* day */ 1, /* hours */ 48); 267 | Assert.IsTrue(j1.IsWithin(0.0000001, j2)); 268 | } 269 | 270 | [TestMethod] 271 | public void LeapYear() 272 | { 273 | Assert.IsFalse(CalendarUtil.IsLeapYear(2015)); 274 | Assert.IsTrue(CalendarUtil.IsLeapYear(2016)); 275 | Assert.IsTrue(CalendarUtil.IsLeapYear(1600)); 276 | Assert.IsTrue(CalendarUtil.IsLeapYear(2000)); 277 | Assert.IsTrue(CalendarUtil.IsLeapYear(2400)); 278 | Assert.IsFalse(CalendarUtil.IsLeapYear(1700)); 279 | Assert.IsFalse(CalendarUtil.IsLeapYear(1800)); 280 | Assert.IsFalse(CalendarUtil.IsLeapYear(1900)); 281 | Assert.IsFalse(CalendarUtil.IsLeapYear(2100)); 282 | Assert.IsFalse(CalendarUtil.IsLeapYear(2200)); 283 | Assert.IsFalse(CalendarUtil.IsLeapYear(2300)); 284 | Assert.IsFalse(CalendarUtil.IsLeapYear(2500)); 285 | Assert.IsFalse(CalendarUtil.IsLeapYear(2600)); 286 | } 287 | 288 | private string TimeString(double when) 289 | { 290 | TimeComponents components = TimeComponents.FromDouble(when); 291 | if (components == null) 292 | { 293 | return ""; 294 | } 295 | 296 | int minutes = (int)(components.Minutes + Math.Round(components.Seconds / 60.0)); 297 | return string.Format("{0}:{1}", components.Hours.ToString(), minutes.ToString("D2")); 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/Adhan/internal/Astronomical.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Batoulapps.Adhan.Internal 4 | { 5 | /// 6 | /// Astronomical equations 7 | /// 8 | public class Astronomical 9 | { 10 | /// 11 | /// The geometric mean longitude of the sun in degrees. 12 | /// 13 | /// the julian century 14 | /// the geometric longitude of the sun in degrees 15 | public static double MeanSolarLongitude(double T) 16 | { 17 | /* Equation from Astronomical Algorithms page 163 */ 18 | double term1 = 280.4664567; 19 | double term2 = 36000.76983 * T; 20 | double term3 = 0.0003032 * Math.Pow(T, 2); 21 | double L0 = term1 + term2 + term3; 22 | return DoubleUtil.UnwindAngle(L0); 23 | } 24 | 25 | /// 26 | /// The geometric mean longitude of the moon in degrees 27 | /// 28 | /// the julian century 29 | /// the geometric mean longitude of the moon in degrees 30 | public static double MeanLunarLongitude(double T) 31 | { 32 | /* Equation from Astronomical Algorithms page 144 */ 33 | double term1 = 218.3165; 34 | double term2 = 481267.8813 * T; 35 | double Lp = term1 + term2; 36 | return DoubleUtil.UnwindAngle(Lp); 37 | } 38 | 39 | /// 40 | /// The apparent longitude of the Sun, referred to the true equinox of the date. 41 | /// 42 | /// the julian century 43 | /// the mean longitude 44 | /// the true equinox of the date 45 | public static double ApparentSolarLongitude(double T, double L0) 46 | { 47 | /* Equation from Astronomical Algorithms page 164 */ 48 | double longitude = L0 + SolarEquationOfTheCenter(T, MeanSolarAnomaly(T)); 49 | double Ω = 125.04 - (1934.136 * T); 50 | double λ = longitude - 0.00569 - (0.00478 * Math.Sin(MathHelper.ToRadians(Ω))); 51 | return DoubleUtil.UnwindAngle(λ); 52 | } 53 | 54 | /// 55 | /// The ascending lunar node longitude 56 | /// 57 | /// the julian century 58 | /// the ascending lunar node longitude 59 | public static double AscendingLunarNodeLongitude(double T) 60 | { 61 | /* Equation from Astronomical Algorithms page 144 */ 62 | double term1 = 125.04452; 63 | double term2 = 1934.136261 * T; 64 | double term3 = 0.0020708 * Math.Pow(T, 2); 65 | double term4 = Math.Pow(T, 3) / 450000; 66 | double Ω = term1 - term2 + term3 + term4; 67 | return DoubleUtil.UnwindAngle(Ω); 68 | } 69 | 70 | /// 71 | /// The mean anomaly of the sun 72 | /// 73 | /// the julian century 74 | /// the mean solar anomaly 75 | public static double MeanSolarAnomaly(double T) { 76 | /* Equation from Astronomical Algorithms page 163 */ 77 | double term1 = 357.52911; 78 | double term2 = 35999.05029 * T; 79 | double term3 = 0.0001537 * Math.Pow(T, 2); 80 | double M = term1 + term2 - term3; 81 | return DoubleUtil.UnwindAngle(M); 82 | } 83 | 84 | /// 85 | /// The Sun's equation of the center in degrees. 86 | /// 87 | /// the julian century 88 | /// the mean anomaly 89 | /// the sun's equation of the center in degrees 90 | public static double SolarEquationOfTheCenter(double T, double M) 91 | { 92 | /* Equation from Astronomical Algorithms page 164 */ 93 | double Mrad = MathHelper.ToRadians(M); 94 | double term1 = (1.914602 - (0.004817 * T) - (0.000014 * Math.Pow(T, 2))) * Math.Sin(Mrad); 95 | double term2 = (0.019993 - (0.000101 * T)) * Math.Sin(2 * Mrad); 96 | double term3 = 0.000289 * Math.Sin(3 * Mrad); 97 | return term1 + term2 + term3; 98 | } 99 | 100 | /// 101 | /// The mean obliquity of the ecliptic in degrees 102 | /// formula adopted by the International Astronomical Union. 103 | /// 104 | /// the julian century 105 | /// the mean obliquity of the ecliptic in degrees 106 | public static double MeanObliquityOfTheEcliptic(double T) 107 | { 108 | /* Equation from Astronomical Algorithms page 147 */ 109 | double term1 = 23.439291; 110 | double term2 = 0.013004167 * T; 111 | double term3 = 0.0000001639 * Math.Pow(T, 2); 112 | double term4 = 0.0000005036 * Math.Pow(T, 3); 113 | return term1 - term2 - term3 + term4; 114 | } 115 | 116 | /// 117 | /// The mean obliquity of the ecliptic, corrected for calculating the 118 | /// apparent position of the sun, in degrees. 119 | /// 120 | /// the julian century 121 | /// the mean obliquity of the ecliptic 122 | /// the corrected mean obliquity of the ecliptic in degrees 123 | public static double ApparentObliquityOfTheEcliptic(double T, double ε0) 124 | { 125 | /* Equation from Astronomical Algorithms page 165 */ 126 | double O = 125.04 - (1934.136 * T); 127 | return ε0 + (0.00256 * Math.Cos(MathHelper.ToRadians(O))); 128 | } 129 | 130 | /// 131 | /// Mean sidereal time, the hour angle of the vernal equinox, in degrees. 132 | /// 133 | /// the julian century 134 | /// the mean sidereal time in degrees 135 | public static double MeanSiderealTime(double T) 136 | { 137 | /* Equation from Astronomical Algorithms page 165 */ 138 | double JD = (T * 36525) + 2451545.0; 139 | double term1 = 280.46061837; 140 | double term2 = 360.98564736629 * (JD - 2451545); 141 | double term3 = 0.000387933 * Math.Pow(T, 2); 142 | double term4 = Math.Pow(T, 3) / 38710000; 143 | double θ = term1 + term2 + term3 - term4; 144 | return DoubleUtil.UnwindAngle(θ); 145 | } 146 | 147 | /// 148 | /// Get the nutation in longitude 149 | /// 150 | /// the julian century 151 | /// the solar longitude 152 | /// the lunar longitude 153 | /// the ascending node 154 | /// the nutation in longitude 155 | public static double NutationInLongitude(double T, double L0, double Lp, double Ω) 156 | { 157 | /* Equation from Astronomical Algorithms page 144 */ 158 | double term1 = (-17.2/3600) * Math.Sin(MathHelper.ToRadians(Ω)); 159 | double term2 = (1.32/3600) * Math.Sin(2 * MathHelper.ToRadians(L0)); 160 | double term3 = (0.23/3600) * Math.Sin(2 * MathHelper.ToRadians(Lp)); 161 | double term4 = (0.21/3600) * Math.Sin(2 * MathHelper.ToRadians(Ω)); 162 | return term1 - term2 - term3 + term4; 163 | } 164 | 165 | /// 166 | /// Get the nutation in obliquity 167 | /// 168 | /// the julian century 169 | /// the solar longitude 170 | /// the lunar longitude 171 | /// the ascending node 172 | /// the nutation in obliquity 173 | public static double NutationInObliquity(double T, double L0, double Lp, double Ω) 174 | { 175 | /* Equation from Astronomical Algorithms page 144 */ 176 | double term1 = (9.2/3600) * Math.Cos(MathHelper.ToRadians(Ω)); 177 | double term2 = (0.57/3600) * Math.Cos(2 * MathHelper.ToRadians(L0)); 178 | double term3 = (0.10/3600) * Math.Cos(2 * MathHelper.ToRadians(Lp)); 179 | double term4 = (0.09/3600) * Math.Cos(2 * MathHelper.ToRadians(Ω)); 180 | return term1 + term2 + term3 - term4; 181 | } 182 | 183 | /// 184 | /// Return the altitude of the celestial body 185 | /// 186 | /// the observer latitude 187 | /// the declination 188 | /// the local hour angle 189 | /// the altitude of the celestial body 190 | public static double AltitudeOfCelestialBody(double φ, double δ, double H) 191 | { 192 | /* Equation from Astronomical Algorithms page 93 */ 193 | double term1 = Math.Sin(MathHelper.ToRadians(φ)) * Math.Sin(MathHelper.ToRadians(δ)); 194 | double term2 = Math.Cos(MathHelper.ToRadians(φ)) * 195 | Math.Cos(MathHelper.ToRadians(δ)) * Math.Cos(MathHelper.ToRadians(H)); 196 | return MathHelper.ToDegrees(Math.Asin(term1 + term2)); 197 | } 198 | 199 | /// 200 | /// Return the approximate transite 201 | /// 202 | /// the longitude 203 | /// the sidereal time 204 | /// the right ascension 205 | /// the approximate transite 206 | public static double ApproximateTransit(double L, double Θ0, double α2) 207 | { 208 | /* Equation from page Astronomical Algorithms 102 */ 209 | double Lw = L * -1; 210 | return DoubleUtil.NormalizeWithBound((α2 + Lw - Θ0) / 360, 1); 211 | } 212 | 213 | /// 214 | /// The time at which the sun is at its highest point in the sky (in universal time) 215 | /// 216 | /// approximate transit 217 | /// the longitude 218 | /// the sidereal time 219 | /// the right ascension 220 | /// the previous right ascension 221 | /// the next right ascension 222 | /// the time (in universal time) when the sun is at its highest point in the sky 223 | public static double CorrectedTransit(double m0, double L, double Θ0, double α2, double α1, double α3) 224 | { 225 | /* Equation from page Astronomical Algorithms 102 */ 226 | double Lw = L * -1; 227 | double θ = DoubleUtil.UnwindAngle(Θ0 + (360.985647 * m0)); 228 | double α = DoubleUtil.UnwindAngle(InterpolateAngles( 229 | /* value */ α2, /* previousValue */ α1, /* nextValue */ α3, /* factor */ m0)); 230 | double H = DoubleUtil.ClosestAngle(θ - Lw - α); 231 | double Δm = H / -360; 232 | return (m0 + Δm) * 24; 233 | } 234 | 235 | /// 236 | /// Get the corrected hour angle 237 | /// 238 | /// the approximate transit 239 | /// the angle 240 | /// the coordinates 241 | /// whether it's after transit 242 | /// the sidereal time 243 | /// the right ascension 244 | /// the previous right ascension 245 | /// the next right ascension 246 | /// the declination 247 | /// the previous declination 248 | /// the next declination 249 | /// the corrected hour angle 250 | public static double CorrectedHourAngle(double m0, double h0, Coordinates coordinates, bool afterTransit, 251 | double Θ0, double α2, double α1, double α3, double δ2, double δ1, double δ3) 252 | { 253 | /* Equation from page Astronomical Algorithms 102 */ 254 | double Lw = coordinates.Longitude * -1; 255 | double term1 = Math.Sin(MathHelper.ToRadians(h0)) - 256 | (Math.Sin(MathHelper.ToRadians(coordinates.Latitude)) * Math.Sin(MathHelper.ToRadians(δ2))); 257 | double term2 = Math.Cos(MathHelper.ToRadians(coordinates.Latitude)) * Math.Cos(MathHelper.ToRadians(δ2)); 258 | double H0 = MathHelper.ToDegrees(Math.Acos(term1 / term2)); 259 | double m = afterTransit ? m0 + (H0 / 360) : m0 - (H0 / 360); 260 | double θ = DoubleUtil.UnwindAngle(Θ0 + (360.985647 * m)); 261 | double α = DoubleUtil.UnwindAngle(InterpolateAngles( 262 | /* value */ α2, /* previousValue */ α1, /* nextValue */ α3, /* factor */ m)); 263 | double δ = Interpolate(/* value */ δ2, /* previousValue */ δ1, 264 | /* nextValue */ δ3, /* factor */ m); 265 | double H = (θ - Lw - α); 266 | double h = AltitudeOfCelestialBody(/* observerLatitude */ coordinates.Latitude, 267 | /* declination */ δ, /* localHourAngle */ H); 268 | double term3 = h - h0; 269 | double term4 = 360 * Math.Cos(MathHelper.ToRadians(δ)) * 270 | Math.Cos(MathHelper.ToRadians(coordinates.Latitude)) * Math.Sin(MathHelper.ToRadians(H)); 271 | double Δm = term3 / term4; 272 | return (m + Δm) * 24; 273 | } 274 | 275 | /// 276 | /// Interpolation of a value given equidistant 277 | /// previous and next values and a factor 278 | /// equal to the fraction of the interpolated 279 | /// point's time over the time between values. 280 | /// 281 | /// the value 282 | /// the previous value 283 | /// the next value 284 | /// the factor 285 | /// the interpolated value 286 | public static double Interpolate(double y2, double y1, double y3, double n) 287 | { 288 | /* Equation from Astronomical Algorithms page 24 */ 289 | double a = y2 - y1; 290 | double b = y3 - y2; 291 | double c = b - a; 292 | return y2 + ((n/2) * (a + b + (n * c))); 293 | } 294 | 295 | /// 296 | /// Interpolation of three angles, accounting for angle unwinding 297 | /// 298 | /// value 299 | /// previousValue 300 | /// nextValue 301 | /// factor 302 | /// interpolated angle 303 | public static double InterpolateAngles(double y2, double y1, double y3, double n) 304 | { 305 | /* Equation from Astronomical Algorithms page 24 */ 306 | double a = DoubleUtil.UnwindAngle(y2 - y1); 307 | double b = DoubleUtil.UnwindAngle(y3 - y2); 308 | double c = b - a; 309 | return y2 + ((n/2) * (a + b + (n * c))); 310 | } 311 | } 312 | } -------------------------------------------------------------------------------- /src/Adhan.Test/TimeZoneConverter/TZConvert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | #if NETSTANDARD2_0 || NETSTANDARD1_3 6 | using System.Runtime.InteropServices; 7 | #endif 8 | 9 | namespace Adhan.Test.TimeZoneConverter 10 | { 11 | /// 12 | /// Converts time zone identifiers from various sources. 13 | /// 14 | public static class TZConvert 15 | { 16 | private static readonly IDictionary IanaMap = new Dictionary(StringComparer.OrdinalIgnoreCase); 17 | private static readonly IDictionary WindowsMap = new Dictionary(StringComparer.OrdinalIgnoreCase); 18 | private static readonly IDictionary RailsMap = new Dictionary(StringComparer.OrdinalIgnoreCase); 19 | private static readonly IDictionary> InverseRailsMap = new Dictionary>(StringComparer.OrdinalIgnoreCase); 20 | 21 | #if NETSTANDARD2_0 || NETSTANDARD1_3 22 | private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 23 | #else 24 | private const bool IsWindows = true; 25 | #endif 26 | 27 | #if !NETSTANDARD1_1 28 | private static readonly Dictionary SystemTimeZones; 29 | 30 | #endif 31 | 32 | static TZConvert() 33 | { 34 | DataLoader.Populate(IanaMap, WindowsMap, RailsMap, InverseRailsMap); 35 | 36 | KnownIanaTimeZoneNames = new HashSet(IanaMap.Select(x => x.Key)); 37 | KnownWindowsTimeZoneIds = new HashSet(WindowsMap.Keys.Select(x => x.Split('|')[1]).Distinct()); 38 | KnownRailsTimeZoneNames = new HashSet(RailsMap.Select(x => x.Key)); 39 | 40 | #if !NETSTANDARD1_1 41 | SystemTimeZones = GetSystemTimeZones(); 42 | #endif 43 | } 44 | 45 | /// 46 | /// Gets a collection of all IANA time zone names known to this library. 47 | /// 48 | public static ICollection KnownIanaTimeZoneNames { get; } 49 | 50 | /// 51 | /// Gets a collection of all Windows time zone IDs known to this library. 52 | /// 53 | public static ICollection KnownWindowsTimeZoneIds { get; } 54 | 55 | /// 56 | /// Gets a collection of all Rails time zone names known to this library. 57 | /// 58 | public static ICollection KnownRailsTimeZoneNames { get; } 59 | 60 | /// 61 | /// Converts an IANA time zone name to the equivalent Windows time zone ID. 62 | /// 63 | /// The IANA time zone name to convert. 64 | /// A Windows time zone ID. 65 | /// Thrown if the input string was not recognized or has no equivalent Windows zone. 66 | public static string IanaToWindows(string ianaTimeZoneName) 67 | { 68 | if (TryIanaToWindows(ianaTimeZoneName, out var windowsTimeZoneId)) 69 | return windowsTimeZoneId; 70 | 71 | throw new InvalidTimeZoneException($"\"{ianaTimeZoneName}\" was not recognized as a valid IANA time zone name, or has no equivalent Windows time zone."); 72 | } 73 | 74 | /// 75 | /// Attempts to convert an IANA time zone name to the equivalent Windows time zone ID. 76 | /// 77 | /// The IANA time zone name to convert. 78 | /// A Windows time zone ID. 79 | /// true if successful, false otherwise. 80 | public static bool TryIanaToWindows(string ianaTimeZoneName, out string windowsTimeZoneId) 81 | { 82 | return IanaMap.TryGetValue(ianaTimeZoneName, out windowsTimeZoneId); 83 | } 84 | 85 | /// 86 | /// Converts a Windows time zone ID to an equivalent IANA time zone name. 87 | /// 88 | /// The Windows time zone ID to convert. 89 | /// 90 | /// An optional two-letter ISO Country/Region code, used to get a a specific mapping. 91 | /// Defaults to "001" if not specified, which means to get the "golden zone" - the one that is most prevalent. 92 | /// 93 | /// An IANA time zone name. 94 | /// Thrown if the input string was not recognized or has no equivalent IANA zone. 95 | public static string WindowsToIana(string windowsTimeZoneId, string territoryCode = "001") 96 | { 97 | if (TryWindowsToIana(windowsTimeZoneId, territoryCode, out var ianaTimeZoneName)) 98 | return ianaTimeZoneName; 99 | 100 | throw new InvalidTimeZoneException($"\"{windowsTimeZoneId}\" was not recognized as a valid Windows time zone ID."); 101 | } 102 | 103 | /// 104 | /// Attempts to convert a Windows time zone ID to an equivalent IANA time zone name. 105 | /// Uses the "golden zone" - the one that is the most prevalent. 106 | /// 107 | /// The Windows time zone ID to convert. 108 | /// An IANA time zone name. 109 | /// true if successful, false otherwise. 110 | public static bool TryWindowsToIana(string windowsTimeZoneId, out string ianaTimeZoneName) 111 | { 112 | return TryWindowsToIana(windowsTimeZoneId, "001", out ianaTimeZoneName); 113 | } 114 | 115 | /// 116 | /// Attempts to convert a Windows time zone ID to an equivalent IANA time zone name. 117 | /// 118 | /// The Windows time zone ID to convert. 119 | /// 120 | /// An optional two-letter ISO Country/Region code, used to get a a specific mapping. 121 | /// Defaults to "001" if not specified, which means to get the "golden zone" - the one that is most prevalent. 122 | /// 123 | /// An IANA time zone name. 124 | /// true if successful, false otherwise. 125 | public static bool TryWindowsToIana(string windowsTimeZoneId, string territoryCode, out string ianaTimeZoneName) 126 | { 127 | if (WindowsMap.TryGetValue($"{territoryCode}|{windowsTimeZoneId}", out ianaTimeZoneName)) 128 | return true; 129 | 130 | // use the golden zone when not found with a particular region 131 | return territoryCode != "001" && WindowsMap.TryGetValue($"001|{windowsTimeZoneId}", out ianaTimeZoneName); 132 | } 133 | 134 | #if !NETSTANDARD1_1 135 | 136 | /// 137 | /// Retrieves a object given a valid Windows or IANA time zone identifier, 138 | /// regardless of which platform the application is running on. 139 | /// 140 | /// A valid Windows or IANA time zone identifier. 141 | /// A object. 142 | public static TimeZoneInfo GetTimeZoneInfo(string windowsOrIanaTimeZoneId) 143 | { 144 | if (TryGetTimeZoneInfo(windowsOrIanaTimeZoneId, out var timeZoneInfo)) 145 | return timeZoneInfo; 146 | 147 | #if !NETSTANDARD1_3 148 | throw new TimeZoneNotFoundException(); 149 | #else 150 | // this will also throw, but we can't throw directly because TimeZoneNotFoundException is not available in .NET Standard 1.3 151 | return TimeZoneInfo.FindSystemTimeZoneById(windowsOrIanaTimeZoneId); 152 | #endif 153 | } 154 | 155 | /// 156 | /// Attempts to retrieve a object given a valid Windows or IANA time zone identifier, 157 | /// regardless of which platform the application is running on. 158 | /// 159 | /// A valid Windows or IANA time zone identifier. 160 | /// A object. 161 | /// true if successful, false otherwise. 162 | public static bool TryGetTimeZoneInfo(string windowsOrIanaTimeZoneId, out TimeZoneInfo timeZoneInfo) 163 | { 164 | if (string.Equals(windowsOrIanaTimeZoneId, "UTC", StringComparison.OrdinalIgnoreCase)) 165 | { 166 | timeZoneInfo = TimeZoneInfo.Utc; 167 | return true; 168 | } 169 | 170 | // Try a direct approach 171 | if (SystemTimeZones.TryGetValue(windowsOrIanaTimeZoneId, out timeZoneInfo)) 172 | return true; 173 | 174 | // Convert to the opposite platform and try again 175 | return (IsWindows && TryIanaToWindows(windowsOrIanaTimeZoneId, out var tzid) || 176 | TryWindowsToIana(windowsOrIanaTimeZoneId, out tzid)) && 177 | SystemTimeZones.TryGetValue(tzid, out timeZoneInfo); 178 | } 179 | #endif 180 | 181 | /// 182 | /// Converts an IANA time zone name to one or more equivalent Rails time zone names. 183 | /// 184 | /// The IANA time zone name to convert. 185 | /// One or more equivalent Rails time zone names. 186 | /// Thrown if the input string was not recognized or has no equivalent Rails zone. 187 | public static IList IanaToRails(string ianaTimeZoneName) 188 | { 189 | if (TryIanaToRails(ianaTimeZoneName, out var railsTimeZoneNames)) 190 | return railsTimeZoneNames; 191 | 192 | throw new InvalidTimeZoneException($"\"{ianaTimeZoneName}\" was not recognized as a valid IANA time zone name, or has no equivalent Rails time zone."); 193 | } 194 | 195 | /// 196 | /// Attempts to convert an IANA time zone name to one or more equivalent Rails time zone names. 197 | /// 198 | /// The IANA time zone name to convert. 199 | /// One or more equivalent Rails time zone names. 200 | /// true if successful, false otherwise. 201 | public static bool TryIanaToRails(string ianaTimeZoneName, out IList railsTimeZoneNames) 202 | { 203 | // try directly first 204 | if (InverseRailsMap.TryGetValue(ianaTimeZoneName, out railsTimeZoneNames)) 205 | return true; 206 | 207 | // try again with the golden zone 208 | return TryIanaToWindows(ianaTimeZoneName, out var windowsTimeZoneId) && 209 | TryWindowsToIana(windowsTimeZoneId, out var ianaGoldenZone) && 210 | InverseRailsMap.TryGetValue(ianaGoldenZone, out railsTimeZoneNames); 211 | } 212 | 213 | /// 214 | /// Converts a Rails time zone name to an equivalent IANA time zone name. 215 | /// 216 | /// The Rails time zone name to convert. 217 | /// An IANA time zone name. 218 | /// Thrown if the input string was not recognized or has no equivalent IANA zone. 219 | public static string RailsToIana(string railsTimeZoneName) 220 | { 221 | if (TryRailsToIana(railsTimeZoneName, out var ianaTimeZoneName)) 222 | return ianaTimeZoneName; 223 | 224 | throw new InvalidTimeZoneException($"\"{railsTimeZoneName}\" was not recognized as a valid Rails time zone name."); 225 | } 226 | 227 | /// 228 | /// Attempts to convert a Rails time zone name to an equivalent IANA time zone name. 229 | /// 230 | /// The Rails time zone name to convert. 231 | /// An IANA time zone name. 232 | /// true if successful, false otherwise. 233 | public static bool TryRailsToIana(string railsTimeZoneName, out string ianaTimeZoneName) 234 | { 235 | return RailsMap.TryGetValue(railsTimeZoneName, out ianaTimeZoneName); 236 | } 237 | 238 | /// 239 | /// Converts a Rails time zone name to an equivalent Windows time zone ID. 240 | /// 241 | /// The Rails time zone name to convert. 242 | /// A Windows time zone ID. 243 | /// Thrown if the input string was not recognized or has no equivalent Windows zone. 244 | public static string RailsToWindows(string railsTimeZoneName) 245 | { 246 | if (TryRailsToWindows(railsTimeZoneName, out var windowsTimeZoneId)) 247 | return windowsTimeZoneId; 248 | 249 | throw new InvalidTimeZoneException($"\"{railsTimeZoneName}\" was not recognized as a valid Rails time zone name."); 250 | } 251 | 252 | /// 253 | /// Attempts to convert a Rails time zone name to an equivalent Windows time zone ID. 254 | /// 255 | /// The Rails time zone name to convert. 256 | /// A Windows time zone ID. 257 | /// true if successful, false otherwise. 258 | public static bool TryRailsToWindows(string railsTimeZoneName, out string windowsTimeZoneId) 259 | { 260 | if (TryRailsToIana(railsTimeZoneName, out var ianaTimeZoneName) && 261 | TryIanaToWindows(ianaTimeZoneName, out windowsTimeZoneId)) 262 | return true; 263 | 264 | windowsTimeZoneId = null; 265 | return false; 266 | } 267 | 268 | /// 269 | /// Converts a Windows time zone ID to one ore more equivalent Rails time zone names. 270 | /// 271 | /// The Windows time zone ID to convert. 272 | /// 273 | /// An optional two-letter ISO Country/Region code, used to get a a specific mapping. 274 | /// Defaults to "001" if not specified, which means to get the "golden zone" - the one that is most prevalent. 275 | /// 276 | /// One or more equivalent Rails time zone names. 277 | /// Thrown if the input string was not recognized or has no equivalent Rails zone. 278 | public static IList WindowsToRails(string windowsTimeZoneId, string territoryCode = "001") 279 | { 280 | if (TryWindowsToRails(windowsTimeZoneId, territoryCode, out var railsTimeZoneNames)) 281 | return railsTimeZoneNames; 282 | 283 | throw new InvalidTimeZoneException($"\"{windowsTimeZoneId}\" was not recognized as a valid Windows time zone ID, or has no equivalent Rails time zone."); 284 | } 285 | 286 | /// 287 | /// Attempts to convert a Windows time zone ID to one ore more equivalent Rails time zone names. 288 | /// Uses the "golden zone" - the one that is the most prevalent. 289 | /// 290 | /// The Windows time zone ID to convert. 291 | /// One or more equivalent Rails time zone names. 292 | /// true if successful, false otherwise. 293 | public static bool TryWindowsToRails(string windowsTimeZoneId, out IList railsTimeZoneNames) 294 | { 295 | return TryWindowsToRails(windowsTimeZoneId, "001", out railsTimeZoneNames); 296 | } 297 | 298 | /// 299 | /// Attempts to convert a Windows time zone ID to one ore more equivalent Rails time zone names. 300 | /// 301 | /// The Windows time zone ID to convert. 302 | /// 303 | /// An optional two-letter ISO Country/Region code, used to get a a specific mapping. 304 | /// Defaults to "001" if not specified, which means to get the "golden zone" - the one that is most prevalent. 305 | /// 306 | /// One or more equivalent Rails time zone names. 307 | /// true if successful, false otherwise. 308 | public static bool TryWindowsToRails(string windowsTimeZoneId, string territoryCode, out IList railsTimeZoneNames) 309 | { 310 | if (TryWindowsToIana(windowsTimeZoneId, territoryCode, out var ianaTimeZoneName) && 311 | TryIanaToRails(ianaTimeZoneName, out railsTimeZoneNames)) 312 | return true; 313 | 314 | railsTimeZoneNames = new string[0]; 315 | return false; 316 | } 317 | 318 | #if !NETSTANDARD1_1 319 | private static Dictionary GetSystemTimeZones() 320 | { 321 | #if NETSTANDARD2_0 || NETSTANDARD1_3 322 | if (IsWindows) 323 | return TimeZoneInfo.GetSystemTimeZones().ToDictionary(x => x.Id, x => x, StringComparer.OrdinalIgnoreCase); 324 | 325 | var zones = GetSystemTimeZonesLinux().ToDictionary(x => x.Id, x => x, StringComparer.OrdinalIgnoreCase); 326 | 327 | // Include special case to resolve deleted link 328 | if (!zones.ContainsKey("Canada/East-Saskatchewan")) 329 | { 330 | try 331 | { 332 | var tzi = TimeZoneInfo.FindSystemTimeZoneById("Canada/Saskatchewan"); 333 | zones.Add("Canada/East-Saskatchewan", tzi); 334 | } 335 | catch 336 | { 337 | } 338 | } 339 | 340 | return zones; 341 | #else 342 | return TimeZoneInfo.GetSystemTimeZones().ToDictionary(x => x.Id, x => x, StringComparer.OrdinalIgnoreCase); 343 | #endif 344 | } 345 | 346 | #if NETSTANDARD2_0 || NETSTANDARD1_3 347 | private static IEnumerable GetSystemTimeZonesLinux() 348 | { 349 | // Don't trust TimeZoneInfo.GetSystemTimeZones on Non-Windows 350 | // Because it doesn't return any links, or any Etc zones 351 | 352 | foreach (var name in KnownIanaTimeZoneNames) 353 | { 354 | TimeZoneInfo tzi = null; 355 | 356 | try 357 | { 358 | tzi = TimeZoneInfo.FindSystemTimeZoneById(name); 359 | } 360 | catch 361 | { 362 | } 363 | 364 | if (tzi != null) 365 | yield return tzi; 366 | } 367 | } 368 | #endif 369 | #endif 370 | } 371 | } --------------------------------------------------------------------------------