Skip to content

Commit 72659ab

Browse files
support Duration in Date.range/3
1 parent ef1450d commit 72659ab

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

lib/elixir/lib/calendar/date.ex

+30-2
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ defmodule Date do
100100
101101
"""
102102
@doc since: "1.5.0"
103-
@spec range(Calendar.date(), Calendar.date()) :: Date.Range.t()
103+
@spec range(Calendar.date(), Calendar.date() | Duration.t()) :: Date.Range.t()
104104
def range(%{calendar: calendar} = first, %{calendar: calendar} = last) do
105105
{first_days, _} = to_iso_days(first)
106106
{last_days, _} = to_iso_days(last)
@@ -119,6 +119,26 @@ defmodule Date do
119119
range(first, first_days, last, last_days, calendar, step)
120120
end
121121

122+
def range(%{calendar: calendar} = first, %Duration{} = duration) do
123+
last = shift(first, duration)
124+
125+
{first_days, _} = to_iso_days(first)
126+
{last_days, _} = to_iso_days(last)
127+
128+
step =
129+
if first_days <= last_days do
130+
1
131+
else
132+
IO.warn(
133+
"a negative range was inferred for Date.range/2, call Date.range/3 instead with -1 as third argument"
134+
)
135+
136+
-1
137+
end
138+
139+
range(first, first_days, last, last_days, calendar, step)
140+
end
141+
122142
def range(%{calendar: _, year: _, month: _, day: _}, %{calendar: _, year: _, month: _, day: _}) do
123143
raise ArgumentError, "both dates must have matching calendars"
124144
end
@@ -140,7 +160,7 @@ defmodule Date do
140160
141161
"""
142162
@doc since: "1.12.0"
143-
@spec range(Calendar.date(), Calendar.date(), step :: pos_integer | neg_integer) ::
163+
@spec range(Calendar.date(), Calendar.date() | Duration.t(), step :: pos_integer | neg_integer) ::
144164
Date.Range.t()
145165
def range(%{calendar: calendar} = first, %{calendar: calendar} = last, step)
146166
when is_integer(step) and step != 0 do
@@ -149,6 +169,14 @@ defmodule Date do
149169
range(first, first_days, last, last_days, calendar, step)
150170
end
151171

172+
def range(%{calendar: calendar} = first, %Duration{} = duration, step) do
173+
last = shift(first, duration)
174+
175+
{first_days, _} = to_iso_days(first)
176+
{last_days, _} = to_iso_days(last)
177+
range(first, first_days, last, last_days, calendar, step)
178+
end
179+
152180
def range(
153181
%{calendar: _, year: _, month: _, day: _} = first,
154182
%{calendar: _, year: _, month: _, day: _} = last,

lib/elixir/test/elixir/calendar/date_range_test.exs

+22
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ defmodule Date.RangeTest do
66

77
@asc_range Date.range(~D[2000-01-01], ~D[2001-01-01])
88
@asc_range_2 Date.range(~D[2000-01-01], ~D[2001-01-01], 2)
9+
@asc_range_duration Date.range(~D[2000-01-01], Duration.new!(year: 1))
10+
@asc_range_duration_2 Date.range(~D[2000-01-01], Duration.new!(year: 1), 2)
911
@desc_range Date.range(~D[2001-01-01], ~D[2000-01-01], -1)
1012
@desc_range_2 Date.range(~D[2001-01-01], ~D[2000-01-01], -2)
13+
@desc_range_duration Date.range(~D[2001-01-01], Duration.new!(year: -1))
14+
@desc_range_duration_2 Date.range(~D[2001-01-01], Duration.new!(year: -1), 2)
1115
@empty_range Date.range(~D[2001-01-01], ~D[2000-01-01], 1)
1216

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

2125
assert Enum.member?(@asc_range_2, ~D[2000-01-03])
2226
refute Enum.member?(@asc_range_2, ~D[2000-01-02])
27+
28+
assert Enum.member?(@asc_range_duration, ~D[2000-01-03])
29+
refute Enum.member?(@asc_range_duration_2, ~D[2000-01-02])
2330
end
2431

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

3239
assert Enum.member?(@desc_range_2, ~D[2000-12-30])
3340
refute Enum.member?(@desc_range_2, ~D[2000-12-29])
41+
42+
assert Enum.member?(@desc_range_duration, ~D[2000-12-30])
43+
refute Enum.member?(@desc_range_duration_2, ~D[2000-12-29])
3444
end
3545

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

122+
test "works with durations" do
123+
range = Date.range(~D[2000-01-01], Duration.new!(day: 1))
124+
assert range.first == ~D[2000-01-01]
125+
assert range.last == ~D[2000-01-02]
126+
assert Enum.to_list(range) == [~D[2000-01-01], ~D[2000-01-02]]
127+
128+
range = Date.range(~D[2000-01-01], Duration.new!(day: 2), 2)
129+
assert range.first == ~D[2000-01-01]
130+
assert range.last == ~D[2000-01-03]
131+
assert Enum.to_list(range) == [~D[2000-01-01], ~D[2000-01-03]]
132+
end
133+
112134
test "both dates must have matching calendars" do
113135
first = ~D[2000-01-01]
114136
last = Calendar.Holocene.date(12001, 1, 1)

0 commit comments

Comments
 (0)