diff --git a/lib/tz/time_zone_database.ex b/lib/tz/time_zone_database.ex index 78e46f4..d803d97 100644 --- a/lib/tz/time_zone_database.ex +++ b/lib/tz/time_zone_database.ex @@ -136,6 +136,10 @@ defmodule Tz.TimeZoneDatabase do div(days * 86_400_000_000 + parts_in_day, 1_000_000) end + defp iso_days_to_gregorian_seconds({days, {parts_in_day, parts_per_day}}) do + div(days * parts_per_day + parts_in_day, div(parts_per_day, 86400)) + end + defp naive_datetime_to_gregorian_seconds(%{calendar: Calendar.ISO, year: year}) when year < 0, do: 0 diff --git a/test/datetime_math_test.exs b/test/datetime_math_test.exs new file mode 100644 index 0000000..b307b12 --- /dev/null +++ b/test/datetime_math_test.exs @@ -0,0 +1,45 @@ +defmodule DateTimeMathTest do + use ExUnit.Case, async: true + + @timezone "America/Fortaleza" + @datetime DateTime.from_naive!(~N[2025-04-15 00:00:01.000000], @timezone, Tz.TimeZoneDatabase) + @added_second DateTime.add(@datetime, +1, :second, Tz.TimeZoneDatabase) + @minus_second DateTime.add(@datetime, -1, :second, Tz.TimeZoneDatabase) + + describe "nanosecond" do + test "adds 1 second with nanosecond unit" do + amount = System.convert_time_unit(+1, :second, :nanosecond) + unit = :nanosecond + assert DateTime.add(@datetime, amount, unit, Tz.TimeZoneDatabase) == @added_second + end + + test "subtracts 1 second with nanosecond unit" do + amount = System.convert_time_unit(-1, :second, :nanosecond) + unit = :nanosecond + assert DateTime.add(@datetime, amount, unit, Tz.TimeZoneDatabase) == @minus_second + end + end + + describe "strictly positive integer with units ranging from decisecond (10^-1s) to Planck time (10^-44s)" do + # To understand how this units work see: + # - https://hexdocs.pm/elixir/1.18.2/System.html#t:time_unit/0 + # - https://en.wikipedia.org/wiki/Orders_of_magnitude_(time) + @units_in_parts_per_second Enum.map(1..44, &System.convert_time_unit(1, :second, 10 ** &1)) + + test "adds 1 second" do + Enum.each(@units_in_parts_per_second, fn parts_per_second -> + amount = +parts_per_second + unit = parts_per_second + assert DateTime.add(@datetime, amount, unit, Tz.TimeZoneDatabase) == @added_second + end) + end + + test "subtracts 1 second" do + Enum.each(@units_in_parts_per_second, fn parts_per_second -> + amount = -parts_per_second + unit = parts_per_second + assert DateTime.add(@datetime, amount, unit, Tz.TimeZoneDatabase) == @minus_second + end) + end + end +end diff --git a/test/dynamic_periods_test.exs b/test/dynamic_periods_test.exs index 1f4fa91..83a3c64 100644 --- a/test/dynamic_periods_test.exs +++ b/test/dynamic_periods_test.exs @@ -1,5 +1,5 @@ defmodule DynamicPeriodsTest do - use ExUnit.Case + use ExUnit.Case, async: true test "dynamic periods" do time_zone = "Antarctica/Troll" diff --git a/test/time_zone_database_test.exs b/test/time_zone_database_test.exs index a6c1bae..f018d38 100644 --- a/test/time_zone_database_test.exs +++ b/test/time_zone_database_test.exs @@ -1,5 +1,5 @@ defmodule TimeZoneDatabaseTest do - use ExUnit.Case + use ExUnit.Case, async: true alias Support.HoloceneCalendar diff --git a/test/updater_test.exs b/test/updater_test.exs index f12f6e1..6d8eb10 100644 --- a/test/updater_test.exs +++ b/test/updater_test.exs @@ -1,6 +1,7 @@ defmodule UpdaterTest do - use ExUnit.Case + use ExUnit.Case, async: true + @tag :updater test "updater is only started once" do assert {:ok, _} = start_supervised(Tz.UpdatePeriodically) assert {:error, _} = start_supervised(Tz.UpdatePeriodically)