Skip to content

Commit 26b9477

Browse files
authored
REF: make get_day_of_month nogil (#34764)
1 parent 7240ed4 commit 26b9477

File tree

3 files changed

+38
-18
lines changed

3 files changed

+38
-18
lines changed

pandas/_libs/tslibs/np_datetime.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ cdef void td64_to_tdstruct(int64_t td64, pandas_timedeltastruct* out) nogil
6363

6464
cdef int64_t pydatetime_to_dt64(datetime val, npy_datetimestruct *dts)
6565
cdef int64_t pydate_to_dt64(date val, npy_datetimestruct *dts)
66+
cdef void pydate_to_dtstruct(date val, npy_datetimestruct *dts)
6667

6768
cdef npy_datetime get_datetime64_value(object obj) nogil
6869
cdef npy_timedelta get_timedelta64_value(object obj) nogil

pandas/_libs/tslibs/np_datetime.pyx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,16 @@ cdef inline int64_t pydatetime_to_dt64(datetime val,
152152
return dtstruct_to_dt64(dts)
153153

154154

155-
cdef inline int64_t pydate_to_dt64(date val, npy_datetimestruct *dts):
155+
cdef inline void pydate_to_dtstruct(date val, npy_datetimestruct *dts):
156156
dts.year = PyDateTime_GET_YEAR(val)
157157
dts.month = PyDateTime_GET_MONTH(val)
158158
dts.day = PyDateTime_GET_DAY(val)
159159
dts.hour = dts.min = dts.sec = dts.us = 0
160160
dts.ps = dts.as = 0
161+
return
162+
163+
cdef inline int64_t pydate_to_dt64(date val, npy_datetimestruct *dts):
164+
pydate_to_dtstruct(val, dts)
161165
return dtstruct_to_dt64(dts)
162166

163167

pandas/_libs/tslibs/offsets.pyx

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ from pandas._libs.tslibs.conversion cimport (
4242
)
4343
from pandas._libs.tslibs.nattype cimport NPY_NAT, c_NaT as NaT
4444
from pandas._libs.tslibs.np_datetime cimport (
45-
npy_datetimestruct, dtstruct_to_dt64, dt64_to_dtstruct)
45+
npy_datetimestruct,
46+
dtstruct_to_dt64,
47+
dt64_to_dtstruct,
48+
pydate_to_dtstruct,
49+
)
4650
from pandas._libs.tslibs.timezones cimport utc_pytz as UTC
4751
from pandas._libs.tslibs.tzconversion cimport tz_convert_single
4852

@@ -607,7 +611,10 @@ cdef class BaseOffset:
607611
def _get_offset_day(self, datetime other):
608612
# subclass must implement `_day_opt`; calling from the base class
609613
# will raise NotImplementedError.
610-
return get_day_of_month(other, self._day_opt)
614+
cdef:
615+
npy_datetimestruct dts
616+
pydate_to_dtstruct(other, &dts)
617+
return get_day_of_month(&dts, self._day_opt)
611618

612619
def is_on_offset(self, dt) -> bool:
613620
if self.normalize and not _is_normalized(dt):
@@ -1864,10 +1871,11 @@ cdef class YearOffset(SingleConstructorOffset):
18641871

18651872
def _get_offset_day(self, other) -> int:
18661873
# override BaseOffset method to use self.month instead of other.month
1867-
# TODO: there may be a more performant way to do this
1868-
return get_day_of_month(
1869-
other.replace(month=self.month), self._day_opt
1870-
)
1874+
cdef:
1875+
npy_datetimestruct dts
1876+
pydate_to_dtstruct(other, &dts)
1877+
dts.month = self.month
1878+
return get_day_of_month(&dts, self._day_opt)
18711879

18721880
@apply_wraps
18731881
def apply(self, other):
@@ -4052,14 +4060,14 @@ def shift_month(stamp: datetime, months: int,
40524060
return stamp.replace(year=year, month=month, day=day)
40534061

40544062

4055-
cdef int get_day_of_month(datetime other, day_opt) except? -1:
4063+
cdef int get_day_of_month(npy_datetimestruct* dts, day_opt) nogil except? -1:
40564064
"""
40574065
Find the day in `other`'s month that satisfies a DateOffset's is_on_offset
40584066
policy, as described by the `day_opt` argument.
40594067
40604068
Parameters
40614069
----------
4062-
other : datetime or Timestamp
4070+
dts : npy_datetimestruct*
40634071
day_opt : {'start', 'end', 'business_start', 'business_end'}
40644072
'start': returns 1
40654073
'end': returns last day of the month
@@ -4085,20 +4093,20 @@ cdef int get_day_of_month(datetime other, day_opt) except? -1:
40854093
if day_opt == 'start':
40864094
return 1
40874095
elif day_opt == 'end':
4088-
days_in_month = get_days_in_month(other.year, other.month)
4096+
days_in_month = get_days_in_month(dts.year, dts.month)
40894097
return days_in_month
40904098
elif day_opt == 'business_start':
40914099
# first business day of month
4092-
return get_firstbday(other.year, other.month)
4100+
return get_firstbday(dts.year, dts.month)
40934101
elif day_opt == 'business_end':
40944102
# last business day of month
4095-
return get_lastbday(other.year, other.month)
4103+
return get_lastbday(dts.year, dts.month)
4104+
elif day_opt is not None:
4105+
raise ValueError(day_opt)
40964106
elif day_opt is None:
40974107
# Note: unlike `shift_month`, get_day_of_month does not
40984108
# allow day_opt = None
40994109
raise NotImplementedError
4100-
else:
4101-
raise ValueError(day_opt)
41024110

41034111

41044112
cpdef int roll_convention(int other, int n, int compare) nogil:
@@ -4151,21 +4159,24 @@ def roll_qtrday(other: datetime, n: int, month: int,
41514159
"""
41524160
cdef:
41534161
int months_since
4162+
npy_datetimestruct dts
4163+
pydate_to_dtstruct(other, &dts)
4164+
41544165
# TODO: Merge this with roll_yearday by setting modby=12 there?
41554166
# code de-duplication versus perf hit?
41564167
# TODO: with small adjustments this could be used in shift_quarters
41574168
months_since = other.month % modby - month % modby
41584169

41594170
if n > 0:
41604171
if months_since < 0 or (months_since == 0 and
4161-
other.day < get_day_of_month(other,
4172+
other.day < get_day_of_month(&dts,
41624173
day_opt)):
41634174
# pretend to roll back if on same month but
41644175
# before compare_day
41654176
n -= 1
41664177
else:
41674178
if months_since > 0 or (months_since == 0 and
4168-
other.day > get_day_of_month(other,
4179+
other.day > get_day_of_month(&dts,
41694180
day_opt)):
41704181
# make sure to roll forward, so negate
41714182
n += 1
@@ -4232,18 +4243,22 @@ def roll_yearday(other: datetime, n: int, month: int, day_opt: object) -> int:
42324243
-6
42334244

42344245
"""
4246+
cdef:
4247+
npy_datetimestruct dts
4248+
pydate_to_dtstruct(other, &dts)
4249+
42354250
# Note: The other.day < ... condition will never hold when day_opt=='start'
42364251
# and the other.day > ... condition will never hold when day_opt=='end'.
42374252
# At some point these extra checks may need to be optimized away.
42384253
# But that point isn't today.
42394254
if n > 0:
42404255
if other.month < month or (other.month == month and
4241-
other.day < get_day_of_month(other,
4256+
other.day < get_day_of_month(&dts,
42424257
day_opt)):
42434258
n -= 1
42444259
else:
42454260
if other.month > month or (other.month == month and
4246-
other.day > get_day_of_month(other,
4261+
other.day > get_day_of_month(&dts,
42474262
day_opt)):
42484263
n += 1
42494264
return n

0 commit comments

Comments
 (0)