diff --git a/lib/cldr/calendar.ex b/lib/cldr/calendar.ex index c6b3227..ecc361e 100644 --- a/lib/cldr/calendar.ex +++ b/lib/cldr/calendar.ex @@ -673,7 +673,7 @@ defmodule Cldr.Calendar do """ @spec new(module(), calendar_type(), Keyword.t()) :: - {:ok, calendar()} | {:module_already_exists, module()} + {:ok, calendar()} | {:module_already_exists, module()} | {:error, String.t()} def new(calendar_module, calendar_type, config) when is_atom(calendar_module) and calendar_type in [:week, :month] do @@ -860,7 +860,8 @@ defmodule Cldr.Calendar do %Date{calendar: Cldr.Calendar.NRF, day: 1, month: 1, year: 2019} """ - @spec first_day_of_year(year :: year(), calendar :: calendar()) :: Date.t() + @spec first_day_of_year(year :: year(), calendar :: calendar()) :: + Date.t() | {:error, :invalid_date} def first_day_of_year(year, calendar) do with {:ok, date} <- Date.new(year, 1, 1, calendar) do @@ -888,7 +889,7 @@ defmodule Cldr.Calendar do ~D[2019-01-01] """ - @spec first_day_of_year(date :: date()) :: Date.t() + @spec first_day_of_year(date :: date()) :: Date.t() | {:error, :invalid_date} def first_day_of_year(%Date{year: year, calendar: calendar}) do first_day_of_year(year, calendar) @@ -2053,7 +2054,7 @@ defmodule Cldr.Calendar do """ @spec weeks_to_days(integer) :: integer def weeks_to_days(n) do - n * @days_in_a_week + trunc(n * @days_in_a_week) end @doc """ @@ -2746,7 +2747,7 @@ defmodule Cldr.Calendar do {:ok, backend} end - @spec plus(integer, integer()) :: integer() + @spec plus(integer, integer()) :: integer() | {:error, :invalid_date} @doc false def plus(value, increment) when is_integer(value) and is_integer(increment) do @@ -2870,7 +2871,7 @@ defmodule Cldr.Calendar do """ @spec plus(Calendar.date() | Date.Range.t(), atom(), integer(), Keyword.t()) :: - Calendar.date() + Calendar.date() | {:error, :invalid_date} def plus(date, period, increment, options \\ []) @@ -3429,7 +3430,8 @@ defmodule Cldr.Calendar do ~D[0000-01-01] """ - @spec date_from_iso_days(Calendar.iso_days() | iso_day_number, calendar()) :: Date.t() + @spec date_from_iso_days(Calendar.iso_days() | iso_day_number, calendar()) :: + Date.t() | {:error, :incompatible_calendars | :invalid_date} def date_from_iso_days({days, _}, calendar) do date_from_iso_days(days, calendar) diff --git a/lib/cldr/calendar/backend/calendar.ex b/lib/cldr/calendar/backend/calendar.ex index fe7b6a0..fee30bf 100644 --- a/lib/cldr/calendar/backend/calendar.ex +++ b/lib/cldr/calendar/backend/calendar.ex @@ -167,8 +167,14 @@ defmodule Cldr.Calendar.Backend do """ @doc since: "1.25.0" - @spec localize(datetime :: Cldr.Calendar.any_date_time(), part :: Cldr.Calendar.part(), options :: Keyword.t()) :: - String.t() | [Cldr.Calendar.day_of_week_to_binary()] | {:error, {module(), String.t()}} + @spec localize( + datetime :: Cldr.Calendar.any_date_time(), + part :: Cldr.Calendar.part(), + options :: Keyword.t() + ) :: + String.t() + | [Cldr.Calendar.day_of_week_to_binary()] + | {:error, {module(), String.t()}} def localize(datetime, part, options \\ []) do options = Keyword.put(options, :backend, unquote(backend)) diff --git a/lib/cldr/calendar/backend/month_compiler.ex b/lib/cldr/calendar/backend/month_compiler.ex index d74e357..4518972 100644 --- a/lib/cldr/calendar/backend/month_compiler.ex +++ b/lib/cldr/calendar/backend/month_compiler.ex @@ -403,7 +403,7 @@ defmodule Cldr.Calendar.Compiler.Month do """ @spec days_in_month(year :: Cldr.Calendar.year(), month :: Cldr.Calendar.month()) :: - days :: Calendar.day() | {:error, {module(), String.t()}} + days :: Calendar.day() | {:error, {module(), String.t()}} | {:ambiguous, Range.t()} @impl true def days_in_month(year, month) do @@ -418,7 +418,7 @@ defmodule Cldr.Calendar.Compiler.Month do """ @spec days_in_month(month :: Cldr.Calendar.month()) :: - days :: Calendar.day() | {:error, {module(), String.t()}} + days :: Calendar.day() | {:error, {module(), String.t()}} | {:ambiguous, Range.t()} @impl true def days_in_month(month) do diff --git a/lib/cldr/calendar/backend/week_compiler.ex b/lib/cldr/calendar/backend/week_compiler.ex index 0f1e3b2..f1fe35f 100644 --- a/lib/cldr/calendar/backend/week_compiler.ex +++ b/lib/cldr/calendar/backend/week_compiler.ex @@ -324,7 +324,7 @@ defmodule Cldr.Calendar.Compiler.Week do a week in week-based calendars.. """ - @spec periods_in_year(year :: Cldr.Calendar.year()) :: Calendar.week() + @spec periods_in_year(year :: Cldr.Calendar.year()) :: Calendar.week() | :error @impl true def periods_in_year(year) do {weeks_in_year, _} = weeks_in_year(year) @@ -377,7 +377,11 @@ defmodule Cldr.Calendar.Compiler.Week do """ @spec days_in_month(year :: Cldr.Calendar.year(), month :: Cldr.Calendar.month()) :: - days :: Calendar.day() | {:error, {module(), String.t()}} + days :: + Calendar.day() + | {:error, {module(), String.t()}} + | {:ambiguous, Range.t()} + | {:ambiguous, [integer(), ...]} @impl true def days_in_month(year, month) do @@ -392,7 +396,11 @@ defmodule Cldr.Calendar.Compiler.Week do """ @spec days_in_month(month :: Cldr.Calendar.month()) :: - days :: Calendar.day() | {:error, {module(), String.t()}} + days :: + Calendar.day() + | {:error, {module(), String.t()}} + | {:ambiguous, Range.t()} + | {:ambiguous, [integer(), ...]} @impl true def days_in_month(month) do diff --git a/lib/cldr/calendar/base/week.ex b/lib/cldr/calendar/base/week.ex index df12648..4c92640 100644 --- a/lib/cldr/calendar/base/week.ex +++ b/lib/cldr/calendar/base/week.ex @@ -140,7 +140,7 @@ defmodule Cldr.Calendar.Base.Week do def day_of_year(year, week, day, config) when is_date(year, week, day) do start_of_year = first_gregorian_day_of_year(year, config) this_day = first_gregorian_day_of_year(year, config) + week_to_days(week) + day - this_day - start_of_year + 1 + trunc(this_day - start_of_year + 1) end def day_of_year(year, week, day, _config) do @@ -210,13 +210,13 @@ defmodule Cldr.Calendar.Base.Week do def days_in_month(year, @months_in_year, config) do %Config{weeks_in_month: [_, _, weeks_in_last_month]} = config weeks = if long_year?(year, config), do: weeks_in_last_month + 1, else: weeks_in_last_month - weeks * days_in_week() + trunc(weeks * days_in_week()) end def days_in_month(_year, month, config) do %Config{weeks_in_month: weeks_in_month} = config month_in_quarter = Math.amod(rem(month, @months_in_quarter), @months_in_quarter) - Enum.at(weeks_in_month, month_in_quarter - 1) * days_in_week() + (Enum.at(weeks_in_month, month_in_quarter - 1) * days_in_week()) |> trunc() end # If the month is the last month of the year then it will be different @@ -225,8 +225,8 @@ defmodule Cldr.Calendar.Base.Week do def days_in_month(month, %Config{weeks_in_month: [_, _, weeks_in_last_month]} = config) do if month == @months_in_year do - long_year_days = (weeks_in_last_month + 1) * days_in_week() - short_year_days = weeks_in_last_month * days_in_week() + long_year_days = trunc((weeks_in_last_month + 1) * days_in_week()) + short_year_days = trunc(weeks_in_last_month * days_in_week()) {:ambiguous, [short_year_days, long_year_days]} else days_in_month(@any_year, month, config) diff --git a/lib/cldr/calendar/calendars/julian.ex b/lib/cldr/calendar/calendars/julian.ex index 0f7046d..97b0a4d 100644 --- a/lib/cldr/calendar/calendars/julian.ex +++ b/lib/cldr/calendar/calendars/julian.ex @@ -227,8 +227,8 @@ defmodule Cldr.Calendar.Julian do @spec day_of_year(year, month, day) :: 1..366 @impl Calendar def day_of_year(year, month, day) do - first_day = date_to_iso_days(year, 1, 1) - this_day = date_to_iso_days(year, month, day) + first_day = date_to_iso_days(year, 1, 1) |> floor() + this_day = date_to_iso_days(year, month, day) |> floor() this_day - first_day + 1 end @@ -383,7 +383,7 @@ defmodule Cldr.Calendar.Julian do knowning the year an error tuple is returned. """ - @spec days_in_month(month) :: Calendar.day() + @spec days_in_month(month) :: Calendar.day() | {:error, :unresolved} @impl true def days_in_month(month) do case month do diff --git a/lib/cldr/calendar/duration.ex b/lib/cldr/calendar/duration.ex index 5c35a87..ed87432 100644 --- a/lib/cldr/calendar/duration.ex +++ b/lib/cldr/calendar/duration.ex @@ -250,7 +250,10 @@ defmodule Cldr.Calendar.Duration do """ @spec new(from :: date_or_time_or_datetime(), to :: date_or_time_or_datetime()) :: - {:ok, t()} | {:error, {module(), String.t()}} + {:ok, t()} + | {:error, {module(), String.t()}} + | {:ambiguous, DateTime.t(), DateTime.t()} + | {:gap, DateTime.t(), DateTime.t()} def new(unquote(Cldr.Calendar.datetime()) = from, unquote(Cldr.Calendar.datetime()) = to) do with :ok <- confirm_same_time_zone(from, to), @@ -326,7 +329,11 @@ defmodule Cldr.Calendar.Duration do }} """ - @spec new(interval :: interval()) :: {:ok, t()} | {:error, {module(), String.t()}} + @spec new(interval :: interval()) :: + {:ok, t()} + | {:error, {module(), String.t()}} + | {:ambiguous, DateTime.t(), DateTime.t()} + | {:gap, DateTime.t(), DateTime.t()} if Code.ensure_loaded?(CalendarInterval) do def new(%CalendarInterval{first: first, last: last, precision: precision} = _interval) diff --git a/lib/cldr/calendar/interval.ex b/lib/cldr/calendar/interval.ex index 19f8631..d2677f3 100644 --- a/lib/cldr/calendar/interval.ex +++ b/lib/cldr/calendar/interval.ex @@ -256,13 +256,14 @@ defmodule Cldr.Calendar.Interval do Date.range(~D[2019-W02-1 Cldr.Calendar.ISOWeek], ~D[2019-W02-1 Cldr.Calendar.ISOWeek]) """ - @spec day(Calendar.year(), Calendar.day(), Cldr.Calendar.calendar()) :: Date.Range.t() + @spec day(Calendar.year(), Calendar.day(), Cldr.Calendar.calendar()) :: + Date.Range.t() | {:error, :invalid_date} @spec day(Date.t()) :: Date.Range.t() def day(%{calendar: Calendar.ISO} = date) do %{date | calendar: Cldr.Calendar.Gregorian} - |> day - |> coerce_iso_calendar + |> day() + |> coerce_iso_calendar() end def day(date) do diff --git a/lib/cldr/calendar/kday.ex b/lib/cldr/calendar/kday.ex index 2b461e5..acedb75 100644 --- a/lib/cldr/calendar/kday.ex +++ b/lib/cldr/calendar/kday.ex @@ -44,7 +44,7 @@ defmodule Cldr.Calendar.Kday do """ @spec kday_on_or_before(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def kday_on_or_before(%{year: _, month: _, day: _, calendar: calendar} = date, k) when k in 1..7 do @@ -90,7 +90,7 @@ defmodule Cldr.Calendar.Kday do """ @spec kday_on_or_after(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def kday_on_or_after(%{year: _, month: _, day: _, calendar: calendar} = date, k) when k in 1..7 do @@ -133,7 +133,7 @@ defmodule Cldr.Calendar.Kday do """ @spec kday_nearest(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def kday_nearest(%{year: _, month: _, day: _, calendar: calendar} = date, k) when k in 1..7 do @@ -180,7 +180,7 @@ defmodule Cldr.Calendar.Kday do """ @spec kday_before(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def kday_before(%{year: _, month: _, day: _, calendar: calendar} = date, k) when k in 1..7 do @@ -226,7 +226,7 @@ defmodule Cldr.Calendar.Kday do """ @spec kday_after(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def kday_after(%{year: _, month: _, day: _, calendar: calendar} = date, k) when k in 1..7 do @@ -275,7 +275,7 @@ defmodule Cldr.Calendar.Kday do """ @spec nth_kday(Calendar.day() | Date.t(), integer(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def nth_kday(%{year: _, month: _, day: _, calendar: calendar} = date, n, k) when k in 1..7 and is_integer(n) do @@ -321,7 +321,7 @@ defmodule Cldr.Calendar.Kday do """ @spec first_kday(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def first_kday(%{year: _, month: _, day: _, calendar: calendar} = date, k) when k in 1..7 do @@ -359,7 +359,7 @@ defmodule Cldr.Calendar.Kday do """ @spec last_kday(Calendar.day() | Date.t(), Cldr.Calendar.day_of_week()) :: - Calendar.day() | Date.t() + Calendar.day() | Date.t() | {:error, :incompatible_calendars | :invalid_date} def last_kday(%{year: _, month: _, day: _, calendar: calendar} = date, k) when k in 1..7 do diff --git a/mix.exs b/mix.exs index 1365796..2ebd2c4 100644 --- a/mix.exs +++ b/mix.exs @@ -25,7 +25,13 @@ defmodule Cldr.Calendar.MixProject do ignore_warnings: ".dialyzer_ignore_warnings", plt_add_apps: ~w(inets jason mix ex_cldr_currencies ex_cldr_units ex_cldr_lists ex_cldr_numbers calendar_interval)a, - flags: [:underspecs] + flags: [ + :error_handling, + :unknown, + :underspecs, + :extra_return, + :missing_return + ] ], compilers: Mix.compilers() ] diff --git a/mix/basic_behaviour.ex b/mix/basic_behaviour.ex index 3dc70bf..e0b704f 100644 --- a/mix/basic_behaviour.ex +++ b/mix/basic_behaviour.ex @@ -28,7 +28,7 @@ defmodule Cldr.Calendar.Behaviour.Basic do """ def date_to_iso_days(year, month, day) do - epoch() + 365 * (year - 1) + floor(year / 4) + 30 * (month - 1) + day - 1 + floor(epoch() + 365 * (year - 1) + floor(year / 4) + 30 * (month - 1) + day - 1) end @doc """ diff --git a/mix/for_dialyzer.ex b/mix/for_dialyzer.ex index 215e35b..442614e 100644 --- a/mix/for_dialyzer.ex +++ b/mix/for_dialyzer.ex @@ -11,5 +11,4 @@ defmodule Cldr.Calendar.ForDialyzer do MyApp.Cldr.Calendar.localize(%{month: 3}, :month, format: :wide, locale: :da) end - end