Skip to content

Commit 6f58739

Browse files
committed
Stop interval ranges after yielding the final endpoint
1 parent e06dace commit 6f58739

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

‎src/pendulum/interval.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ def range(self, unit: str, amount: int = 1) -> Iterator[_T]:
300300
while op(start, end):
301301
yield start
302302

303+
if start == end and amount != 0:
304+
break
305+
303306
start = getattr(self.start, method)(**{unit: i})
304307

305308
i += amount

‎tests/interval/test_range.py‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
from __future__ import annotations
22

3+
from itertools import islice
4+
5+
import pytest
6+
37
import pendulum
48

59
from pendulum.interval import Interval
@@ -117,3 +121,73 @@ def test_range_amount():
117121
assert_datetime(r[1], 2016, 10, 16, 1, 0, 0)
118122
assert_datetime(r[2], 2016, 10, 18, 0, 0, 0)
119123
assert_datetime(r[3], 2016, 10, 20, 0, 0, 0)
124+
125+
126+
@pytest.mark.parametrize("factory", [pendulum.date, pendulum.datetime])
127+
@pytest.mark.parametrize("absolute", [False, True])
128+
@pytest.mark.parametrize(
129+
"start, end, unit, amount",
130+
[
131+
((9999, 12, 30), (9999, 12, 31), "days", 1),
132+
((1, 1, 2), (1, 1, 1), "days", 1),
133+
((9999, 12, 31), (9999, 12, 31), "days", 1),
134+
((1, 1, 1), (1, 1, 1), "days", 1),
135+
((9999, 12, 29), (9999, 12, 31), "days", 2),
136+
((1, 1, 3), (1, 1, 1), "days", 2),
137+
((9999, 12, 24), (9999, 12, 31), "weeks", 1),
138+
((1, 1, 8), (1, 1, 1), "weeks", 1),
139+
((9999, 11, 30), (9999, 12, 30), "months", 1),
140+
((1, 2, 1), (1, 1, 1), "months", 1),
141+
((9998, 12, 31), (9999, 12, 31), "years", 1),
142+
((2, 1, 1), (1, 1, 1), "years", 1),
143+
],
144+
)
145+
def test_range_stops_at_final_endpoint(factory, absolute, start, end, unit, amount):
146+
start, end = factory(*start), factory(*end)
147+
interval = pendulum.interval(start, end, absolute=absolute)
148+
expected = [start] if start == end else [start, end]
149+
if absolute:
150+
expected.sort()
151+
152+
assert list(interval.range(unit, amount)) == expected
153+
154+
155+
@pytest.mark.parametrize("reverse", [False, True])
156+
@pytest.mark.parametrize("unit", ["hours", "minutes", "seconds", "microseconds"])
157+
def test_range_stops_at_final_datetime_endpoint(reverse, unit):
158+
if reverse:
159+
end = pendulum.datetime(1, 1, 1)
160+
start = end.add(**{unit: 1})
161+
else:
162+
end = pendulum.datetime(9999, 12, 31, 23, 59, 59, 999999)
163+
start = end.subtract(**{unit: 1})
164+
165+
assert list(pendulum.interval(start, end).range(unit)) == [start, end]
166+
167+
168+
@pytest.mark.parametrize("end_day", [1, 3])
169+
def test_range_zero_amount(end_day):
170+
start = pendulum.date(2000, 1, 1)
171+
end = pendulum.date(2000, 1, end_day)
172+
173+
assert list(islice(pendulum.interval(start, end).range("days", 0), 3)) == [
174+
start,
175+
start,
176+
start,
177+
]
178+
179+
180+
@pytest.mark.parametrize("reverse", [False, True])
181+
@pytest.mark.parametrize("absolute", [False, True])
182+
def test_range_amount_does_not_reach_endpoint(reverse, absolute):
183+
start, end = pendulum.date(2000, 1, 1), pendulum.date(2000, 1, 4)
184+
if reverse:
185+
start, end = end, start
186+
expected = [pendulum.date(2000, 1, 1), pendulum.date(2000, 1, 3)]
187+
if reverse and not absolute:
188+
expected = [pendulum.date(2000, 1, 4), pendulum.date(2000, 1, 2)]
189+
190+
assert (
191+
list(pendulum.interval(start, end, absolute=absolute).range("days", 2))
192+
== expected
193+
)

0 commit comments

Comments
 (0)