Skip to content

Commit c868423

Browse files
jbrockmendeljreback
authored andcommitted
Follow-Up: Unify apply and onOffset implementations (#18329)
1 parent b00e62c commit c868423

File tree

3 files changed

+118
-63
lines changed

3 files changed

+118
-63
lines changed

pandas/_libs/tslibs/offsets.pyx

+26-3
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ class _BaseOffset(object):
357357
_typ = "dateoffset"
358358
_normalize_cache = True
359359
_cacheable = False
360+
_day_opt = None
360361

361362
def __call__(self, other):
362363
return self.apply(other)
@@ -394,6 +395,11 @@ class _BaseOffset(object):
394395
out = '<%s' % n_str + className + plural + self._repr_attrs() + '>'
395396
return out
396397

398+
def _get_offset_day(self, datetime other):
399+
# subclass must implement `_day_opt`; calling from the base class
400+
# will raise NotImplementedError.
401+
return get_day_of_month(other, self._day_opt)
402+
397403

398404
class BaseOffset(_BaseOffset):
399405
# Here we add __rfoo__ methods that don't play well with cdef classes
@@ -468,7 +474,7 @@ cpdef datetime shift_month(datetime stamp, int months, object day_opt=None):
468474
return stamp.replace(year=year, month=month, day=day)
469475

470476

471-
cdef int get_day_of_month(datetime other, day_opt) except? -1:
477+
cpdef int get_day_of_month(datetime other, day_opt) except? -1:
472478
"""
473479
Find the day in `other`'s month that satisfies a DateOffset's onOffset
474480
policy, as described by the `day_opt` argument.
@@ -493,10 +499,27 @@ cdef int get_day_of_month(datetime other, day_opt) except? -1:
493499
30
494500
495501
"""
502+
cdef:
503+
int wkday, days_in_month
504+
496505
if day_opt == 'start':
497506
return 1
498-
elif day_opt == 'end':
499-
return monthrange(other.year, other.month)[1]
507+
508+
wkday, days_in_month = monthrange(other.year, other.month)
509+
if day_opt == 'end':
510+
return days_in_month
511+
elif day_opt == 'business_start':
512+
# first business day of month
513+
return get_firstbday(wkday, days_in_month)
514+
elif day_opt == 'business_end':
515+
# last business day of month
516+
return get_lastbday(wkday, days_in_month)
517+
elif is_integer_object(day_opt):
518+
day = min(day_opt, days_in_month)
519+
elif day_opt is None:
520+
# Note: unlike `shift_month`, get_day_of_month does not
521+
# allow day_opt = None
522+
raise NotImplementedError
500523
else:
501524
raise ValueError(day_opt)
502525

pandas/tests/tseries/offsets/test_offsets.py

+8
Original file line numberDiff line numberDiff line change
@@ -4680,3 +4680,11 @@ def test_all_offset_classes(self, tup):
46804680
first = Timestamp(test_values[0], tz='US/Eastern') + offset()
46814681
second = Timestamp(test_values[1], tz='US/Eastern')
46824682
assert first == second
4683+
4684+
4685+
def test_get_offset_day_error():
4686+
# subclass of _BaseOffset must override _day_opt attribute, or we should
4687+
# get a NotImplementedError
4688+
4689+
with pytest.raises(NotImplementedError):
4690+
DateOffset()._get_offset_day(datetime.now())

0 commit comments

Comments
 (0)