Skip to content

Commit

Permalink
Port negative frequency fix for pandas.date_range to cftime_range (
Browse files Browse the repository at this point in the history
  • Loading branch information
spencerkclark authored May 6, 2024
1 parent c4031cd commit e0f2cee
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 4 deletions.
6 changes: 6 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ Breaking changes

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


Internal Changes
Expand Down
7 changes: 6 additions & 1 deletion xarray/coding/cftime_offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,12 @@ def _generate_range(start, end, periods, offset):
A generator object
"""
if start:
start = offset.rollforward(start)
# From pandas GH 56147 / 56832 to account for negative direction and
# range bounds
if offset.n >= 0:
start = offset.rollforward(start)
else:
start = offset.rollback(start)

if periods is None and end < start and offset.n >= 0:
end = None
Expand Down
1 change: 1 addition & 0 deletions xarray/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def _importorskip(
has_numexpr, requires_numexpr = _importorskip("numexpr")
has_flox, requires_flox = _importorskip("flox")
has_pandas_ge_2_2, __ = _importorskip("pandas", "2.2")
has_pandas_3, requires_pandas_3 = _importorskip("pandas", "3.0.0.dev0")


# some special cases
Expand Down
17 changes: 14 additions & 3 deletions xarray/tests/test_cftime_offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
has_cftime,
has_pandas_ge_2_2,
requires_cftime,
requires_pandas_3,
)

cftime = pytest.importorskip("cftime")
Expand Down Expand Up @@ -1354,7 +1355,7 @@ def test_calendar_specific_month_end_negative_freq(
) -> None:
year = 2000 # Use a leap-year to highlight calendar differences
result = cftime_range(
start="2000-12",
start="2001",
end="2000",
freq="-2ME",
calendar=calendar,
Expand Down Expand Up @@ -1464,7 +1465,7 @@ def test_date_range_errors() -> None:
("2020-02-01", "QE-DEC", "noleap", "gregorian", True, "2020-03-31", True),
("2020-02-01", "YS-FEB", "noleap", "gregorian", True, "2020-02-01", True),
("2020-02-01", "YE-FEB", "noleap", "gregorian", True, "2020-02-29", True),
("2020-02-01", "-1YE-FEB", "noleap", "gregorian", True, "2020-02-29", True),
("2020-02-01", "-1YE-FEB", "noleap", "gregorian", True, "2019-02-28", True),
("2020-02-28", "3h", "all_leap", "gregorian", False, "2020-02-28", True),
("2020-03-30", "ME", "360_day", "gregorian", False, "2020-03-31", True),
("2020-03-31", "ME", "gregorian", "360_day", None, "2020-03-30", False),
Expand Down Expand Up @@ -1724,7 +1725,17 @@ def test_new_to_legacy_freq_pd_freq_passthrough(freq, expected):
@pytest.mark.parametrize("start", ("2000", "2001"))
@pytest.mark.parametrize("end", ("2000", "2001"))
@pytest.mark.parametrize(
"freq", ("MS", "-1MS", "YS", "-1YS", "ME", "-1ME", "YE", "-1YE")
"freq",
(
"MS",
pytest.param("-1MS", marks=requires_pandas_3),
"YS",
pytest.param("-1YS", marks=requires_pandas_3),
"ME",
pytest.param("-1ME", marks=requires_pandas_3),
"YE",
pytest.param("-1YE", marks=requires_pandas_3),
),
)
def test_cftime_range_same_as_pandas(start, end, freq):
result = date_range(start, end, freq=freq, calendar="standard", use_cftime=True)
Expand Down

0 comments on commit e0f2cee

Please sign in to comment.