Skip to content

Commit

Permalink
support Duration in Date.range/3
Browse files Browse the repository at this point in the history
  • Loading branch information
tfiedlerdejanze committed Jan 10, 2025
1 parent ef1450d commit 563fd96
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 2 deletions.
19 changes: 17 additions & 2 deletions lib/elixir/lib/calendar/date.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ defmodule Date do
iex> Date.range(~D[1999-01-01], ~D[2000-01-01])
Date.range(~D[1999-01-01], ~D[2000-01-01])
Alternatively, a range may be built from a first `Date` and a `Duration`:
iex> Date.range(~D[1999-01-01], Duration.new!(year: 1))
Date.range(~D[1999-01-01], ~D[2000-01-01])
A range of dates implements the `Enumerable` protocol, which means
functions in the `Enum` module can be used to work with
ranges:
Expand All @@ -100,7 +105,7 @@ defmodule Date do
"""
@doc since: "1.5.0"
@spec range(Calendar.date(), Calendar.date()) :: Date.Range.t()
@spec range(Calendar.date(), Calendar.date() | Duration.t()) :: Date.Range.t()
def range(%{calendar: calendar} = first, %{calendar: calendar} = last) do
{first_days, _} = to_iso_days(first)
{last_days, _} = to_iso_days(last)
Expand All @@ -119,6 +124,11 @@ defmodule Date do
range(first, first_days, last, last_days, calendar, step)
end

def range(%{calendar: calendar} = first, %Duration{} = duration) do
last = shift(first, duration)
range(first, last)
end

def range(%{calendar: _, year: _, month: _, day: _}, %{calendar: _, year: _, month: _, day: _}) do
raise ArgumentError, "both dates must have matching calendars"
end
Expand All @@ -140,7 +150,7 @@ defmodule Date do
"""
@doc since: "1.12.0"
@spec range(Calendar.date(), Calendar.date(), step :: pos_integer | neg_integer) ::
@spec range(Calendar.date(), Calendar.date() | Duration.t(), step :: pos_integer | neg_integer) ::
Date.Range.t()
def range(%{calendar: calendar} = first, %{calendar: calendar} = last, step)
when is_integer(step) and step != 0 do
Expand All @@ -149,6 +159,11 @@ defmodule Date do
range(first, first_days, last, last_days, calendar, step)
end

def range(%{calendar: calendar} = first, %Duration{} = duration, step) do
last = shift(first, duration)
range(first, last, step)
end

def range(
%{calendar: _, year: _, month: _, day: _} = first,
%{calendar: _, year: _, month: _, day: _} = last,
Expand Down
22 changes: 22 additions & 0 deletions lib/elixir/test/elixir/calendar/date_range_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ defmodule Date.RangeTest do

@asc_range Date.range(~D[2000-01-01], ~D[2001-01-01])
@asc_range_2 Date.range(~D[2000-01-01], ~D[2001-01-01], 2)
@asc_range_duration Date.range(~D[2000-01-01], Duration.new!(year: 1))
@asc_range_duration_2 Date.range(~D[2000-01-01], Duration.new!(year: 1), 2)
@desc_range Date.range(~D[2001-01-01], ~D[2000-01-01], -1)
@desc_range_2 Date.range(~D[2001-01-01], ~D[2000-01-01], -2)
@desc_range_duration Date.range(~D[2001-01-01], Duration.new!(year: -1))
@desc_range_duration_2 Date.range(~D[2001-01-01], Duration.new!(year: -1), 2)
@empty_range Date.range(~D[2001-01-01], ~D[2000-01-01], 1)

describe "Enum.member?/2" do
Expand All @@ -20,6 +24,9 @@ defmodule Date.RangeTest do

assert Enum.member?(@asc_range_2, ~D[2000-01-03])
refute Enum.member?(@asc_range_2, ~D[2000-01-02])

assert Enum.member?(@asc_range_duration, ~D[2000-01-03])
refute Enum.member?(@asc_range_duration_2, ~D[2000-01-02])
end

test "for descending range" do
Expand All @@ -31,6 +38,9 @@ defmodule Date.RangeTest do

assert Enum.member?(@desc_range_2, ~D[2000-12-30])
refute Enum.member?(@desc_range_2, ~D[2000-12-29])

assert Enum.member?(@desc_range_duration, ~D[2000-12-30])
refute Enum.member?(@desc_range_duration_2, ~D[2000-12-29])
end

test "empty range" do
Expand Down Expand Up @@ -109,6 +119,18 @@ defmodule Date.RangeTest do
assert Enum.to_list(range) == [~D[2000-01-01], ~D[2000-01-03]]
end

test "works with durations" do
range = Date.range(~D[2000-01-01], Duration.new!(day: 1))
assert range.first == ~D[2000-01-01]
assert range.last == ~D[2000-01-02]
assert Enum.to_list(range) == [~D[2000-01-01], ~D[2000-01-02]]

range = Date.range(~D[2000-01-01], Duration.new!(day: 2), 2)
assert range.first == ~D[2000-01-01]
assert range.last == ~D[2000-01-03]
assert Enum.to_list(range) == [~D[2000-01-01], ~D[2000-01-03]]
end

test "both dates must have matching calendars" do
first = ~D[2000-01-01]
last = Calendar.Holocene.date(12001, 1, 1)
Expand Down

0 comments on commit 563fd96

Please sign in to comment.