-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
BUG: Breaking change to offsets.pyx - quarter offset classes #39890
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 55 commits
d4428da
a546ddc
d564763
85ee259
8b4ed54
6e1f3c5
01ce09f
b95cc70
ccb9b97
f1204de
a8c8a3d
1338c1f
d7c8cad
3399d67
79fe747
2f456cb
3fd9a64
3ce1de9
7e1e5d2
f9e7c3b
27a24ba
197fa1b
8154be9
24afb65
2757983
dc55ee6
12cf5bf
8b91c5c
9b4f609
d1099a8
8892734
1cde76e
4e659a5
36df0f7
1ec3a9b
1a6bf2c
a8e47a4
cbce0bd
9506ee0
6a7b0db
bafe9d2
e5ad2ac
66d37b5
57fe350
e2665e0
94947d4
30ad2ff
9621c16
b9d6383
5b89343
6208287
ace4503
63a1152
f2410e7
bc0d0c6
f21921d
448e0da
fe07ea2
5b2ec3f
827ab65
f27b5ef
1441a29
7875614
688eaf6
c0f6d84
a5416a1
1d3d45d
22c779f
384cb82
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 |
---|---|---|
|
@@ -1840,7 +1840,7 @@ cdef class YearOffset(SingleConstructorOffset): | |
""" | ||
_attributes = tuple(["n", "normalize", "month"]) | ||
|
||
# _default_month: int # FIXME: python annotation here breaks things | ||
# FIXME: python annotation here breaks things | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
cdef readonly: | ||
int month | ||
|
@@ -1986,52 +1986,90 @@ cdef class YearBegin(YearOffset): | |
# Quarter-Based Offset Classes | ||
|
||
cdef class QuarterOffset(SingleConstructorOffset): | ||
_attributes = tuple(["n", "normalize", "startingMonth"]) | ||
""" | ||
Base for quarter-based offset classes. | ||
|
||
Parameters | ||
---------- | ||
n : int, default 1 | ||
Number of quarters to offset. | ||
normalize : bool, default False | ||
If true, the time component of the resulting date-time is converted | ||
to 00:00:00, i.e. midnight (the start, not the end of date-time). | ||
month : int | ||
The calendar month number (1 for January) of the beginning or ending | ||
month in a custom-defined quarter to be used as an offset. | ||
|
||
.. versionadded:: 1.X.X | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
startingMonth : int | ||
The calendar month number (1 for January) of the beginning or ending | ||
month in a custom-defined quarter to be used as an offset. | ||
|
||
.. deprecated:: 1.X.X | ||
""" | ||
|
||
_attributes = tuple(["n", "normalize", "month"]) | ||
# TODO: Consider combining QuarterOffset and YearOffset __init__ at some | ||
# point. Also apply_index, is_on_offset, rule_code if | ||
# startingMonth vs month attr names are resolved | ||
# point. Also apply_index, is_on_offset, rule_code. | ||
|
||
# FIXME: python annotations here breaks things | ||
# _default_starting_month: int | ||
# _from_name_starting_month: int | ||
# FIXME: python annotation here breaks things | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
cdef readonly: | ||
int startingMonth | ||
int month | ||
int startingMonth # Backwards compatibility | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def __init__(self, n=1, normalize=False, startingMonth=None): | ||
def __init__(self, n=1, normalize=False, month=None, | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*, startingMonth=None): # Backwards compatibility | ||
BaseOffset.__init__(self, n, normalize) | ||
|
||
if startingMonth is None: | ||
startingMonth = self._default_starting_month | ||
self.startingMonth = startingMonth | ||
# Backwards compatibility | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if month and startingMonth and month != startingMonth: | ||
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. don't you need to check not-none? e.g. 0 is value that would hit this too (which would fail but is misleading no?) |
||
raise TypeError("Offset received both month and startingMonth") | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
elif month is None and startingMonth is not None: | ||
warnings.warn( | ||
"startingMonth is deprecated, use month instead.", | ||
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. should this explain that the meaning has changed? or point to a link? |
||
DeprecationWarning, | ||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
stacklevel=2 | ||
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. trailing comma, pretend we could run 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. Fixed, hopefully. |
||
) | ||
month = startingMonth | ||
|
||
month = month if month is not None else self._default_month | ||
self.month = month | ||
|
||
if month < 1 or month > 12: | ||
raise ValueError("Month must go from 1 to 12.") | ||
|
||
cpdef __setstate__(self, state): | ||
self.startingMonth = state.pop("startingMonth") | ||
try: | ||
self.month = state.pop("month") | ||
except: | ||
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. can you be more specifi on the except 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. I added a longer comment there. 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. a) im pretty sure jreback meant not to use a bare "except:", but to specifically catch b) rule of thumb: pls let the person who asked the question hit the "resolve conversation" button; something might be clear to you but not to them 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. yeah this is non-obvious on the exception. |
||
self.month = state.pop("startingMonth") # For legacy pickles | ||
self.n = state.pop("n") | ||
self.normalize = state.pop("normalize") | ||
self._cache = {} | ||
|
||
@classmethod | ||
def _from_name(cls, suffix=None): | ||
kwargs = {} | ||
if suffix: | ||
kwargs["startingMonth"] = MONTH_TO_CAL_NUM[suffix] | ||
kwargs["month"] = MONTH_TO_CAL_NUM[suffix] | ||
else: | ||
if cls._from_name_starting_month is not None: | ||
kwargs["startingMonth"] = cls._from_name_starting_month | ||
if cls._from_name_month is not None: | ||
kwargs["month"] = cls._from_name_month | ||
return cls(**kwargs) | ||
|
||
@property | ||
def rule_code(self) -> str: | ||
month = MONTH_ALIASES[self.startingMonth] | ||
month = MONTH_ALIASES[self.month] | ||
return f"{self._prefix}-{month}" | ||
|
||
def is_anchored(self) -> bool: | ||
return self.n == 1 and self.startingMonth is not None | ||
return self.n == 1 and self.month is not None | ||
|
||
def is_on_offset(self, dt: datetime) -> bool: | ||
if self.normalize and not _is_normalized(dt): | ||
return False | ||
mod_month = (dt.month - self.startingMonth) % 3 | ||
mod_month = (dt.month - self.month) % 3 | ||
return mod_month == 0 and dt.day == self._get_offset_day(dt) | ||
|
||
@apply_wraps | ||
|
@@ -2041,9 +2079,9 @@ cdef class QuarterOffset(SingleConstructorOffset): | |
# Then find the month in that quarter containing an is_on_offset date for | ||
# self. `months_since` is the number of months to shift other.month | ||
# to get to this on-offset month. | ||
months_since = other.month % 3 - self.startingMonth % 3 | ||
months_since = other.month % 3 - self.month % 3 | ||
qtrs = roll_qtrday( | ||
other, self.n, self.startingMonth, day_opt=self._day_opt, modby=3 | ||
other, self.n, self.month, day_opt=self._day_opt, modby=3 | ||
) | ||
months = qtrs * 3 - months_since | ||
return shift_month(other, months, self._day_opt) | ||
|
@@ -2055,35 +2093,53 @@ cdef class QuarterOffset(SingleConstructorOffset): | |
@apply_array_wraps | ||
def _apply_array(self, dtarr): | ||
shifted = shift_quarters( | ||
dtarr.view("i8"), self.n, self.startingMonth, self._day_opt | ||
dtarr.view("i8"), self.n, self.month, self._day_opt | ||
) | ||
return shifted | ||
|
||
|
||
cdef class BQuarterEnd(QuarterOffset): | ||
""" | ||
DateOffset increments between the last business day of each Quarter. | ||
DateOffset increments between the last business day of each quarter. | ||
|
||
startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ... | ||
startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ... | ||
startingMonth = 3 corresponds to dates like 3/30/2007, 6/29/2007, ... | ||
month = 1 corresponds to dates like 1/31/2007, 4/30/2007, ... | ||
month = 2 corresponds to dates like 2/28/2007, 5/31/2007, ... | ||
month = 3 corresponds to dates like 3/30/2007, 6/29/2007, ... | ||
|
||
Parameters | ||
---------- | ||
n : int, default 1 | ||
Number of business quarters to offset. | ||
normalize : bool, default False | ||
If true, the time component of the resulting date-time is converted | ||
to 00:00:00, i.e. midnight (the start, not the end of date-time). | ||
month : int, default 3 | ||
The calendar month number (3 for March) of the last month | ||
in a custom-defined business quarter to be used as an offset. | ||
|
||
.. versionadded:: 1.X.X | ||
startingMonth : int, default 3 | ||
The calendar month number (3 for March) of the last month | ||
in a custom-defined business quarter to be used as an offset. | ||
|
||
.. deprecated:: 1.X.X | ||
|
||
Examples | ||
-------- | ||
>>> from pandas.tseries.offset import BQuarterEnd | ||
>>> from pandas.tseries.offsets import BQuarterEnd | ||
>>> ts = pd.Timestamp('2020-05-24 05:01:15') | ||
>>> ts + BQuarterEnd() | ||
Timestamp('2020-06-30 05:01:15') | ||
>>> ts + BQuarterEnd(2) | ||
Timestamp('2020-09-30 05:01:15') | ||
>>> ts + BQuarterEnd(1, startingMonth=2) | ||
>>> ts + BQuarterEnd(1, month=2) | ||
Timestamp('2020-05-29 05:01:15') | ||
>>> ts + BQuarterEnd(startingMonth=2) | ||
>>> ts + BQuarterEnd(month=2) | ||
Timestamp('2020-05-29 05:01:15') | ||
""" | ||
_output_name = "BusinessQuarterEnd" | ||
_default_starting_month = 3 | ||
_from_name_starting_month = 12 | ||
_default_month = 3 | ||
_from_name_month = 3 | ||
_prefix = "BQ" | ||
_day_opt = "business_end" | ||
|
||
|
@@ -2092,26 +2148,44 @@ cdef class BQuarterBegin(QuarterOffset): | |
""" | ||
DateOffset increments between the first business day of each Quarter. | ||
|
||
startingMonth = 1 corresponds to dates like 1/01/2007, 4/01/2007, ... | ||
startingMonth = 2 corresponds to dates like 2/01/2007, 5/01/2007, ... | ||
startingMonth = 3 corresponds to dates like 3/01/2007, 6/01/2007, ... | ||
month = 1 corresponds to dates like 1/01/2007, 4/01/2007, ... | ||
month = 2 corresponds to dates like 2/01/2007, 5/01/2007, ... | ||
month = 3 corresponds to dates like 3/01/2007, 6/01/2007, ... | ||
|
||
Parameters | ||
---------- | ||
n : int, default 1 | ||
Number of business quarters to offset. | ||
normalize : bool, default False | ||
If true, the time component of the resulting date-time is converted | ||
to 00:00:00, i.e. midnight (the start, not the end of date-time). | ||
month : int, default 1 | ||
The calendar month number (1 for January) of the first month | ||
in a custom-defined business quarter to be used as an offset. | ||
|
||
Pawel-Kranzberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.. versionadded:: 1.X.X | ||
startingMonth : int, default 1 | ||
The calendar month number (1 for January) of the first month | ||
in a custom-defined business quarter to be used as an offset. | ||
|
||
.. deprecated:: 1.X.X | ||
|
||
Examples | ||
-------- | ||
>>> from pandas.tseries.offset import BQuarterBegin | ||
>>> from pandas.tseries.offsets import BQuarterBegin | ||
>>> ts = pd.Timestamp('2020-05-24 05:01:15') | ||
>>> ts + BQuarterBegin() | ||
Timestamp('2020-06-01 05:01:15') | ||
>>> ts + BQuarterBegin(2) | ||
Timestamp('2020-09-01 05:01:15') | ||
>>> ts + BQuarterBegin(startingMonth=2) | ||
>>> ts + BQuarterBegin(month=2) | ||
Timestamp('2020-08-03 05:01:15') | ||
>>> ts + BQuarterBegin(-1) | ||
Timestamp('2020-03-02 05:01:15') | ||
""" | ||
_output_name = "BusinessQuarterBegin" | ||
_default_starting_month = 3 | ||
_from_name_starting_month = 1 | ||
_default_month = 1 | ||
_from_name_month = 1 | ||
_prefix = "BQS" | ||
_day_opt = "business_start" | ||
|
||
|
@@ -2120,34 +2194,73 @@ cdef class QuarterEnd(QuarterOffset): | |
""" | ||
DateOffset increments between Quarter end dates. | ||
|
||
startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ... | ||
startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ... | ||
startingMonth = 3 corresponds to dates like 3/31/2007, 6/30/2007, ... | ||
month = 1 corresponds to dates like 1/31/2007, 4/30/2007, ... | ||
month = 2 corresponds to dates like 2/28/2007, 5/31/2007, ... | ||
month = 3 corresponds to dates like 3/31/2007, 6/30/2007, ... | ||
|
||
Parameters | ||
---------- | ||
n : int, default 1 | ||
Number of quarters to offset. | ||
normalize : bool, default False | ||
If true, the time component of the resulting date-time is converted | ||
to 00:00:00, i.e. midnight (the start, not the end of date-time). | ||
month : int, default 3 | ||
The calendar month number (3 for March) of the last month | ||
in a custom-defined quarter to be used as an offset. | ||
|
||
.. versionadded:: 1.X.X | ||
startingMonth : int, default 3 | ||
The calendar month number (3 for March) of the last month | ||
in a custom-defined quarter to be used as an offset. | ||
|
||
.. deprecated:: 1.X.X | ||
""" | ||
_default_starting_month = 3 | ||
_default_month = 3 | ||
_from_name_month = 3 | ||
_prefix = "Q" | ||
_day_opt = "end" | ||
|
||
cdef readonly: | ||
int _period_dtype_code | ||
|
||
def __init__(self, n=1, normalize=False, startingMonth=None): | ||
def __init__(self, n=1, normalize=False, month=None, | ||
*, startingMonth=None): # Backwards compatibility | ||
# Because QuarterEnd can be the freq for a Period, define its | ||
# _period_dtype_code at construction for performance | ||
QuarterOffset.__init__(self, n, normalize, startingMonth) | ||
self._period_dtype_code = PeriodDtypeCode.Q_DEC + self.startingMonth % 12 | ||
QuarterOffset.__init__(self, n, normalize, month, | ||
startingMonth=startingMonth) | ||
self._period_dtype_code = PeriodDtypeCode.Q_DEC + self.month % 12 | ||
|
||
|
||
cdef class QuarterBegin(QuarterOffset): | ||
""" | ||
DateOffset increments between Quarter start dates. | ||
|
||
startingMonth = 1 corresponds to dates like 1/01/2007, 4/01/2007, ... | ||
startingMonth = 2 corresponds to dates like 2/01/2007, 5/01/2007, ... | ||
startingMonth = 3 corresponds to dates like 3/01/2007, 6/01/2007, ... | ||
month = 1 corresponds to dates like 1/01/2007, 4/01/2007, ... | ||
month = 2 corresponds to dates like 2/01/2007, 5/01/2007, ... | ||
month = 3 corresponds to dates like 3/01/2007, 6/01/2007, ... | ||
|
||
Parameters | ||
---------- | ||
n : int, default 1 | ||
Number of quarters to offset. | ||
normalize : bool, default False | ||
If true, the time component of the resulting date-time is converted | ||
to 00:00:00, i.e. midnight (the start, not the end of date-time). | ||
month : int, default 1 | ||
The calendar month number (1 for January) of the first month | ||
in a custom-defined quarter to be used as an offset. | ||
|
||
.. versionadded:: 1.X.X | ||
startingMonth : int, default 1 | ||
The calendar month number (1 for January) of the first month | ||
in a custom-defined quarter to be used as an offset. | ||
|
||
.. deprecated:: 1.X.X | ||
""" | ||
_default_starting_month = 3 | ||
_from_name_starting_month = 1 | ||
_default_month = 1 | ||
_from_name_month = 1 | ||
_prefix = "QS" | ||
_day_opt = "start" | ||
|
||
|
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.
isn't this a duplicate of the above section?