-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Fix FY5253 onOffset/apply bug, simplify #18877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
7f2bcf4
ed7fde0
e0850fa
81b1805
a9a5cdc
e08a1a7
9f02521
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -158,17 +158,6 @@ def test_apply(self): | |
|
||
class TestFY5253NearestEndMonth(Base): | ||
|
||
def test_get_target_month_end(self): | ||
assert (makeFY5253NearestEndMonth( | ||
startingMonth=8, weekday=WeekDay.SAT).get_target_month_end( | ||
datetime(2013, 1, 1)) == datetime(2013, 8, 31)) | ||
assert (makeFY5253NearestEndMonth( | ||
startingMonth=12, weekday=WeekDay.SAT).get_target_month_end( | ||
datetime(2013, 1, 1)) == datetime(2013, 12, 31)) | ||
assert (makeFY5253NearestEndMonth( | ||
startingMonth=2, weekday=WeekDay.SAT).get_target_month_end( | ||
datetime(2013, 1, 1)) == datetime(2013, 2, 28)) | ||
|
||
def test_get_year_end(self): | ||
assert (makeFY5253NearestEndMonth( | ||
startingMonth=8, weekday=WeekDay.SAT).get_year_end( | ||
|
@@ -625,3 +614,22 @@ def test_bunched_yearends(): | |
assert fy.rollback(dt) == Timestamp('2002-12-28') | ||
assert (-fy).apply(dt) == Timestamp('2002-12-28') | ||
assert dt - fy == Timestamp('2002-12-28') | ||
|
||
|
||
def test_fy5253_last_onoffset(): | ||
# GH#18877 dates on the year-end but not normalized to midnight | ||
offset = FY5253(n=-5, startingMonth=5, variation="last", weekday=0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment to these tests about what you are testing. next person to come along will have no idea. add the issue number as well. |
||
ts = Timestamp('1984-05-28 06:29:43.955911354+0200', | ||
tz='Europe/San_Marino') | ||
fast = offset.onOffset(ts) | ||
slow = (ts + offset) - offset == ts | ||
assert fast == slow | ||
|
||
|
||
def test_fy5253_nearest_onoffset(): | ||
# GH#18877 dates on the year-end but not normalized to midnight | ||
offset = FY5253(n=3, startingMonth=7, variation="nearest", weekday=2) | ||
ts = Timestamp('2032-07-28 00:12:59.035729419+0000', tz='Africa/Dakar') | ||
fast = offset.onOffset(ts) | ||
slow = (ts + offset) - offset == ts | ||
assert fast == slow |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1814,13 +1814,6 @@ def __init__(self, n=1, normalize=False, weekday=0, startingMonth=1, | |
raise ValueError('{variation} is not a valid variation' | ||
.format(variation=self.variation)) | ||
|
||
@cache_readonly | ||
def _offset_lwom(self): | ||
if self.variation == "nearest": | ||
return None | ||
else: | ||
return LastWeekOfMonth(n=1, weekday=self.weekday) | ||
|
||
def isAnchored(self): | ||
return (self.n == 1 and | ||
self.startingMonth is not None and | ||
|
@@ -1841,6 +1834,8 @@ def onOffset(self, dt): | |
|
||
@apply_wraps | ||
def apply(self, other): | ||
norm = Timestamp(other).normalize() | ||
|
||
n = self.n | ||
prev_year = self.get_year_end( | ||
datetime(other.year - 1, self.startingMonth, 1)) | ||
|
@@ -1853,32 +1848,26 @@ def apply(self, other): | |
cur_year = tslib._localize_pydatetime(cur_year, other.tzinfo) | ||
next_year = tslib._localize_pydatetime(next_year, other.tzinfo) | ||
|
||
if other == prev_year: | ||
# Note: next_year.year == other.year + 1, so we will always | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pls add a blank line (generally) before comments. |
||
# have other < next_year | ||
if norm == prev_year: | ||
n -= 1 | ||
elif other == cur_year: | ||
elif norm == cur_year: | ||
pass | ||
elif other == next_year: | ||
n += 1 | ||
# TODO: Not hit in tests | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double-checked this, it cannot be reached. See new comment above re |
||
elif n > 0: | ||
if other < prev_year: | ||
if norm < prev_year: | ||
n -= 2 | ||
elif prev_year < other < cur_year: | ||
elif prev_year < norm < cur_year: | ||
n -= 1 | ||
elif cur_year < other < next_year: | ||
elif cur_year < norm < next_year: | ||
pass | ||
else: | ||
assert False | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double-checked this, it cannot be reached. See new comment above re |
||
else: | ||
if next_year < other: | ||
n += 2 | ||
# TODO: Not hit in tests; UPDATE: looks impossible | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double-checked this, it cannot be reached. See new comment above re |
||
elif cur_year < other < next_year: | ||
if cur_year < norm < next_year: | ||
n += 1 | ||
elif prev_year < other < cur_year: | ||
elif prev_year < norm < cur_year: | ||
pass | ||
elif (other.year == prev_year.year and other < prev_year and | ||
prev_year - other <= timedelta(6)): | ||
elif (norm.year == prev_year.year and norm < prev_year and | ||
prev_year - norm <= timedelta(6)): | ||
# GH#14774, error when next_year.year == cur_year.year | ||
# e.g. prev_year == datetime(2004, 1, 3), | ||
# other == datetime(2004, 1, 1) | ||
|
@@ -1894,35 +1883,30 @@ def apply(self, other): | |
return result | ||
|
||
def get_year_end(self, dt): | ||
if self.variation == "nearest": | ||
return self._get_year_end_nearest(dt) | ||
else: | ||
return self._get_year_end_last(dt) | ||
|
||
def get_target_month_end(self, dt): | ||
target_month = datetime(dt.year, self.startingMonth, 1, | ||
tzinfo=dt.tzinfo) | ||
return shift_month(target_month, 0, 'end') | ||
# TODO: is this DST-safe? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. non-issue b/c |
||
assert dt.tzinfo is None | ||
|
||
def _get_year_end_nearest(self, dt): | ||
target_date = self.get_target_month_end(dt) | ||
dim = ccalendar.get_days_in_month(dt.year, self.startingMonth) | ||
target_date = datetime(dt.year, self.startingMonth, dim) | ||
wkday_diff = self.weekday - target_date.weekday() | ||
if wkday_diff == 0: | ||
# year_end is the same for "last" and "nearest" cases | ||
return target_date | ||
|
||
days_forward = wkday_diff % 7 | ||
if days_forward <= 3: | ||
# The upcoming self.weekday is closer than the previous one | ||
return target_date + timedelta(days_forward) | ||
else: | ||
# The previous self.weekday is closer than the upcoming one | ||
return target_date + timedelta(days_forward - 7) | ||
if self.variation == "last": | ||
days_forward = (wkday_diff % 7) - 7 | ||
|
||
def _get_year_end_last(self, dt): | ||
current_year = datetime(dt.year, self.startingMonth, 1, | ||
tzinfo=dt.tzinfo) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only called with |
||
return current_year + self._offset_lwom | ||
# days_forward is always negative, so we always end up | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here |
||
# in the same year as dt | ||
return target_date + timedelta(days=days_forward) | ||
else: | ||
# variation == "nearest": | ||
days_forward = wkday_diff % 7 | ||
if days_forward <= 3: | ||
# The upcoming self.weekday is closer than the previous one | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these comments are good, FYI (you only need a blank line when the indentation is the same) |
||
return target_date + timedelta(days_forward) | ||
else: | ||
# The previous self.weekday is closer than the upcoming one | ||
return target_date + timedelta(days_forward - 7) | ||
|
||
@property | ||
def rule_code(self): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these tests wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but
get_target_month_end
is removed in this PR.