Skip to content

Commit 872c524

Browse files
jrmylowpre-commit-ci[bot]mroeschke
authored andcommitted
BUG: Negative freq in date_range produces values out of start and endpoints (pandas-dev#56832)
* pandas-dev#56147 negative offset and year end interaction * pandas-dev#56147 tests * documentation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixing typing/pylint to mirror other branch * moved note to Datetimelike * documentation re-merge * whatsnew update * updated date_range docstring * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * reformatted docstring * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update doc/source/whatsnew/v3.0.0.rst Co-authored-by: Matthew Roeschke <[email protected]> --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Matthew Roeschke <[email protected]>
1 parent 137cc41 commit 872c524

File tree

4 files changed

+31
-8
lines changed

4 files changed

+31
-8
lines changed

doc/source/whatsnew/v3.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ Categorical
301301
Datetimelike
302302
^^^^^^^^^^^^
303303
- Bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`)
304+
- Bug in :func:`date_range` where using a negative frequency value would not include all points between the start and end values (:issue:`56382`)
304305
-
305306

306307
Timedelta

pandas/core/arrays/datetimes.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -2777,7 +2777,12 @@ def _generate_range(
27772777
if start and not offset.is_on_offset(start):
27782778
# Incompatible types in assignment (expression has type "datetime",
27792779
# variable has type "Optional[Timestamp]")
2780-
start = offset.rollforward(start) # type: ignore[assignment]
2780+
2781+
# GH #56147 account for negative direction and range bounds
2782+
if offset.n >= 0:
2783+
start = offset.rollforward(start) # type: ignore[assignment]
2784+
else:
2785+
start = offset.rollback(start) # type: ignore[assignment]
27812786

27822787
# Unsupported operand types for < ("Timestamp" and "None")
27832788
if periods is None and end < start and offset.n >= 0: # type: ignore[operator]

pandas/core/indexes/datetimes.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -841,13 +841,15 @@ def date_range(
841841
Return a fixed frequency DatetimeIndex.
842842
843843
Returns the range of equally spaced time points (where the difference between any
844-
two adjacent points is specified by the given frequency) such that they all
845-
satisfy `start <[=] x <[=] end`, where the first one and the last one are, resp.,
846-
the first and last time points in that range that fall on the boundary of ``freq``
847-
(if given as a frequency string) or that are valid for ``freq`` (if given as a
848-
:class:`pandas.tseries.offsets.DateOffset`). (If exactly one of ``start``,
849-
``end``, or ``freq`` is *not* specified, this missing parameter can be computed
850-
given ``periods``, the number of timesteps in the range. See the note below.)
844+
two adjacent points is specified by the given frequency) such that they fall in the
845+
range `[start, end]` , where the first one and the last one are, resp., the first
846+
and last time points in that range that fall on the boundary of ``freq`` (if given
847+
as a frequency string) or that are valid for ``freq`` (if given as a
848+
:class:`pandas.tseries.offsets.DateOffset`). If ``freq`` is positive, the points
849+
satisfy `start <[=] x <[=] end`, and if ``freq`` is negative, the points satisfy
850+
`end <[=] x <[=] start`. (If exactly one of ``start``, ``end``, or ``freq`` is *not*
851+
specified, this missing parameter can be computed given ``periods``, the number of
852+
timesteps in the range. See the note below.)
851853
852854
Parameters
853855
----------

pandas/tests/indexes/datetimes/test_date_range.py

+15
Original file line numberDiff line numberDiff line change
@@ -1735,3 +1735,18 @@ def test_date_range_partial_day_year_end(self, unit):
17351735
freq="YE",
17361736
)
17371737
tm.assert_index_equal(rng, exp)
1738+
1739+
def test_date_range_negative_freq_year_end_inbounds(self, unit):
1740+
# GH#56147
1741+
rng = date_range(
1742+
start="2023-10-31 00:00:00",
1743+
end="2021-10-31 00:00:00",
1744+
freq="-1YE",
1745+
unit=unit,
1746+
)
1747+
exp = DatetimeIndex(
1748+
["2022-12-31 00:00:00", "2021-12-31 00:00:00"],
1749+
dtype=f"M8[{unit}]",
1750+
freq="-1YE",
1751+
)
1752+
tm.assert_index_equal(rng, exp)

0 commit comments

Comments
 (0)