@@ -1582,25 +1582,7 @@ cdef class BusinessDay(BusinessMixin):
1582
1582
1583
1583
# avoid slowness below by operating on weeks first
1584
1584
weeks = n // 5
1585
- if n <= 0 and wday > 4 :
1586
- # roll forward
1587
- n += 1
1588
-
1589
- n -= 5 * weeks
1590
-
1591
- # n is always >= 0 at this point
1592
- if n == 0 and wday > 4 :
1593
- # roll back
1594
- days = 4 - wday
1595
- elif wday > 4 :
1596
- # roll forward
1597
- days = (7 - wday) + (n - 1 )
1598
- elif wday + n <= 4 :
1599
- # shift by n days without leaving the current week
1600
- days = n
1601
- else :
1602
- # shift by n days plus 2 to get past the weekend
1603
- days = n + 2
1585
+ days = self ._adjust_ndays(wday, weeks)
1604
1586
1605
1587
result = other + timedelta(days = 7 * weeks + days)
1606
1588
if self .offset:
@@ -1617,11 +1599,90 @@ cdef class BusinessDay(BusinessMixin):
1617
1599
" Only know how to combine business day with datetime or timedelta."
1618
1600
)
1619
1601
1602
+ @ cython.wraparound (False )
1603
+ @ cython.boundscheck (False )
1604
+ cdef ndarray _shift_bdays(
1605
+ self ,
1606
+ ndarray i8other,
1607
+ NPY_DATETIMEUNIT reso = NPY_DATETIMEUNIT.NPY_FR_ns,
1608
+ ):
1609
+ """
1610
+ Implementation of BusinessDay.apply_offset.
1611
+
1612
+ Parameters
1613
+ ----------
1614
+ i8other : const int64_t[:]
1615
+ reso : NPY_DATETIMEUNIT, default NPY_FR_ns
1616
+
1617
+ Returns
1618
+ -------
1619
+ ndarray[int64_t]
1620
+ """
1621
+ cdef:
1622
+ int periods = self .n
1623
+ Py_ssize_t i, n = i8other.size
1624
+ ndarray result = cnp.PyArray_EMPTY(
1625
+ i8other.ndim, i8other.shape, cnp.NPY_INT64, 0
1626
+ )
1627
+ int64_t val, res_val
1628
+ int wday, days
1629
+ npy_datetimestruct dts
1630
+ int64_t DAY_PERIODS = periods_per_day(reso)
1631
+ cnp.broadcast mi = cnp.PyArray_MultiIterNew2(result, i8other)
1632
+
1633
+ for i in range (n):
1634
+ # Analogous to: val = i8other[i]
1635
+ val = (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 1 ))[0 ]
1636
+
1637
+ if val == NPY_NAT:
1638
+ res_val = NPY_NAT
1639
+ else :
1640
+ # The rest of this is effectively a copy of BusinessDay.apply
1641
+ weeks = periods // 5
1642
+ pandas_datetime_to_datetimestruct(val, reso, & dts)
1643
+ wday = dayofweek(dts.year, dts.month, dts.day)
1644
+
1645
+ days = self ._adjust_ndays(wday, weeks)
1646
+ res_val = val + (7 * weeks + days) * DAY_PERIODS
1647
+
1648
+ # Analogous to: out[i] = res_val
1649
+ (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 0 ))[0 ] = res_val
1650
+
1651
+ cnp.PyArray_MultiIter_NEXT(mi)
1652
+
1653
+ return result
1654
+
1655
+ cdef int _adjust_ndays(self , int wday, int weeks):
1656
+ cdef:
1657
+ int n = self .n
1658
+ int days
1659
+
1660
+ if n <= 0 and wday > 4 :
1661
+ # roll forward
1662
+ n += 1
1663
+
1664
+ n -= 5 * weeks
1665
+
1666
+ # n is always >= 0 at this point
1667
+ if n == 0 and wday > 4 :
1668
+ # roll back
1669
+ days = 4 - wday
1670
+ elif wday > 4 :
1671
+ # roll forward
1672
+ days = (7 - wday) + (n - 1 )
1673
+ elif wday + n <= 4 :
1674
+ # shift by n days without leaving the current week
1675
+ days = n
1676
+ else :
1677
+ # shift by n days plus 2 to get past the weekend
1678
+ days = n + 2
1679
+ return days
1680
+
1620
1681
@apply_array_wraps
1621
1682
def _apply_array (self , dtarr ):
1622
1683
i8other = dtarr.view(" i8" )
1623
1684
reso = get_unit_from_dtype(dtarr.dtype)
1624
- res = _shift_bdays(i8other, self .n , reso = reso)
1685
+ res = self . _shift_bdays(i8other, reso = reso)
1625
1686
if self .offset:
1626
1687
res = res.view(dtarr.dtype) + Timedelta(self .offset)
1627
1688
res = res.view(" i8" )
@@ -4328,80 +4389,6 @@ def shift_months(
4328
4389
return out
4329
4390
4330
4391
4331
- @ cython.wraparound (False )
4332
- @ cython.boundscheck (False )
4333
- cdef ndarray _shift_bdays(
4334
- ndarray i8other,
4335
- int periods,
4336
- NPY_DATETIMEUNIT reso = NPY_DATETIMEUNIT.NPY_FR_ns,
4337
- ):
4338
- """
4339
- Implementation of BusinessDay.apply_offset.
4340
-
4341
- Parameters
4342
- ----------
4343
- i8other : const int64_t[:]
4344
- periods : int
4345
- reso : NPY_DATETIMEUNIT, default NPY_FR_ns
4346
-
4347
- Returns
4348
- -------
4349
- ndarray[int64_t]
4350
- """
4351
- cdef:
4352
- Py_ssize_t i, n = i8other.size
4353
- ndarray result = cnp.PyArray_EMPTY(
4354
- i8other.ndim, i8other.shape, cnp.NPY_INT64, 0
4355
- )
4356
- int64_t val, res_val
4357
- int wday, nadj, days
4358
- npy_datetimestruct dts
4359
- int64_t DAY_PERIODS = periods_per_day(reso)
4360
- cnp.broadcast mi = cnp.PyArray_MultiIterNew2(result, i8other)
4361
-
4362
- for i in range (n):
4363
- # Analogous to: val = i8other[i]
4364
- val = (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 1 ))[0 ]
4365
-
4366
- if val == NPY_NAT:
4367
- res_val = NPY_NAT
4368
- else :
4369
- # The rest of this is effectively a copy of BusinessDay.apply
4370
- nadj = periods
4371
- weeks = nadj // 5
4372
- pandas_datetime_to_datetimestruct(val, reso, & dts)
4373
- wday = dayofweek(dts.year, dts.month, dts.day)
4374
-
4375
- if nadj <= 0 and wday > 4 :
4376
- # roll forward
4377
- nadj += 1
4378
-
4379
- nadj -= 5 * weeks
4380
-
4381
- # nadj is always >= 0 at this point
4382
- if nadj == 0 and wday > 4 :
4383
- # roll back
4384
- days = 4 - wday
4385
- elif wday > 4 :
4386
- # roll forward
4387
- days = (7 - wday) + (nadj - 1 )
4388
- elif wday + nadj <= 4 :
4389
- # shift by n days without leaving the current week
4390
- days = nadj
4391
- else :
4392
- # shift by nadj days plus 2 to get past the weekend
4393
- days = nadj + 2
4394
-
4395
- res_val = val + (7 * weeks + days) * DAY_PERIODS
4396
-
4397
- # Analogous to: out[i] = res_val
4398
- (< int64_t* > cnp.PyArray_MultiIter_DATA(mi, 0 ))[0 ] = res_val
4399
-
4400
- cnp.PyArray_MultiIter_NEXT(mi)
4401
-
4402
- return result
4403
-
4404
-
4405
4392
def shift_month (stamp: datetime , months: int , day_opt: object = None ) -> datetime:
4406
4393
"""
4407
4394
Given a datetime (or Timestamp ) `stamp`, an integer `months` and an
0 commit comments