defmodule Azan.IshaTime do @moduledoc """ Documentation for `IshaTime`. """ use TypedStruct alias Azan.{ CalculationParameter, DateUtils, SafeIsha, SolarTime, TimeComponent } typedstruct do field :solar_time, SolarTime.t() field :date, Date.t() field :calculation_parameter, CalculationParameter.t() field :sunset_time, DateTime.t() field :latitude, number() field :night, number() end def find( solar_time, sunset_time, night, latitude, date, %CalculationParameter{} = calculation_parameter ) do {:ok, %__MODULE__{ calculation_parameter: calculation_parameter, sunset_time: sunset_time, solar_time: solar_time, night: night, latitude: latitude, date: date } |> __MODULE__.find!()} end def find!(%__MODULE__{ sunset_time: sunset_time, calculation_parameter: %CalculationParameter{isha_interval: isha_interval} }) when isha_interval > 0 do sunset_time |> Timex.shift(minutes: isha_interval) end def find!( %__MODULE__{ calculation_parameter: calculation_parameter, night: night, latitude: latitude, date: date, sunset_time: sunset_time } = isha_time_struct ) do naive_isha_time = isha_time_struct |> find_naive_time() safe_isha_time = %SafeIsha{ calculation_parameter: calculation_parameter, night: night, latitude: latitude, date: date, sunset_time: sunset_time } |> SafeIsha.find() naive_isha_time |> naive_or_safe_isha(safe_isha_time) end def find_naive_time(%__MODULE__{ calculation_parameter: %CalculationParameter{method: :moonsighting_committee}, sunset_time: sunset_time, night: night, latitude: latitude }) when latitude >= 55 do night_fraction = night / 7 sunset_time |> DateUtils.shift_by_seconds(night_fraction) end def find_naive_time(%__MODULE__{ calculation_parameter: %CalculationParameter{isha_angle: isha_angle}, date: date, solar_time: solar_time }) do case solar_time |> SolarTime.hour_angle(-1 * isha_angle, true) do {:error, _} -> {:error, :invalid_datetime} corrected_hour_angle -> corrected_hour_angle |> TimeComponent.new() |> TimeComponent.create_utc_datetime(date) end end def naive_or_safe_isha({:error, _}, safe_isha), do: safe_isha def naive_or_safe_isha(naive_isha_time, safe_isha_time) do case DateTime.compare(safe_isha_time, naive_isha_time) do :lt -> safe_isha_time _ -> naive_isha_time end end def adjust(datetime, %CalculationParameter{ adjustments: %{isha: adjustment}, method_adjustments: %{isha: method_adjustment}, rounding: rounding }) do adjustment = adjustment |> DateUtils.sum_adjustment(method_adjustment) datetime |> DateUtils.rounded_time(adjustment, rounding) end end