@@ -3757,16 +3757,22 @@ cdef shift_quarters(
37573757 n = quarters
37583758
37593759 months_since = (dts.month - q1start_month) % modby
3760+ compare_day = get_day_of_month(& dts, day_opt)
37603761
37613762 # offset semantics - if on the anchor point and going backwards
37623763 # shift to next
37633764 if n <= 0 and (months_since != 0 or
3764- (months_since == 0 and dts.day > 1 )):
3765+ (months_since == 0 and dts.day > compare_day)):
3766+ # make sure to roll forward, so negate
37653767 n += 1
3768+ elif n > 0 and (months_since == 0 and dts.day < compare_day):
3769+ # pretend to roll back if on same month but
3770+ # before compare_day
3771+ n -= 1
37663772
37673773 dts.year = year_add_months(dts, modby * n - months_since)
37683774 dts.month = month_add_months(dts, modby * n - months_since)
3769- dts.day = 1
3775+ dts.day = get_day_of_month( & dts, day_opt)
37703776
37713777 out[i] = dtstruct_to_dt64(& dts)
37723778
@@ -3781,21 +3787,20 @@ cdef shift_quarters(
37813787 n = quarters
37823788
37833789 months_since = (dts.month - q1start_month) % modby
3790+ compare_day = get_day_of_month(& dts, day_opt)
37843791
3785- if n <= 0 and months_since != 0 :
3786- # The general case of this condition would be
3787- # `months_since != 0 or (months_since == 0 and
3788- # dts.day > get_days_in_month(dts.year, dts.month))`
3789- # but the get_days_in_month inequality would never hold.
3792+ if n <= 0 and (months_since != 0 or
3793+ (months_since == 0 and dts.day > compare_day)):
3794+ # make sure to roll forward, so negate
37903795 n += 1
3791- elif n > 0 and (months_since == 0 and
3792- dts.day < get_days_in_month(dts.year,
3793- dts.month)):
3796+ elif n > 0 and (months_since == 0 and dts.day < compare_day):
3797+ # pretend to roll back if on same month but
3798+ # before compare_day
37943799 n -= 1
37953800
37963801 dts.year = year_add_months(dts, modby * n - months_since)
37973802 dts.month = month_add_months(dts, modby * n - months_since)
3798- dts.day = get_days_in_month( dts.year, dts.month )
3803+ dts.day = get_day_of_month( & dts, day_opt )
37993804
38003805 out[i] = dtstruct_to_dt64(& dts)
38013806
@@ -3812,7 +3817,7 @@ cdef shift_quarters(
38123817 months_since = (dts.month - q1start_month) % modby
38133818 # compare_day is only relevant for comparison in the case
38143819 # where months_since == 0.
3815- compare_day = get_firstbday( dts.year, dts.month )
3820+ compare_day = get_day_of_month( & dts, day_opt )
38163821
38173822 if n <= 0 and (months_since != 0 or
38183823 (months_since == 0 and dts.day > compare_day)):
@@ -3826,7 +3831,7 @@ cdef shift_quarters(
38263831 dts.year = year_add_months(dts, modby * n - months_since)
38273832 dts.month = month_add_months(dts, modby * n - months_since)
38283833
3829- dts.day = get_firstbday( dts.year, dts.month )
3834+ dts.day = get_day_of_month( & dts, day_opt )
38303835
38313836 out[i] = dtstruct_to_dt64(& dts)
38323837
@@ -3843,7 +3848,7 @@ cdef shift_quarters(
38433848 months_since = (dts.month - q1start_month) % modby
38443849 # compare_day is only relevant for comparison in the case
38453850 # where months_since == 0.
3846- compare_day = get_lastbday( dts.year, dts.month )
3851+ compare_day = get_day_of_month( & dts, day_opt )
38473852
38483853 if n <= 0 and (months_since != 0 or
38493854 (months_since == 0 and dts.day > compare_day)):
@@ -3857,7 +3862,7 @@ cdef shift_quarters(
38573862 dts.year = year_add_months(dts, modby * n - months_since)
38583863 dts.month = month_add_months(dts, modby * n - months_since)
38593864
3860- dts.day = get_lastbday( dts.year, dts.month )
3865+ dts.day = get_day_of_month( & dts, day_opt )
38613866
38623867 out[i] = dtstruct_to_dt64(& dts)
38633868
@@ -3909,7 +3914,7 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
39093914
39103915 dt64_to_dtstruct(dtindex[i], & dts)
39113916 months_to_roll = months
3912- compare_day = 1
3917+ compare_day = get_day_of_month( & dts, day_opt)
39133918
39143919 # offset semantics - if on the anchor point and going backwards
39153920 # shift to next
@@ -3918,7 +3923,7 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
39183923
39193924 dts.year = year_add_months(dts, months_to_roll)
39203925 dts.month = month_add_months(dts, months_to_roll)
3921- dts.day = 1
3926+ dts.day = get_day_of_month( & dts, day_opt)
39223927
39233928 out[i] = dtstruct_to_dt64(& dts)
39243929 elif day_opt == " end" :
@@ -3930,7 +3935,7 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
39303935
39313936 dt64_to_dtstruct(dtindex[i], & dts)
39323937 months_to_roll = months
3933- compare_day = get_days_in_month( dts.year, dts.month )
3938+ compare_day = get_day_of_month( & dts, day_opt )
39343939
39353940 # similar semantics - when adding shift forward by one
39363941 # month if already at an end of month
@@ -3940,7 +3945,7 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
39403945 dts.year = year_add_months(dts, months_to_roll)
39413946 dts.month = month_add_months(dts, months_to_roll)
39423947
3943- dts.day = get_days_in_month( dts.year, dts.month )
3948+ dts.day = get_day_of_month( & dts, day_opt )
39443949 out[i] = dtstruct_to_dt64(& dts)
39453950
39463951 elif day_opt == " business_start" :
@@ -3952,15 +3957,15 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
39523957
39533958 dt64_to_dtstruct(dtindex[i], & dts)
39543959 months_to_roll = months
3955- compare_day = get_firstbday( dts.year, dts.month )
3960+ compare_day = get_day_of_month( & dts, day_opt )
39563961
39573962 months_to_roll = roll_convention(dts.day, months_to_roll,
39583963 compare_day)
39593964
39603965 dts.year = year_add_months(dts, months_to_roll)
39613966 dts.month = month_add_months(dts, months_to_roll)
39623967
3963- dts.day = get_firstbday( dts.year, dts.month )
3968+ dts.day = get_day_of_month( & dts, day_opt )
39643969 out[i] = dtstruct_to_dt64(& dts)
39653970
39663971 elif day_opt == " business_end" :
@@ -3972,15 +3977,15 @@ def shift_months(const int64_t[:] dtindex, int months, object day_opt=None):
39723977
39733978 dt64_to_dtstruct(dtindex[i], & dts)
39743979 months_to_roll = months
3975- compare_day = get_lastbday( dts.year, dts.month )
3980+ compare_day = get_day_of_month( & dts, day_opt )
39763981
39773982 months_to_roll = roll_convention(dts.day, months_to_roll,
39783983 compare_day)
39793984
39803985 dts.year = year_add_months(dts, months_to_roll)
39813986 dts.month = month_add_months(dts, months_to_roll)
39823987
3983- dts.day = get_lastbday( dts.year, dts.month )
3988+ dts.day = get_day_of_month( & dts, day_opt )
39843989 out[i] = dtstruct_to_dt64(& dts)
39853990
39863991 else :
@@ -4051,7 +4056,7 @@ def shift_month(stamp: datetime, months: int,
40514056 return stamp.replace(year = year, month = month, day = day)
40524057
40534058
4054- cdef int get_day_of_month(npy_datetimestruct* dts, day_opt) nogil except ? - 1 :
4059+ cdef inline int get_day_of_month(npy_datetimestruct* dts, day_opt) nogil except ? - 1 :
40554060 """
40564061 Find the day in `other`'s month that satisfies a DateOffset's is_on_offset
40574062 policy, as described by the `day_opt` argument.
0 commit comments