Skip to content

Commit

Permalink
BUG: Negative freq in date_range produces values out of start and end…
Browse files Browse the repository at this point in the history
…points (#56832)

* #56147 negative offset and year end interaction

* #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]>
  • Loading branch information
3 people authored Mar 20, 2024
1 parent 303d78b commit 1b84930
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 8 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ Categorical
Datetimelike
^^^^^^^^^^^^
- Bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`)
- Bug in :func:`date_range` where using a negative frequency value would not include all points between the start and end values (:issue:`56382`)
-

Timedelta
Expand Down
7 changes: 6 additions & 1 deletion pandas/core/arrays/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2777,7 +2777,12 @@ def _generate_range(
if start and not offset.is_on_offset(start):
# Incompatible types in assignment (expression has type "datetime",
# variable has type "Optional[Timestamp]")
start = offset.rollforward(start) # type: ignore[assignment]

# GH #56147 account for negative direction and range bounds
if offset.n >= 0:
start = offset.rollforward(start) # type: ignore[assignment]
else:
start = offset.rollback(start) # type: ignore[assignment]

# Unsupported operand types for < ("Timestamp" and "None")
if periods is None and end < start and offset.n >= 0: # type: ignore[operator]
Expand Down
16 changes: 9 additions & 7 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,13 +841,15 @@ def date_range(
Return a fixed frequency DatetimeIndex.
Returns the range of equally spaced time points (where the difference between any
two adjacent points is specified by the given frequency) such that they all
satisfy `start <[=] x <[=] end`, where the first one and the last one are, resp.,
the first and last time points in that range that fall on the boundary of ``freq``
(if given as a frequency string) or that are valid for ``freq`` (if given as a
:class:`pandas.tseries.offsets.DateOffset`). (If exactly one of ``start``,
``end``, or ``freq`` is *not* specified, this missing parameter can be computed
given ``periods``, the number of timesteps in the range. See the note below.)
two adjacent points is specified by the given frequency) such that they fall in the
range `[start, end]` , where the first one and the last one are, resp., the first
and last time points in that range that fall on the boundary of ``freq`` (if given
as a frequency string) or that are valid for ``freq`` (if given as a
:class:`pandas.tseries.offsets.DateOffset`). If ``freq`` is positive, the points
satisfy `start <[=] x <[=] end`, and if ``freq`` is negative, the points satisfy
`end <[=] x <[=] start`. (If exactly one of ``start``, ``end``, or ``freq`` is *not*
specified, this missing parameter can be computed given ``periods``, the number of
timesteps in the range. See the note below.)
Parameters
----------
Expand Down
15 changes: 15 additions & 0 deletions pandas/tests/indexes/datetimes/test_date_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -1735,3 +1735,18 @@ def test_date_range_partial_day_year_end(self, unit):
freq="YE",
)
tm.assert_index_equal(rng, exp)

def test_date_range_negative_freq_year_end_inbounds(self, unit):
# GH#56147
rng = date_range(
start="2023-10-31 00:00:00",
end="2021-10-31 00:00:00",
freq="-1YE",
unit=unit,
)
exp = DatetimeIndex(
["2022-12-31 00:00:00", "2021-12-31 00:00:00"],
dtype=f"M8[{unit}]",
freq="-1YE",
)
tm.assert_index_equal(rng, exp)

0 comments on commit 1b84930

Please sign in to comment.