Skip to content

Commit

Permalink
Add next_period/1
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieuprog committed Jul 29, 2024
1 parent a90c563 commit 78c0f78
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 2 deletions.
30 changes: 30 additions & 0 deletions lib/tz/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,36 @@ defmodule Tz.Compiler do
def iana_version() do
unquote(tzdata_version)
end

def compiled_at() do
unquote(Macro.escape(DateTime.utc_now()))
end

def next_period(%DateTime{} = datetime) do
{gregorian_secs, _} = DateTime.to_gregorian_seconds(datetime)

{:ok, periods} = periods(datetime.time_zone)
reversed_periods = Enum.reverse(periods)

period = Enum.find(reversed_periods, fn {from, _, _, _} -> gregorian_secs < from end)

if period do
period
else
{utc_secs, {utc_to_std_offset, _, _}, _, rules_and_template} = hd(periods)

periods =
Tz.TimeZoneDatabase.generate_dynamic_periods(
utc_secs,
utc_to_std_offset,
rules_and_template
)

reversed_periods = Enum.reverse(periods)

Enum.find(reversed_periods, fn {from, _, _, _} -> gregorian_secs < from end)
end
end
end,
for period_or_link <- periods_and_links do
case period_or_link do
Expand Down
7 changes: 5 additions & 2 deletions lib/tz/time_zone_database.ex
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ defmodule Tz.TimeZoneDatabase do
}
end

defp generate_dynamic_periods(secs, utc_offset, {rule_name, format_time_zone_abbr}) do
@doc false
def generate_dynamic_periods(secs, utc_offset, {rule_name, format_time_zone_abbr}) do
%{year: year} = gregorian_seconds_to_naive_datetime(secs)

[rule1, rule2] = Tz.OngoingChangingRulesProvider.rules(rule_name)
Expand All @@ -114,7 +115,9 @@ defmodule Tz.TimeZoneDatabase do
Tz.IanaFileParser.change_rule_year(rule2, year - 1),
Tz.IanaFileParser.change_rule_year(rule1, year - 1),
Tz.IanaFileParser.change_rule_year(rule2, year),
Tz.IanaFileParser.change_rule_year(rule1, year)
Tz.IanaFileParser.change_rule_year(rule1, year),
Tz.IanaFileParser.change_rule_year(rule2, year + 1),
Tz.IanaFileParser.change_rule_year(rule1, year + 1)
])

zone_line = %{
Expand Down
38 changes: 38 additions & 0 deletions test/time_zone_database_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,42 @@ defmodule TimeZoneDatabaseTest do

assert DateTime.to_iso8601(datetime) == "2030-01-01T00:00:00+00:00"
end

test "next_period/1 and previous_period/1" do
{:ok, dt} =
DateTime.new(~D[2030-09-01], ~T[10:00:00], "Europe/Copenhagen", Tz.TimeZoneDatabase)

{from, _, _, _} = Tz.PeriodsProvider.next_period(dt)

datetime_next_period =
DateTime.from_gregorian_seconds(from)
|> DateTime.shift_zone!(dt.time_zone, Tz.TimeZoneDatabase)

{:ambiguous, first_dt, second_dt} =
DateTime.new(~D[2030-10-27], ~T[02:00:00], "Europe/Copenhagen", Tz.TimeZoneDatabase)

assert DateTime.compare(datetime_next_period, second_dt) == :eq

{from, _, _, _} = Tz.PeriodsProvider.next_period(second_dt)

datetime_next_period =
DateTime.from_gregorian_seconds(from)
|> DateTime.shift_zone!(dt.time_zone, Tz.TimeZoneDatabase)

{:gap, _dt_just_before, dt_just_after} =
DateTime.new(~D[2031-03-30], ~T[02:30:00], "Europe/Copenhagen", Tz.TimeZoneDatabase)

assert DateTime.compare(datetime_next_period, dt_just_after) == :eq

{from, _, _, _} = Tz.PeriodsProvider.next_period(first_dt)

datetime_next_period =
DateTime.from_gregorian_seconds(from)
|> DateTime.shift_zone!(dt.time_zone, Tz.TimeZoneDatabase)

{:ambiguous, _first_dt, second_dt} =
DateTime.new(~D[2030-10-27], ~T[02:00:00], "Europe/Copenhagen", Tz.TimeZoneDatabase)

assert DateTime.compare(datetime_next_period, second_dt) == :eq
end
end

0 comments on commit 78c0f78

Please sign in to comment.