diff --git a/lib/cocktail/validation/shift.ex b/lib/cocktail/validation/shift.ex index 1c5c766..4f1975f 100644 --- a/lib/cocktail/validation/shift.ex +++ b/lib/cocktail/validation/shift.ex @@ -20,6 +20,7 @@ defmodule Cocktail.Validation.Shift do time |> shift_time("#{type}": amount) |> apply_option(option) + |> maybe_dst_change(time) {:change, new_time} end @@ -29,4 +30,27 @@ defmodule Cocktail.Validation.Shift do defp apply_option(time, :beginning_of_day), do: time |> beginning_of_day() defp apply_option(time, :beginning_of_hour), do: %{time | minute: 0, second: 0, microsecond: {0, 0}} defp apply_option(time, :beginning_of_minute), do: %{time | second: 0, microsecond: {0, 0}} + + defp maybe_dst_change(%DateTime{} = new_time, %DateTime{} = time) do + dst_diff = new_time.std_offset - time.std_offset + + case dst_diff do + 0 -> + new_time + + diff -> + maybe_shift_time(new_time, time, diff) + end + end + + defp maybe_dst_change(new_time, _time), do: new_time + + defp maybe_shift_time(new_time, time, dst_diff) do + shifted_time = shift_time(new_time, seconds: -dst_diff) + + case DateTime.compare(shifted_time, time) do + :eq -> new_time + _ -> shifted_time + end + end end diff --git a/test/cocktail/daily_test.exs b/test/cocktail/daily_test.exs index 9318705..4cb61c1 100644 --- a/test/cocktail/daily_test.exs +++ b/test/cocktail/daily_test.exs @@ -163,4 +163,34 @@ defmodule Cocktail.DailyTest do |> Cocktail.Schedule.occurrences(~N[2015-01-24 18:30:00]) |> Enum.take(100) == [~N[2015-01-24 18:30:00]] end + + test "generating occurrences with spring forward transition" do + schedule = + ~Y[2022-03-12 18:00:00 America/Los_Angeles] + |> Schedule.new() + |> Schedule.add_recurrence_rule(:daily) + + times = schedule |> Schedule.occurrences() |> Enum.take(3) + + assert times == [ + ~Y[2022-03-12 18:00:00 America/Los_Angeles], + ~Y[2022-03-13 18:00:00 America/Los_Angeles], + ~Y[2022-03-14 18:00:00 America/Los_Angeles] + ] + end + + test "generating occurrences with fall back transition" do + schedule = + ~Y[2022-11-05 18:00:00 America/Los_Angeles] + |> Schedule.new() + |> Schedule.add_recurrence_rule(:daily) + + times = schedule |> Schedule.occurrences() |> Enum.take(3) + + assert times == [ + ~Y[2022-11-05 18:00:00 America/Los_Angeles], + ~Y[2022-11-06 18:00:00 America/Los_Angeles], + ~Y[2022-11-07 18:00:00 America/Los_Angeles] + ] + end end diff --git a/test/cocktail/weekly_test.exs b/test/cocktail/weekly_test.exs index e750ef1..7f9dd91 100644 --- a/test/cocktail/weekly_test.exs +++ b/test/cocktail/weekly_test.exs @@ -154,4 +154,19 @@ defmodule Cocktail.WeeklyTest do ~Y[2017-01-09 10:00:00 America/Los_Angeles] ] end + + test "Weekly with dst transition" do + times = + ~Y[2022-03-12 06:00:00 America/Los_Angeles] + |> Cocktail.schedule() + |> Schedule.add_recurrence_rule(:weekly) + |> Cocktail.Schedule.occurrences() + |> Enum.take(3) + + assert times == [ + ~Y[2022-03-12 06:00:00 America/Los_Angeles], + ~Y[2022-03-19 06:00:00 America/Los_Angeles], + ~Y[2022-03-26 06:00:00 America/Los_Angeles] + ] + end end