Skip to content

Commit 59ec695

Browse files
spencerkclarkandersy005
authored andcommitted
Port negative frequency fix for pandas.date_range to cftime_range (#8999)
1 parent 6d60be9 commit 59ec695

File tree

4 files changed

+27
-4
lines changed

4 files changed

+27
-4
lines changed

doc/whats-new.rst

+6
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ Breaking changes
6060

6161
Bug fixes
6262
~~~~~~~~~
63+
- Following `an upstream bug fix
64+
<https://github.com/pandas-dev/pandas/issues/56147>`_ to
65+
:py:func:`pandas.date_range`, date ranges produced by
66+
:py:func:`xarray.cftime_range` with negative frequencies will now fall fully
67+
within the bounds of the provided start and end dates (:pull:`8999`). By
68+
`Spencer Clark <https://github.com/spencerkclark>`_.
6369

6470

6571
Internal Changes

xarray/coding/cftime_offsets.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,12 @@ def _generate_range(start, end, periods, offset):
845845
A generator object
846846
"""
847847
if start:
848-
start = offset.rollforward(start)
848+
# From pandas GH 56147 / 56832 to account for negative direction and
849+
# range bounds
850+
if offset.n >= 0:
851+
start = offset.rollforward(start)
852+
else:
853+
start = offset.rollback(start)
849854

850855
if periods is None and end < start and offset.n >= 0:
851856
end = None

xarray/tests/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def _importorskip(
130130
has_numexpr, requires_numexpr = _importorskip("numexpr")
131131
has_flox, requires_flox = _importorskip("flox")
132132
has_pandas_ge_2_2, __ = _importorskip("pandas", "2.2")
133+
has_pandas_3, requires_pandas_3 = _importorskip("pandas", "3.0.0.dev0")
133134

134135

135136
# some special cases

xarray/tests/test_cftime_offsets.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
has_cftime,
4343
has_pandas_ge_2_2,
4444
requires_cftime,
45+
requires_pandas_3,
4546
)
4647

4748
cftime = pytest.importorskip("cftime")
@@ -1354,7 +1355,7 @@ def test_calendar_specific_month_end_negative_freq(
13541355
) -> None:
13551356
year = 2000 # Use a leap-year to highlight calendar differences
13561357
result = cftime_range(
1357-
start="2000-12",
1358+
start="2001",
13581359
end="2000",
13591360
freq="-2ME",
13601361
calendar=calendar,
@@ -1464,7 +1465,7 @@ def test_date_range_errors() -> None:
14641465
("2020-02-01", "QE-DEC", "noleap", "gregorian", True, "2020-03-31", True),
14651466
("2020-02-01", "YS-FEB", "noleap", "gregorian", True, "2020-02-01", True),
14661467
("2020-02-01", "YE-FEB", "noleap", "gregorian", True, "2020-02-29", True),
1467-
("2020-02-01", "-1YE-FEB", "noleap", "gregorian", True, "2020-02-29", True),
1468+
("2020-02-01", "-1YE-FEB", "noleap", "gregorian", True, "2019-02-28", True),
14681469
("2020-02-28", "3h", "all_leap", "gregorian", False, "2020-02-28", True),
14691470
("2020-03-30", "ME", "360_day", "gregorian", False, "2020-03-31", True),
14701471
("2020-03-31", "ME", "gregorian", "360_day", None, "2020-03-30", False),
@@ -1724,7 +1725,17 @@ def test_new_to_legacy_freq_pd_freq_passthrough(freq, expected):
17241725
@pytest.mark.parametrize("start", ("2000", "2001"))
17251726
@pytest.mark.parametrize("end", ("2000", "2001"))
17261727
@pytest.mark.parametrize(
1727-
"freq", ("MS", "-1MS", "YS", "-1YS", "ME", "-1ME", "YE", "-1YE")
1728+
"freq",
1729+
(
1730+
"MS",
1731+
pytest.param("-1MS", marks=requires_pandas_3),
1732+
"YS",
1733+
pytest.param("-1YS", marks=requires_pandas_3),
1734+
"ME",
1735+
pytest.param("-1ME", marks=requires_pandas_3),
1736+
"YE",
1737+
pytest.param("-1YE", marks=requires_pandas_3),
1738+
),
17281739
)
17291740
def test_cftime_range_same_as_pandas(start, end, freq):
17301741
result = date_range(start, end, freq=freq, calendar="standard", use_cftime=True)

0 commit comments

Comments
 (0)