Skip to content

Commit

Permalink
Add clock_shift/2
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieuprog committed Jul 29, 2024
1 parent a828fbc commit 904d4c7
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 13 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
* `TzExtra.new_resolved_datetime!/4`
* `TzExtra.utc_datetime_range/3`
* `TzExtra.round_datetime/3`
* `TzExtra.advances_clock?/1`
* `TzExtra.shifts_clock?/1`
* `TzExtra.clock_shift/2`
* `TzExtra.next_period_start_in_year_span/1`

### `TzExtra.countries_time_zones/0`
Expand Down
20 changes: 18 additions & 2 deletions lib/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,15 @@ defmodule TzExtra.Compiler do
Enum.map(start_unix_time..end_unix_time//step_in_seconds, &DateTime.from_unix!(&1))
end

def advances_clock?(time_zone_id) when time_zone_id != nil do
def shifts_clock?(time_zone_id) when time_zone_id != nil do
case Tz.PeriodsProvider.periods(time_zone_id) do
{:error, :time_zone_not_found} ->
raise "invalid time zone #{time_zone_id}"

{:ok, [{utc_secs, _, _, nil} | _]} ->
hardcoded_dst_future_periods? =
DateTime.from_gregorian_seconds(utc_secs).year > Tz.PeriodsProvider.compiled_at().year + 20
DateTime.from_gregorian_seconds(utc_secs).year >
Tz.PeriodsProvider.compiled_at().year + 20

hardcoded_dst_future_periods?

Expand All @@ -259,6 +260,21 @@ defmodule TzExtra.Compiler do
DateTime.from_gregorian_seconds(from)
|> DateTime.shift_zone!(datetime.time_zone, Tz.TimeZoneDatabase)
end

def clock_shift(datetime1, datetime2) do
if DateTime.compare(datetime1, datetime2) == :gt do
raise "first datetime must be earlier than or equal to second datetime"
end

offset1 = datetime1.utc_offset + datetime1.std_offset
offset2 = datetime2.utc_offset + datetime2.std_offset

cond do
offset1 < offset2 -> :forward
offset1 > offset2 -> :backward
offset1 == offset2 -> :no_shift
end
end
end,
for %{code: country_code} <- countries do
{:ok, time_zones_for_country} =
Expand Down
41 changes: 31 additions & 10 deletions test/tz_extra_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,43 @@ defmodule TzExtraTest do
assert TzExtra.iana_version() == Tz.iana_version()
end

test "advances_clock?/1" do
assert TzExtra.advances_clock?("Europe/Brussels")
assert TzExtra.advances_clock?("Africa/Casablanca")
test "shifts_clock?/1" do
assert TzExtra.shifts_clock?("Europe/Brussels")
assert TzExtra.shifts_clock?("Africa/Casablanca")

refute TzExtra.advances_clock?("Asia/Manila")
refute TzExtra.advances_clock?("Asia/Tokyo")
refute TzExtra.shifts_clock?("Asia/Manila")
refute TzExtra.shifts_clock?("Asia/Tokyo")
end

test "next_period_start_in_year_span/1" do
{:ambiguous, first_dt, second_dt} = DateTime.new(~D[2018-10-28], ~T[02:00:00], "Europe/Copenhagen", Tz.TimeZoneDatabase)
dt = TzExtra.next_period_start_in_year_span(DateTime.add(first_dt, -1, :day, Tz.TimeZoneDatabase))
test "next_period_start_in_year_span/1 and clock_shift/2" do
{:ambiguous, first_dt, second_dt} =
DateTime.new(~D[2018-10-28], ~T[02:00:00], "Europe/Copenhagen", Tz.TimeZoneDatabase)

dt =
TzExtra.next_period_start_in_year_span(
DateTime.add(first_dt, -1, :day, Tz.TimeZoneDatabase)
)

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

{:gap, dt_just_before, dt_just_after} = DateTime.new(~D[2019-03-31], ~T[02:30:00], "Europe/Copenhagen", Tz.TimeZoneDatabase)
dt = TzExtra.next_period_start_in_year_span(DateTime.add(dt_just_before, -1, :day, Tz.TimeZoneDatabase))
assert TzExtra.clock_shift(first_dt, second_dt) == :backward
assert TzExtra.clock_shift(first_dt, first_dt) == :no_shift

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

dt =
TzExtra.next_period_start_in_year_span(
DateTime.add(dt_just_before, -1, :day, Tz.TimeZoneDatabase)
)

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

assert TzExtra.clock_shift(dt_just_before, dt_just_after) == :forward

assert_raise RuntimeError, fn ->
assert TzExtra.clock_shift(dt_just_after, dt_just_before) == :backward
end
end

test "time_zone_id_exists?/1" do
Expand Down

0 comments on commit 904d4c7

Please sign in to comment.