Skip to content

REF: implement shift_bday #34761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 14, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 59 additions & 19 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ from dateutil.easter import easter

import numpy as np
cimport numpy as cnp
from numpy cimport int64_t
from numpy cimport int64_t, ndarray
cnp.import_array()

# TODO: formalize having _libs.properties "above" tslibs in the dependency structure
Expand Down Expand Up @@ -1380,24 +1380,7 @@ cdef class BusinessDay(BusinessMixin):
@apply_index_wraps
def apply_index(self, dtindex):
i8other = dtindex.asi8
time = (i8other % DAY_NANOS).view("timedelta64[ns]")

# to_period rolls forward to next BDay; track and
# reduce n where it does when rolling forward
asper = dtindex.to_period("B")

if self.n > 0:
shifted = (dtindex.to_perioddelta("B") - time).asi8 != 0

roll = np.where(shifted, self.n - 1, self.n)
shifted = asper._addsub_int_array(roll, operator.add)
else:
# Integer addition is deprecated, so we use _time_shift directly
roll = self.n
shifted = asper._time_shift(roll)

result = shifted.to_timestamp() + time
return result
return shift_bdays(i8other, self.n)

def is_on_offset(self, dt) -> bool:
if self.normalize and not _is_normalized(dt):
Expand Down Expand Up @@ -3990,6 +3973,63 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
return np.asarray(out)


cdef ndarray[int64_t] shift_bdays(const int64_t[:] i8other, int periods):
"""
Implementation of BusinessDay.apply_offset.

Parameters
----------
i8other : const int64_t[:]
periods : int

Returns
-------
ndarray[int64_t]
"""
cdef:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you doc-string args

Py_ssize_t i, n = len(i8other)
int64_t[:] result = np.empty(n, dtype="i8")
int64_t val, res
int wday, nadj, days
npy_datetimestruct dts

for i in range(n):
val = i8other[i]
if val == NPY_NAT:
result[i] = NPY_NAT
else:
# The rest of this is effectively a copy of BusinessDay.apply
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO here to consolidate?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really feasible; different types.

nadj = periods
weeks = nadj // 5
dt64_to_dtstruct(val, &dts)
wday = dayofweek(dts.year, dts.month, dts.day)

if nadj <= 0 and wday > 4:
# roll forward
nadj += 1

nadj -= 5 * weeks

# nadj is always >= 0 at this point
if nadj == 0 and wday > 4:
# roll back
days = 4 - wday
elif wday > 4:
# roll forward
days = (7 - wday) + (nadj - 1)
elif wday + nadj <= 4:
# shift by n days without leaving the current week
days = nadj
else:
# shift by nadj days plus 2 to get past the weekend
days = nadj + 2

res = val + (7 * weeks + days) * DAY_NANOS
result[i] = res

return result.base


def shift_month(stamp: datetime, months: int,
day_opt: object=None) -> datetime:
"""
Expand Down