Skip to content

Commit 116b306

Browse files
jbrockmendelclaude
andcommitted
BUG: fix date_range inclusive filtering with periods (#46331)
When date_range was called with start+periods or end+periods (without the other endpoint), the inclusive parameter failed to filter the unspecified endpoint because Timestamp(None) produced NaT, causing the endpoint comparison to always fail. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 932f54c commit 116b306

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

doc/source/whatsnew/v3.1.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Datetimelike
159159
^^^^^^^^^^^^
160160
- Bug in :class:`Timestamp` constructor where passing ``np.str_`` objects would fail in Cython string parsing (:issue:`48974`)
161161
- Bug in :class:`Timestamp` constructor, :class:`Timedelta` constructor, :func:`to_datetime`, and :func:`to_timedelta` with non-round ``float`` input and ``unit`` failing to raise when the value is just outside the representable bounds (:issue:`57366`)
162+
- Bug in :func:`date_range` where ``inclusive`` parameter failed to filter endpoints when only ``start`` and ``periods`` or ``end`` and ``periods`` were specified (:issue:`46331`)
162163
- Bug in :func:`to_datetime` and :func:`to_timedelta` on ARM platforms where round ``float`` values outside the int64 domain (e.g. ``float(2**63)``) could silently produce incorrect results instead of raising (:issue:`64619`)
163164
- Bug in :func:`to_datetime` and :func:`to_timedelta` where ``uint64`` values greater than ``int64`` max silently overflowed instead of raising :class:`OutOfBoundsDatetime` or :class:`OutOfBoundsTimedelta` (:issue:`60677`)
164165
- Bug in :meth:`DatetimeArray.isin` and :meth:`TimedeltaArray.isin` where mismatched resolutions could silently truncate finer-resolution values, leading to false matches (:issue:`64545`)

pandas/core/arrays/datetimes.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -551,11 +551,11 @@ def _generate_range(
551551
if start == end:
552552
if not left_inclusive and not right_inclusive:
553553
i8values = i8values[1:-1]
554-
else:
555-
start_i8 = Timestamp(start)._value
556-
end_i8 = Timestamp(end)._value
557-
if not left_inclusive or not right_inclusive:
558-
if not left_inclusive and len(i8values) and i8values[0] == start_i8:
554+
elif not left_inclusive or not right_inclusive:
555+
if len(i8values):
556+
start_i8 = Timestamp(start)._value if start is not None else i8values[0]
557+
end_i8 = Timestamp(end)._value if end is not None else i8values[-1]
558+
if not left_inclusive and i8values[0] == start_i8:
559559
i8values = i8values[1:]
560560
if not right_inclusive and len(i8values) and i8values[-1] == end_i8:
561561
i8values = i8values[:-1]

pandas/tests/indexes/datetimes/test_date_range.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,38 @@ def test_range_where_start_equal_end(self, inclusive_endpoints_fixture):
743743

744744
tm.assert_index_equal(result, expected)
745745

746+
@pytest.mark.parametrize(
747+
"inclusive, expected_values",
748+
[
749+
("both", ["2020-06-01", "2020-06-02", "2020-06-03", "2020-06-04"]),
750+
("left", ["2020-06-01", "2020-06-02", "2020-06-03"]),
751+
("right", ["2020-06-02", "2020-06-03", "2020-06-04"]),
752+
("neither", ["2020-06-02", "2020-06-03"]),
753+
],
754+
)
755+
def test_inclusive_with_periods_and_start(self, inclusive, expected_values):
756+
# GH#46331 - inclusive should filter endpoints even when
757+
# only start+periods is provided (end is not specified)
758+
result = date_range(start="2020-06-01", periods=4, inclusive=inclusive)
759+
expected = DatetimeIndex(expected_values, freq="D")
760+
tm.assert_index_equal(result, expected)
761+
762+
@pytest.mark.parametrize(
763+
"inclusive, expected_values",
764+
[
765+
("both", ["2020-06-01", "2020-06-02", "2020-06-03", "2020-06-04"]),
766+
("left", ["2020-06-01", "2020-06-02", "2020-06-03"]),
767+
("right", ["2020-06-02", "2020-06-03", "2020-06-04"]),
768+
("neither", ["2020-06-02", "2020-06-03"]),
769+
],
770+
)
771+
def test_inclusive_with_periods_and_end(self, inclusive, expected_values):
772+
# GH#46331 - inclusive should filter endpoints even when
773+
# only end+periods is provided (start is not specified)
774+
result = date_range(end="2020-06-04", periods=4, inclusive=inclusive)
775+
expected = DatetimeIndex(expected_values, freq="D")
776+
tm.assert_index_equal(result, expected)
777+
746778
def test_freq_dateoffset_with_relateivedelta_nanos(self):
747779
# GH 46877
748780
freq = DateOffset(hours=10, days=57, nanoseconds=3)

0 commit comments

Comments
 (0)