From fa8b4dd8739adab948d3f19e3b7412a5d9516181 Mon Sep 17 00:00:00 2001 From: jrmylow Date: Thu, 11 Jan 2024 22:13:47 +1100 Subject: [PATCH 01/13] pandas-dev#56147 negative offset and year end interaction --- pandas/core/arrays/datetimes.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index a4d01dd6667f6..77c234ee51dde 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -2773,7 +2773,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) elif end and not offset.is_on_offset(end): # Incompatible types in assignment (expression has type "datetime", From f988306dbb6871090ea69c7cd4da10c73211f82c Mon Sep 17 00:00:00 2001 From: jrmylow Date: Thu, 11 Jan 2024 22:15:23 +1100 Subject: [PATCH 02/13] pandas-dev#56147 tests --- pandas/tests/indexes/datetimes/test_date_range.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index 024f37ee5b710..c3597d0ac6e0a 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -1703,3 +1703,18 @@ def test_date_range_freqstr_matches_offset(self, freqstr, offset): idx2 = date_range(start=sdate, end=edate, freq=offset) assert len(idx1) == len(idx2) assert idx1.freq == idx2.freq + + 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) \ No newline at end of file From 577fd6e5b19182ee5d1acdeace240b401b89c73b Mon Sep 17 00:00:00 2001 From: jrmylow Date: Thu, 11 Jan 2024 22:15:55 +1100 Subject: [PATCH 03/13] documentation --- doc/source/whatsnew/v2.3.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v2.3.0.rst b/doc/source/whatsnew/v2.3.0.rst index 7b53ddb3923f0..b273405dc133d 100644 --- a/doc/source/whatsnew/v2.3.0.rst +++ b/doc/source/whatsnew/v2.3.0.rst @@ -209,6 +209,7 @@ Styler Other ^^^^^ +- Bug in :func:`date_range` where a timestamp out of the valid range would be produced with a negative ``freq`` parameter (:issue:`56147`) .. ***DO NOT USE THIS SECTION*** From 3b16bfd4c719ff9261e8ac950b0146ebc86874e3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:24:23 +0000 Subject: [PATCH 04/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pandas/tests/indexes/datetimes/test_date_range.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index c3597d0ac6e0a..44232bb8f1e35 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -1717,4 +1717,4 @@ def test_date_range_negative_freq_year_end_inbounds(self, unit): dtype=f"M8[{unit}]", freq="-1YE", ) - tm.assert_index_equal(rng, exp) \ No newline at end of file + tm.assert_index_equal(rng, exp) From 976ba58761c13006e05f7ad5f58c3e1858fdd3d5 Mon Sep 17 00:00:00 2001 From: jrmylow Date: Thu, 11 Jan 2024 23:25:40 +1100 Subject: [PATCH 05/13] fixing typing/pylint to mirror other branch --- pandas/core/arrays/datetimes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 77c234ee51dde..0d2980fb1df49 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -2778,7 +2778,7 @@ def _generate_range( if offset.n >= 0: start = offset.rollforward(start) # type: ignore[assignment] else: - start = offset.rollback(start) + start = offset.rollback(start) # type: ignore[assignment] elif end and not offset.is_on_offset(end): # Incompatible types in assignment (expression has type "datetime", From 2dbf13494075a6c83f8d58801ff0c0aca0b6c57e Mon Sep 17 00:00:00 2001 From: jrmylow Date: Thu, 11 Jan 2024 23:26:42 +1100 Subject: [PATCH 06/13] moved note to Datetimelike --- doc/source/whatsnew/v2.3.0.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v2.3.0.rst b/doc/source/whatsnew/v2.3.0.rst index b273405dc133d..b3c60bad957e3 100644 --- a/doc/source/whatsnew/v2.3.0.rst +++ b/doc/source/whatsnew/v2.3.0.rst @@ -119,7 +119,7 @@ Categorical Datetimelike ^^^^^^^^^^^^ -- +- Bug in :func:`date_range` where a timestamp out of the valid range would be produced with a negative ``freq`` parameter (:issue:`56147`) - Timedelta @@ -209,7 +209,6 @@ Styler Other ^^^^^ -- Bug in :func:`date_range` where a timestamp out of the valid range would be produced with a negative ``freq`` parameter (:issue:`56147`) .. ***DO NOT USE THIS SECTION*** From ab8ab7ef0e2011e5f081ed71c28974be0415526b Mon Sep 17 00:00:00 2001 From: jrmylow Date: Tue, 23 Jan 2024 22:47:08 +1100 Subject: [PATCH 07/13] documentation re-merge --- doc/source/whatsnew/v3.0.0.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 54321c8d6894a..fdd757355d757 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -125,7 +125,6 @@ Categorical Datetimelike ^^^^^^^^^^^^ -- Bug in :func:`date_range` where a timestamp out of the valid range would be produced with a negative ``freq`` parameter (:issue:`56147`) - Bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`) - From e329d98eff0d4bb7b634b9149fca22ab914c1d04 Mon Sep 17 00:00:00 2001 From: jrmylow Date: Fri, 1 Mar 2024 21:12:00 +1100 Subject: [PATCH 08/13] whatsnew update --- doc/source/whatsnew/v3.0.0.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index a95f0485abd5f..4d491669cc5e8 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -274,7 +274,8 @@ Categorical Datetimelike ^^^^^^^^^^^^ -- Bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`) +- Fixed bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`) +- Fixed 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 From 690ada919a5d53107075756fd76c4b51b274022c Mon Sep 17 00:00:00 2001 From: jrmylow Date: Fri, 1 Mar 2024 21:38:31 +1100 Subject: [PATCH 09/13] updated date_range docstring --- pandas/core/indexes/datetimes.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index b7d2437cbaa44..46a01e207e84b 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -839,11 +839,10 @@ 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., + 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 exactly one of ``start``, + :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.) From 9241d013aff767307251654f348d4e1115f41e33 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:42:30 +0000 Subject: [PATCH 10/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/source/whatsnew/v3.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 4d491669cc5e8..5d3dc831c908c 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -275,7 +275,7 @@ Categorical Datetimelike ^^^^^^^^^^^^ - Fixed bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`) -- Fixed bug in :func:`date_range` where using a negative frequency value would not include all points between the start and end values (:issue:`56382`) +- Fixed 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 From 7f546fc2ad68e936ead5767ef4a49225d8e7a8a4 Mon Sep 17 00:00:00 2001 From: jrmylow Date: Fri, 1 Mar 2024 21:44:17 +1100 Subject: [PATCH 11/13] reformatted docstring --- pandas/core/indexes/datetimes.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 46a01e207e84b..c1db38305e1f4 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -839,12 +839,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 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.) + 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 ---------- From 5ab17c1c19bedb82640b78dbd3997cf6cdd86111 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:48:49 +0000 Subject: [PATCH 12/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pandas/core/indexes/datetimes.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index c1db38305e1f4..b66e632a894e4 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -839,14 +839,14 @@ 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 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 + 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 From 6c36237985516fdfcacfc4cf433c2d0d1f2d55b2 Mon Sep 17 00:00:00 2001 From: jrmylow <33999325+jrmylow@users.noreply.github.com> Date: Tue, 5 Mar 2024 12:37:51 +1100 Subject: [PATCH 13/13] Update doc/source/whatsnew/v3.0.0.rst Co-authored-by: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> --- doc/source/whatsnew/v3.0.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 5d3dc831c908c..0bfe6f39207d5 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -274,8 +274,8 @@ Categorical Datetimelike ^^^^^^^^^^^^ -- Fixed bug in :func:`date_range` where the last valid timestamp would sometimes not be produced (:issue:`56134`) -- Fixed bug in :func:`date_range` where using a negative frequency value would not include all points between the start and end values (:issue:`56382`) +- 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