Skip to content

Commit 9eec76f

Browse files
committed
implement non-controversial cleanup portions of pandas-dev#18762
1 parent feef904 commit 9eec76f

File tree

2 files changed

+46
-42
lines changed

2 files changed

+46
-42
lines changed

pandas/_libs/tslibs/offsets.pyx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -523,11 +523,9 @@ def shift_quarters(int64_t[:] dtindex, int quarters,
523523
n = quarters
524524

525525
months_since = (dts.month - q1start_month) % modby
526-
compare_month = dts.month - months_since
527-
compare_month = compare_month or 12
528526
# compare_day is only relevant for comparison in the case
529527
# where months_since == 0.
530-
compare_day = get_firstbday(dts.year, compare_month)
528+
compare_day = get_firstbday(dts.year, dts.month)
531529

532530
if n <= 0 and (months_since != 0 or
533531
(months_since == 0 and dts.day > compare_day)):
@@ -556,11 +554,9 @@ def shift_quarters(int64_t[:] dtindex, int quarters,
556554
n = quarters
557555

558556
months_since = (dts.month - q1start_month) % modby
559-
compare_month = dts.month - months_since
560-
compare_month = compare_month or 12
561557
# compare_day is only relevant for comparison in the case
562558
# where months_since == 0.
563-
compare_day = get_lastbday(dts.year, compare_month)
559+
compare_day = get_lastbday(dts.year, dts.month)
564560

565561
if n <= 0 and (months_since != 0 or
566562
(months_since == 0 and dts.day > compare_day)):
@@ -827,7 +823,8 @@ cpdef int get_day_of_month(datetime other, day_opt) except? -1:
827823
raise ValueError(day_opt)
828824

829825

830-
cpdef int roll_yearday(other, n, month, day_opt='start') except? -1:
826+
cpdef int roll_yearday(datetime other, int n, int month,
827+
object day_opt='start') except? -1:
831828
"""
832829
Possibly increment or decrement the number of periods to shift
833830
based on rollforward/rollbackward conventions.
@@ -836,17 +833,20 @@ cpdef int roll_yearday(other, n, month, day_opt='start') except? -1:
836833
----------
837834
other : datetime or Timestamp
838835
n : number of periods to increment, before adjusting for rolling
839-
day_opt : 'start', 'end'
840-
'start': returns 1
841-
'end': returns last day of the month
836+
month : reference month giving the first month of the year
837+
day_opt : 'start', 'end', 'business_start', 'business_end'
838+
'start': compare with 1
839+
'end': compare with last day of the month
840+
'business_start': compare with first business day of the month
841+
'business_end': compare with last business day of the month
842842
843843
Returns
844844
-------
845845
n : int number of periods to increment
846846
847847
Notes
848848
-----
849-
* Mirrors `roll_check` in tslib.shift_months
849+
* Mirrors `roll_check` in shift_months
850850
851851
Examples
852852
-------
@@ -888,7 +888,7 @@ cpdef int roll_yearday(other, n, month, day_opt='start') except? -1:
888888
other.day < get_day_of_month(other,
889889
day_opt)):
890890
n -= 1
891-
elif n <= 0:
891+
else:
892892
if other.month > month or (other.month == month and
893893
other.day > get_day_of_month(other,
894894
day_opt)):

pandas/tseries/offsets.py

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from pandas._libs.tslibs.offsets import (
2323
ApplyTypeError,
2424
as_datetime, _is_normalized,
25-
_get_calendar, _to_dt64, _validate_business_time,
25+
_get_calendar, _to_dt64,
2626
_determine_offset,
2727
apply_index_wraps,
2828
roll_yearday,
@@ -557,28 +557,31 @@ def get_str(td):
557557
def apply(self, other):
558558
if isinstance(other, datetime):
559559
n = self.n
560+
wday = other.weekday()
560561

561-
if n == 0 and other.weekday() > 4:
562-
n = 1
563-
564-
result = other
565-
566-
# avoid slowness below
567-
if abs(n) > 5:
568-
k = n // 5
569-
result = result + timedelta(7 * k)
570-
if n < 0 and result.weekday() > 4:
571-
n += 1
572-
n -= 5 * k
573-
if n == 0 and result.weekday() > 4:
574-
n -= 1
562+
# avoid slowness below by operating on weeks first
563+
weeks = n // 5
564+
if n <= 0 and wday > 4:
565+
# roll forward
566+
n += 1
575567

576-
while n != 0:
577-
k = n // abs(n)
578-
result = result + timedelta(k)
579-
if result.weekday() < 5:
580-
n -= k
568+
n -= 5 * weeks
569+
570+
# n is always >= 0 at this point
571+
if n == 0 and wday > 4:
572+
# roll back
573+
days = 4 - wday
574+
elif wday > 4:
575+
# roll forward
576+
days = (7 - wday) + (n - 1)
577+
elif wday + n <= 4:
578+
# shift by n days without leaving the current week
579+
days = n
580+
else:
581+
# shift by n days plus 2 to get past the weekend
582+
days = n + 2
581583

584+
result = other + timedelta(days=7 * weeks + days)
582585
if self.offset:
583586
result = result + self.offset
584587
return result
@@ -614,8 +617,8 @@ class BusinessHourMixin(BusinessMixin):
614617
def __init__(self, start='09:00', end='17:00', offset=timedelta(0)):
615618
# must be validated here to equality check
616619
kwds = {'offset': offset}
617-
self.start = kwds['start'] = _validate_business_time(start)
618-
self.end = kwds['end'] = _validate_business_time(end)
620+
self.start = kwds['start'] = liboffsets._validate_business_time(start)
621+
self.end = kwds['end'] = liboffsets._validate_business_time(end)
619622
self.kwds.update(kwds)
620623
self._offset = offset
621624

@@ -1092,21 +1095,20 @@ class CustomBusinessMonthBegin(_CustomBusinessMonth):
10921095
@apply_wraps
10931096
def apply(self, other):
10941097
n = self.n
1095-
dt_in = other
10961098

10971099
# First move to month offset
1098-
cur_mbegin = self.m_offset.rollback(dt_in)
1100+
cur_mbegin = self.m_offset.rollback(other)
10991101

11001102
# Find this custom month offset
11011103
cur_cmbegin = self.cbday.rollforward(cur_mbegin)
11021104

11031105
# handle zero case. arbitrarily rollforward
1104-
if n == 0 and dt_in != cur_cmbegin:
1106+
if n == 0 and other != cur_cmbegin:
11051107
n += 1
11061108

1107-
if dt_in > cur_cmbegin and n <= -1:
1109+
if other > cur_cmbegin and n <= -1:
11081110
n += 1
1109-
elif dt_in < cur_cmbegin and n >= 1:
1111+
elif other < cur_cmbegin and n >= 1:
11101112
n -= 1
11111113

11121114
new = cur_mbegin + n * self.m_offset
@@ -1564,7 +1566,8 @@ class QuarterOffset(DateOffset):
15641566
_from_name_startingMonth = None
15651567
_adjust_dst = True
15661568
# TODO: Consider combining QuarterOffset and YearOffset __init__ at some
1567-
# point
1569+
# point. Also apply_index, onOffset, rule_code if
1570+
# startingMonth vs month attr names are resolved
15681571

15691572
def __init__(self, n=1, normalize=False, startingMonth=None):
15701573
self.n = self._validate_n(n)
@@ -1613,8 +1616,8 @@ def apply(self, other):
16131616
def onOffset(self, dt):
16141617
if self.normalize and not _is_normalized(dt):
16151618
return False
1616-
modMonth = (dt.month - self.startingMonth) % 3
1617-
return modMonth == 0 and dt.day == self._get_offset_day(dt)
1619+
mod_month = (dt.month - self.startingMonth) % 3
1620+
return mod_month == 0 and dt.day == self._get_offset_day(dt)
16181621

16191622
@apply_index_wraps
16201623
def apply_index(self, dtindex):
@@ -2158,6 +2161,7 @@ def apply(self, other):
21582161
n -= 1
21592162
elif n < 0 and other > current_easter:
21602163
n += 1
2164+
# TODO: Why does this handle the 0 case the opposite of others?
21612165

21622166
# NOTE: easter returns a datetime.date so we have to convert to type of
21632167
# other

0 commit comments

Comments
 (0)