Skip to content

Commit

Permalink
BUG: DatetimeIndex.is_year_start and `DatetimeIndex.is_quarter_star…
Browse files Browse the repository at this point in the history
…t` always return False on double-digit frequencies (#58549)

* correct def get_start_end_field, add test, add a note to v3.0.0

* replace regex with to_offset

* replace offset.freqstr.replace with offset.name

* move to_offset from get_start_end_field up

* fixup test_is_yqm_start_end

* rename the argument freqstr in get_start_end_field

* rename the variable freqstr in _field_accessor

* simplify to_offset(freq.freqstr).name

* rename the variable freqstr
  • Loading branch information
natmokval committed May 10, 2024
1 parent 7be2e21 commit 1c23c5e
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 12 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Expand Up @@ -421,6 +421,7 @@ Interval
Indexing
^^^^^^^^
- Bug in :meth:`DataFrame.__getitem__` returning modified columns when called with ``slice`` in Python 3.12 (:issue:`57500`)
- Bug in :meth:`DatetimeIndex.is_year_start` and :meth:`DatetimeIndex.is_quarter_start` returning ``False`` on double-digit frequencies (:issue:`58523`)
-

Missing
Expand Down
2 changes: 1 addition & 1 deletion pandas/_libs/tslibs/fields.pyi
Expand Up @@ -16,7 +16,7 @@ def get_date_name_field(
def get_start_end_field(
dtindex: npt.NDArray[np.int64],
field: str,
freqstr: str | None = ...,
freq_name: str | None = ...,
month_kw: int = ...,
reso: int = ..., # NPY_DATETIMEUNIT
) -> npt.NDArray[np.bool_]: ...
Expand Down
13 changes: 6 additions & 7 deletions pandas/_libs/tslibs/fields.pyx
Expand Up @@ -210,7 +210,7 @@ cdef bint _is_on_month(int month, int compare_month, int modby) noexcept nogil:
def get_start_end_field(
const int64_t[:] dtindex,
str field,
str freqstr=None,
str freq_name=None,
int month_kw=12,
NPY_DATETIMEUNIT reso=NPY_FR_ns,
):
Expand All @@ -223,7 +223,7 @@ def get_start_end_field(
----------
dtindex : ndarray[int64]
field : str
frestr : str or None, default None
freq_name : str or None, default None
month_kw : int, default 12
reso : NPY_DATETIMEUNIT, default NPY_FR_ns
Expand All @@ -243,18 +243,17 @@ def get_start_end_field(

out = np.zeros(count, dtype="int8")

if freqstr:
if freqstr == "C":
if freq_name:
if freq_name == "C":
raise ValueError(f"Custom business days is not supported by {field}")
is_business = freqstr[0] == "B"
is_business = freq_name[0] == "B"

# YearBegin(), BYearBegin() use month = starting month of year.
# QuarterBegin(), BQuarterBegin() use startingMonth = starting
# month of year. Other offsets use month, startingMonth as ending
# month of year.

if (freqstr[0:2] in ["MS", "QS", "YS"]) or (
freqstr[1:3] in ["MS", "QS", "YS"]):
if freq_name.lstrip("B")[0:2] in ["MS", "QS", "YS"]:
end_month = 12 if month_kw == 1 else month_kw - 1
start_month = month_kw
else:
Expand Down
6 changes: 3 additions & 3 deletions pandas/_libs/tslibs/timestamps.pyx
Expand Up @@ -579,15 +579,15 @@ cdef class _Timestamp(ABCTimestamp):
if freq:
kwds = freq.kwds
month_kw = kwds.get("startingMonth", kwds.get("month", 12))
freqstr = freq.freqstr
freq_name = freq.name
else:
month_kw = 12
freqstr = None
freq_name = None

val = self._maybe_convert_value_to_local()

out = get_start_end_field(np.array([val], dtype=np.int64),
field, freqstr, month_kw, self._creso)
field, freq_name, month_kw, self._creso)
return out[0]

@property
Expand Down
6 changes: 5 additions & 1 deletion pandas/core/arrays/datetimes.py
Expand Up @@ -145,8 +145,12 @@ def f(self):
kwds = freq.kwds
month_kw = kwds.get("startingMonth", kwds.get("month", 12))

if freq is not None:
freq_name = freq.name
else:
freq_name = None
result = fields.get_start_end_field(
values, field, self.freqstr, month_kw, reso=self._creso
values, field, freq_name, month_kw, reso=self._creso
)
else:
result = fields.get_date_field(values, field, reso=self._creso)
Expand Down
8 changes: 8 additions & 0 deletions pandas/tests/indexes/datetimes/test_scalar_compat.py
Expand Up @@ -328,3 +328,11 @@ def test_dti_is_month_start_custom(self):
msg = "Custom business days is not supported by is_month_start"
with pytest.raises(ValueError, match=msg):
dti.is_month_start

def test_dti_is_year_quarter_start_doubledigit_freq(self):
# GH#58523
dr = date_range("2017-01-01", periods=2, freq="10YS")
assert all(dr.is_year_start)

dr = date_range("2017-01-01", periods=2, freq="10QS")
assert all(dr.is_quarter_start)

0 comments on commit 1c23c5e

Please sign in to comment.