Skip to content

Commit f984364

Browse files
authored
REF: De-duplicate roll_yearday/roll_qtrday (#34760)
1 parent a97bddd commit f984364

File tree

2 files changed

+13
-91
lines changed

2 files changed

+13
-91
lines changed

pandas/_libs/tslibs/offsets.pyx

+7-85
Original file line numberDiff line numberDiff line change
@@ -1879,7 +1879,7 @@ cdef class YearOffset(SingleConstructorOffset):
18791879

18801880
@apply_wraps
18811881
def apply(self, other):
1882-
years = roll_yearday(other, self.n, self.month, self._day_opt)
1882+
years = roll_qtrday(other, self.n, self.month, self._day_opt, modby=12)
18831883
months = years * 12 + (self.month - other.month)
18841884
return shift_month(other, months, self._day_opt)
18851885

@@ -4158,10 +4158,13 @@ def roll_qtrday(other: datetime, n: int, month: int,
41584158
npy_datetimestruct dts
41594159
pydate_to_dtstruct(other, &dts)
41604160

4161-
# TODO: Merge this with roll_yearday by setting modby=12 there?
4162-
# code de-duplication versus perf hit?
41634161
# TODO: with small adjustments this could be used in shift_quarters
4164-
months_since = other.month % modby - month % modby
4162+
4163+
if modby == 12:
4164+
# We care about the month-of-year, not month-of-quarter, so skip mod
4165+
months_since = other.month - month
4166+
else:
4167+
months_since = other.month % modby - month % modby
41654168

41664169
if n > 0:
41674170
if months_since < 0 or (months_since == 0 and
@@ -4177,84 +4180,3 @@ def roll_qtrday(other: datetime, n: int, month: int,
41774180
# make sure to roll forward, so negate
41784181
n += 1
41794182
return n
4180-
4181-
4182-
def roll_yearday(other: datetime, n: int, month: int, day_opt: object) -> int:
4183-
"""
4184-
Possibly increment or decrement the number of periods to shift
4185-
based on rollforward/rollbackward conventions.
4186-
4187-
Parameters
4188-
----------
4189-
other : datetime or Timestamp
4190-
n : number of periods to increment, before adjusting for rolling
4191-
month : reference month giving the first month of the year
4192-
day_opt : 'start', 'end', 'business_start', 'business_end', or int
4193-
The day of the month to compare against that of `other` when
4194-
incrementing or decrementing the number of periods:
4195-
4196-
'start': 1
4197-
'end': last day of the month
4198-
'business_start': first business day of the month
4199-
'business_end': last business day of the month
4200-
int: day in the month indicated by `other`, or the last of day
4201-
the month if the value exceeds in that month's number of days.
4202-
4203-
Returns
4204-
-------
4205-
n : int number of periods to increment
4206-
4207-
Notes
4208-
-----
4209-
* Mirrors `roll_check` in shift_months
4210-
4211-
Examples
4212-
-------
4213-
>>> month = 3
4214-
>>> day_opt = 'start' # `other` will be compared to March 1
4215-
>>> other = datetime(2017, 2, 10) # before March 1
4216-
>>> roll_yearday(other, 2, month, day_opt)
4217-
1
4218-
>>> roll_yearday(other, -7, month, day_opt)
4219-
-7
4220-
>>>
4221-
>>> other = Timestamp('2014-03-15', tz='US/Eastern') # after March 1
4222-
>>> roll_yearday(other, 2, month, day_opt)
4223-
2
4224-
>>> roll_yearday(other, -7, month, day_opt)
4225-
-6
4226-
4227-
>>> month = 6
4228-
>>> day_opt = 'end' # `other` will be compared to June 30
4229-
>>> other = datetime(1999, 6, 29) # before June 30
4230-
>>> roll_yearday(other, 5, month, day_opt)
4231-
4
4232-
>>> roll_yearday(other, -7, month, day_opt)
4233-
-7
4234-
>>>
4235-
>>> other = Timestamp(2072, 8, 24, 6, 17, 18) # after June 30
4236-
>>> roll_yearday(other, 5, month, day_opt)
4237-
5
4238-
>>> roll_yearday(other, -7, month, day_opt)
4239-
-6
4240-
4241-
"""
4242-
cdef:
4243-
npy_datetimestruct dts
4244-
pydate_to_dtstruct(other, &dts)
4245-
4246-
# Note: The other.day < ... condition will never hold when day_opt=='start'
4247-
# and the other.day > ... condition will never hold when day_opt=='end'.
4248-
# At some point these extra checks may need to be optimized away.
4249-
# But that point isn't today.
4250-
if n > 0:
4251-
if other.month < month or (other.month == month and
4252-
other.day < get_day_of_month(&dts,
4253-
day_opt)):
4254-
n -= 1
4255-
else:
4256-
if other.month > month or (other.month == month and
4257-
other.day > get_day_of_month(&dts,
4258-
day_opt)):
4259-
n += 1
4260-
return n

pandas/tests/tslibs/test_liboffsets.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ def test_shift_month_error():
8888
],
8989
)
9090
@pytest.mark.parametrize("n", [2, -7, 0])
91-
def test_roll_yearday(other, expected, n):
91+
def test_roll_qtrday_year(other, expected, n):
9292
month = 3
9393
day_opt = "start" # `other` will be compared to March 1.
9494

95-
assert liboffsets.roll_yearday(other, n, month, day_opt) == expected[n]
95+
assert roll_qtrday(other, n, month, day_opt, modby=12) == expected[n]
9696

9797

9898
@pytest.mark.parametrize(
@@ -105,22 +105,22 @@ def test_roll_yearday(other, expected, n):
105105
],
106106
)
107107
@pytest.mark.parametrize("n", [5, -7, 0])
108-
def test_roll_yearday2(other, expected, n):
108+
def test_roll_qtrday_year2(other, expected, n):
109109
month = 6
110110
day_opt = "end" # `other` will be compared to June 30.
111111

112-
assert liboffsets.roll_yearday(other, n, month, day_opt) == expected[n]
112+
assert roll_qtrday(other, n, month, day_opt, modby=12) == expected[n]
113113

114114

115115
def test_get_day_of_month_error():
116116
# get_day_of_month is not directly exposed.
117-
# We test it via roll_yearday.
117+
# We test it via roll_qtrday.
118118
dt = datetime(2017, 11, 15)
119119
day_opt = "foo"
120120

121121
with pytest.raises(ValueError, match=day_opt):
122122
# To hit the raising case we need month == dt.month and n > 0.
123-
liboffsets.roll_yearday(dt, n=3, month=11, day_opt=day_opt)
123+
roll_qtrday(dt, n=3, month=11, day_opt=day_opt, modby=12)
124124

125125

126126
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)