@@ -18,7 +18,7 @@ from dateutil.easter import easter
18
18
19
19
import numpy as np
20
20
cimport numpy as cnp
21
- from numpy cimport int64_t
21
+ from numpy cimport int64_t, ndarray
22
22
cnp.import_array()
23
23
24
24
# TODO: formalize having _libs.properties "above" tslibs in the dependency structure
@@ -1380,24 +1380,7 @@ cdef class BusinessDay(BusinessMixin):
1380
1380
@apply_index_wraps
1381
1381
def apply_index (self , dtindex ):
1382
1382
i8other = dtindex.asi8
1383
- time = (i8other % DAY_NANOS).view(" timedelta64[ns]" )
1384
-
1385
- # to_period rolls forward to next BDay; track and
1386
- # reduce n where it does when rolling forward
1387
- asper = dtindex.to_period(" B" )
1388
-
1389
- if self .n > 0 :
1390
- shifted = (dtindex.to_perioddelta(" B" ) - time).asi8 != 0
1391
-
1392
- roll = np.where(shifted, self .n - 1 , self .n)
1393
- shifted = asper._addsub_int_array(roll, operator.add)
1394
- else :
1395
- # Integer addition is deprecated, so we use _time_shift directly
1396
- roll = self .n
1397
- shifted = asper._time_shift(roll)
1398
-
1399
- result = shifted.to_timestamp() + time
1400
- return result
1383
+ return shift_bdays(i8other, self .n)
1401
1384
1402
1385
def is_on_offset (self , dt ) -> bool:
1403
1386
if self.normalize and not _is_normalized(dt ):
@@ -3995,6 +3978,63 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
3995
3978
return np.asarray(out)
3996
3979
3997
3980
3981
+ cdef ndarray[int64_t] shift_bdays(const int64_t[:] i8other, int periods):
3982
+ """
3983
+ Implementation of BusinessDay.apply_offset.
3984
+
3985
+ Parameters
3986
+ ----------
3987
+ i8other : const int64_t[:]
3988
+ periods : int
3989
+
3990
+ Returns
3991
+ -------
3992
+ ndarray[int64_t]
3993
+ """
3994
+ cdef:
3995
+ Py_ssize_t i, n = len (i8other)
3996
+ int64_t[:] result = np.empty(n, dtype = " i8" )
3997
+ int64_t val, res
3998
+ int wday, nadj, days
3999
+ npy_datetimestruct dts
4000
+
4001
+ for i in range (n):
4002
+ val = i8other[i]
4003
+ if val == NPY_NAT:
4004
+ result[i] = NPY_NAT
4005
+ else :
4006
+ # The rest of this is effectively a copy of BusinessDay.apply
4007
+ nadj = periods
4008
+ weeks = nadj // 5
4009
+ dt64_to_dtstruct(val, & dts)
4010
+ wday = dayofweek(dts.year, dts.month, dts.day)
4011
+
4012
+ if nadj <= 0 and wday > 4 :
4013
+ # roll forward
4014
+ nadj += 1
4015
+
4016
+ nadj -= 5 * weeks
4017
+
4018
+ # nadj is always >= 0 at this point
4019
+ if nadj == 0 and wday > 4 :
4020
+ # roll back
4021
+ days = 4 - wday
4022
+ elif wday > 4 :
4023
+ # roll forward
4024
+ days = (7 - wday) + (nadj - 1 )
4025
+ elif wday + nadj <= 4 :
4026
+ # shift by n days without leaving the current week
4027
+ days = nadj
4028
+ else :
4029
+ # shift by nadj days plus 2 to get past the weekend
4030
+ days = nadj + 2
4031
+
4032
+ res = val + (7 * weeks + days) * DAY_NANOS
4033
+ result[i] = res
4034
+
4035
+ return result.base
4036
+
4037
+
3998
4038
def shift_month (stamp: datetime , months: int ,
3999
4039
day_opt: object = None ) -> datetime:
4000
4040
"""
0 commit comments