defmodule Azan.PrayerTime do @moduledoc """ Documentation for `PrayerTime`. """ alias Azan.{ AsrTime, CalculationParameter, Coordinate, DateUtils, DhuhrTime, FajrTime, IshaTime, MaghribTime, PrayerTime, SolarTime, SunriseTime, SunsetTime } use TypedStruct require Logger typedstruct do field :fajr, DateTime.t() field :sunrise, DateTime.t() field :dhuhr, DateTime.t() field :asr, DateTime.t() field :sunset, DateTime.t() field :maghrib, DateTime.t() field :isha, DateTime.t() end @doc """ Returns a `PrayerTime` struct for the given `date`, `coordinate` and `calculation_parameter`. ## Examples iex> date = ~D[2022-10-01] ~D[2022-10-01] iex> coordinate = %Azan.Coordinate{latitude: 35.671494, longitude: 139.90181} %Azan.Coordinate{latitude: 35.671494, longitude: 139.90181} iex> params = CalculationMethod.moonsighting_committee() %Azan.CalculationParameter{ adjustments: %{asr: 0, dhuhr: 0, fajr: 0, isha: 0, maghrib: 0, sunrise: 0}, fajr_angle: 18.0, high_latitude_rule: :middle_of_the_night, isha_angle: 18.0, isha_interval: 0, madhab: :shafi, maghrib_angle: 0.0, method: :moonsighting_committee, method_adjustments: %{ asr: 0, dhuhr: 5, fajr: 0, isha: 0, maghrib: 3, sunrise: 0 }, polar_circle_resolution: :unresolved, rounding: :nearest, shafaq: :general } iex> coordinate |> PrayerTime.find(date, params) %Azan.PrayerTime{ asr: ~U[2022-10-01 05:51:00.000000Z], dhuhr: ~U[2022-10-01 02:35:00.000000Z], fajr: ~U[2022-09-30 19:10:00Z], isha: ~U[2022-10-01 09:43:00.000000Z], maghrib: ~U[2022-10-01 08:28:00.000000Z], sunrise: ~U[2022-09-30 20:35:00.000000Z], sunset: ~U[2022-10-01 08:25:00.000000Z] } """ def find( %Coordinate{latitude: latitude} = coordinate, date, %CalculationParameter{} = calculation_parameter ) do tomorrow = date |> Timex.shift(days: 1) with {:ok, {%SolarTime{transit: transit, sunrise: sunrise, sunset: sunset} = solar_time, tomorrow_solar_time}} <- SolarTime.find_pair_solar_time( date, coordinate, calculation_parameter ), {:ok, sunrise_time} <- SunriseTime.find(sunrise, date, calculation_parameter, coordinate), {:ok, dhuhr_time} <- DhuhrTime.find(transit, date, calculation_parameter, coordinate), {:ok, asr_time} <- AsrTime.find(solar_time, date, calculation_parameter), {:ok, tomorrow_sunrise} <- SunriseTime.find_naive_time(tomorrow_solar_time.sunrise, tomorrow), {:ok, sunset_time} <- SunsetTime.find(sunset, date, calculation_parameter, coordinate), {:ok, night} <- calculate_night(tomorrow_sunrise, sunset_time), {:ok, isha_time} <- IshaTime.find(solar_time, sunset_time, night, latitude, date, calculation_parameter), {:ok, fajr_time} <- FajrTime.find(solar_time, sunrise_time, night, latitude, date, calculation_parameter), {:ok, maghrib_time} <- MaghribTime.find(solar_time, sunset_time, isha_time, date, calculation_parameter) do %__MODULE__{ fajr: fajr_time |> FajrTime.adjust(calculation_parameter), sunrise: sunrise_time |> SunriseTime.adjust(calculation_parameter), dhuhr: dhuhr_time |> DhuhrTime.adjust(calculation_parameter), asr: asr_time |> AsrTime.adjust(calculation_parameter), sunset: sunset_time |> SunsetTime.adjust(calculation_parameter), maghrib: maghrib_time |> MaghribTime.adjust(calculation_parameter), isha: isha_time |> IshaTime.adjust(calculation_parameter) } else {:error, reason} -> {:error, reason} end end @doc """ Returns prayer time for prayer name ## Examples iex> prayer_time = %Azan.PrayerTime{ ...> asr: ~U[2022-10-01 05:51:00Z], ...> dhuhr: ~U[2022-10-01 02:35:00Z], ...> fajr: ~U[2022-09-30 19:10:00Z], ...> isha: ~U[2022-10-01 09:43:00Z], ...> maghrib: ~U[2022-10-01 08:28:00Z], ...> sunrise: ~U[2022-09-30 20:35:00Z], ...> sunset: ~U[2022-10-01 08:25:00Z] ...> } iex> prayer_time |> PrayerTime.time_for_prayer(:fajr) ~U[2022-09-30 19:10:00Z] """ @spec time_for_prayer(PrayerTime.t(), atom()) :: DateTime.t() def time_for_prayer(%__MODULE__{} = prayer_time, prayer_name) do prayer_time |> Map.get(prayer_name) end def current_prayer( %__MODULE__{ fajr: fajr, sunrise: sunrise, dhuhr: dhuhr, asr: asr, maghrib: maghrib, isha: isha }, time ) do fajr_epoch = fajr |> Timex.to_unix() sunrise_epoch = sunrise |> Timex.to_unix() dhuhr_epoch = dhuhr |> Timex.to_unix() asr_epoch = asr |> Timex.to_unix() maghrib_epoch = maghrib |> Timex.to_unix() isha_epoch = isha |> Timex.to_unix() case time |> Timex.to_unix() do x when x >= isha_epoch -> :isha x when x >= maghrib_epoch -> :maghrib x when x >= asr_epoch -> :asr x when x >= dhuhr_epoch -> :dhuhr x when x >= sunrise_epoch -> :sunrise x when x >= fajr_epoch -> :fajr _ -> :none end end def next_prayer( %__MODULE__{ fajr: fajr, sunrise: sunrise, dhuhr: dhuhr, asr: asr, maghrib: maghrib, isha: isha }, time ) do fajr_epoch = fajr |> Timex.to_unix() sunrise_epoch = sunrise |> Timex.to_unix() dhuhr_epoch = dhuhr |> Timex.to_unix() asr_epoch = asr |> Timex.to_unix() maghrib_epoch = maghrib |> Timex.to_unix() isha_epoch = isha |> Timex.to_unix() case time |> Timex.to_unix() do x when x >= isha_epoch -> :none x when x >= maghrib_epoch -> :isha x when x >= asr_epoch -> :maghrib x when x >= dhuhr_epoch -> :asr x when x >= sunrise_epoch -> :dhuhr x when x >= fajr_epoch -> :sunrise _ -> :fajr end end def rounded_time(prayer_time, adjustment, rounding) do prayer_time |> Timex.shift(minutes: adjustment) |> DateUtils.rounded_minute(rounding) end def calculate_night({:error, _}, _sunset_time), do: {:error, :invalid} def calculate_night(_tomorrow_sunrise, {:error, _}), do: {:error, :invalid} def calculate_night(tomorrow_sunrise, sunset_time) do with {:ok, tomorrow_sunrice_epoch} <- tomorrow_sunrise |> Timex.to_unix() |> DateUtils.wrap_timex_error(), {:ok, sunset_epoch} <- sunset_time |> Timex.to_unix() |> DateUtils.wrap_timex_error() do {:ok, tomorrow_sunrice_epoch - sunset_epoch} else error -> error end end end