├── 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 | }
--------------------------------------------------------------------------------