Skip to content

Commit 47ede13

Browse files
committed
simplify algebra in Year offset apply methods
1 parent 69472f9 commit 47ede13

File tree

2 files changed

+43
-73
lines changed

2 files changed

+43
-73
lines changed

pandas/_libs/tslibs/offsets.pyx

+33
Original file line numberDiff line numberDiff line change
@@ -428,3 +428,36 @@ cpdef datetime shift_month(datetime stamp, int months, object day_opt=None):
428428
else:
429429
raise ValueError(day_opt)
430430
return stamp.replace(year=year, month=month, day=day)
431+
432+
433+
cpdef int _get_day_of_month(datetime other, day_opt):
434+
if day_opt == 'start':
435+
return 1
436+
elif day_opt == 'end':
437+
return monthrange(other.year, other.month)[1]
438+
else:
439+
raise ValueError(day_opt)
440+
441+
442+
cpdef int roll_yearday(other, n, month, day_opt='start'):
443+
"""
444+
Possibly increment or decrement the number of periods to shift
445+
based on rollforward/rollbackward conventions.
446+
447+
Mirrors `roll_check` in tslib.shift_months
448+
"""
449+
# Note: The other.day < ... condition will never hold when day_opt=='start'
450+
# and the other.day > ... condition will never hold when day_opt=='end'.
451+
# At some point these extra checks may need to be optimized away.
452+
# But that point isn't today.
453+
if n > 0:
454+
if other.month < month or (other.month == month and
455+
other.day < _get_day_of_month(other,
456+
day_opt)):
457+
n -= 1
458+
elif n <= 0:
459+
if other.month > month or (other.month == month and
460+
other.day > _get_day_of_month(other,
461+
day_opt)):
462+
n += 1
463+
return n

pandas/tseries/offsets.py

+10-73
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
_int_to_weekday, _weekday_to_int,
2323
_determine_offset,
2424
apply_index_wraps,
25+
roll_yearday,
2526
shift_month,
2627
BeginMixin, EndMixin,
2728
BaseOffset)
@@ -1943,49 +1944,12 @@ class YearEnd(EndMixin, YearOffset):
19431944

19441945
@apply_wraps
19451946
def apply(self, other):
1946-
def _increment(date):
1947-
if date.month == self.month:
1948-
_, days_in_month = tslib.monthrange(date.year, self.month)
1949-
if date.day != days_in_month:
1950-
year = date.year
1951-
else:
1952-
year = date.year + 1
1953-
elif date.month < self.month:
1954-
year = date.year
1955-
else:
1956-
year = date.year + 1
1957-
_, days_in_month = tslib.monthrange(year, self.month)
1958-
return datetime(year, self.month, days_in_month,
1959-
date.hour, date.minute, date.second,
1960-
date.microsecond)
1961-
1962-
def _decrement(date):
1963-
year = date.year if date.month > self.month else date.year - 1
1964-
_, days_in_month = tslib.monthrange(year, self.month)
1965-
return datetime(year, self.month, days_in_month,
1966-
date.hour, date.minute, date.second,
1967-
date.microsecond)
1968-
1969-
def _rollf(date):
1970-
if date.month != self.month or\
1971-
date.day < tslib.monthrange(date.year, date.month)[1]:
1972-
date = _increment(date)
1973-
return date
1974-
1975-
n = self.n
1976-
result = other
1977-
if n > 0:
1978-
while n > 0:
1979-
result = _increment(result)
1980-
n -= 1
1981-
elif n < 0:
1982-
while n < 0:
1983-
result = _decrement(result)
1984-
n += 1
1985-
else:
1986-
# n == 0, roll forward
1987-
result = _rollf(result)
1988-
return result
1947+
n = roll_yearday(other, self.n, self.month, 'end')
1948+
year = other.year + n
1949+
days_in_month = tslib.monthrange(year, self.month)[1]
1950+
return datetime(year, self.month, days_in_month,
1951+
other.hour, other.minute, other.second,
1952+
other.microsecond)
19891953

19901954
@apply_index_wraps
19911955
def apply_index(self, i):
@@ -2006,36 +1970,9 @@ class YearBegin(BeginMixin, YearOffset):
20061970

20071971
@apply_wraps
20081972
def apply(self, other):
2009-
def _increment(date, n):
2010-
year = date.year + n - 1
2011-
if date.month >= self.month:
2012-
year += 1
2013-
return datetime(year, self.month, 1, date.hour, date.minute,
2014-
date.second, date.microsecond)
2015-
2016-
def _decrement(date, n):
2017-
year = date.year + n + 1
2018-
if date.month < self.month or (date.month == self.month and
2019-
date.day == 1):
2020-
year -= 1
2021-
return datetime(year, self.month, 1, date.hour, date.minute,
2022-
date.second, date.microsecond)
2023-
2024-
def _rollf(date):
2025-
if (date.month != self.month) or date.day > 1:
2026-
date = _increment(date, 1)
2027-
return date
2028-
2029-
n = self.n
2030-
result = other
2031-
if n > 0:
2032-
result = _increment(result, n)
2033-
elif n < 0:
2034-
result = _decrement(result, n)
2035-
else:
2036-
# n == 0, roll forward
2037-
result = _rollf(result)
2038-
return result
1973+
n = roll_yearday(other, self.n, self.month, 'start')
1974+
year = other.year + n
1975+
return other.replace(year=year, month=self.month, day=1)
20391976

20401977
@apply_index_wraps
20411978
def apply_index(self, i):

0 commit comments

Comments
 (0)