defmodule Azan.FajrTime do
@moduledoc """
Documentation for `FajrTime`.
"""
use TypedStruct
alias Azan.{
CalculationParameter,
DateUtils,
SafeFajr,
SolarTime,
TimeComponent
}
typedstruct do
field :calculation_parameter, CalculationParameter.t()
field :sunrise_time, DateTime.t()
field :night, number()
field :latitude, number()
field :date, Date.t()
field :solar_time, SolarTime.t()
end
def find(
solar_time,
sunrise_time,
night,
latitude,
date,
%CalculationParameter{} = calculation_parameter
) do
case %__MODULE__{
calculation_parameter: calculation_parameter,
sunrise_time: sunrise_time,
solar_time: solar_time,
night: night,
latitude: latitude,
date: date
}
|> __MODULE__.find!() do
%DateTime{} = fajr_time ->
{:ok, fajr_time}
{:error, error} ->
{:error, error}
end
end
def find!(%__MODULE__{
calculation_parameter:
%CalculationParameter{method: method, fajr_angle: fajr_angle} = calculation_parameter,
solar_time: solar_time,
sunrise_time: sunrise_time,
night: night,
latitude: latitude,
date: date
}) do
naive_fajr_time =
solar_time
|> naive_fajr_time(fajr_angle, date)
|> consider_latitude_and_method(night, method, latitude)
safe_fajr_time =
%SafeFajr{
calculation_parameter: calculation_parameter,
sunrise_time: sunrise_time,
night: night,
latitude: latitude,
date: date
}
|> SafeFajr.find_time()
naive_fajr_time |> naive_or_safe_fajr(safe_fajr_time)
end
def naive_fajr_time(%SolarTime{} = solar_time, fajr_angle, date) do
case solar_time |> SolarTime.hour_angle(-1 * fajr_angle, false) do
{:error, _} ->
{:error, :invalid_datetime}
corrected_hour_angle ->
corrected_hour_angle
|> TimeComponent.new()
|> TimeComponent.create_utc_datetime(date)
end
end
@spec consider_latitude_and_method(DateTime.t(), number, atom(), number()) :: DateTime.t()
def consider_latitude_and_method(fajr_time, night, :moonsighting_committee, latitude)
when latitude >= 55 do
night_fraction = night / 7
fajr_time |> DateUtils.shift_by_seconds(-1 * night_fraction)
end
def consider_latitude_and_method(fajr_time, _night, _method, _latitude), do: fajr_time
def naive_or_safe_fajr({:error, :invalid_datetime}, safe_fajr_time), do: safe_fajr_time
def naive_or_safe_fajr(naive_fajr_time, safe_fajr_time) do
case DateTime.compare(safe_fajr_time, naive_fajr_time) do
:gt -> safe_fajr_time
_ -> naive_fajr_time
end
end
def adjust(datetime, %CalculationParameter{
adjustments: %{fajr: adjustment},
method_adjustments: %{fajr: method_adjustment},
rounding: rounding
}) do
adjustment = adjustment |> DateUtils.sum_adjustment(method_adjustment)
datetime |> DateUtils.rounded_time(adjustment, rounding)
end
end