From 6c7fffee9e4bd8a8a166d40bc1c5eb188e4de803 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 2 Jan 2021 20:22:24 -0800 Subject: [PATCH 1/2] REF: de-duplicate code in libparsing/libperiod --- pandas/_libs/tslibs/parsing.pxd | 1 + pandas/_libs/tslibs/parsing.pyx | 59 ++++++++++++++++++++++++--------- pandas/_libs/tslibs/period.pyx | 37 +-------------------- pandas/core/arrays/period.py | 3 +- 4 files changed, 47 insertions(+), 53 deletions(-) diff --git a/pandas/_libs/tslibs/parsing.pxd b/pandas/_libs/tslibs/parsing.pxd index 9c9262beaafad..25667f00e42b5 100644 --- a/pandas/_libs/tslibs/parsing.pxd +++ b/pandas/_libs/tslibs/parsing.pxd @@ -1,2 +1,3 @@ cpdef str get_rule_month(str source) +cpdef quarter_to_myear(int year, int quarter, str freq) diff --git a/pandas/_libs/tslibs/parsing.pyx b/pandas/_libs/tslibs/parsing.pyx index aeb1be121bc9e..cea241347d774 100644 --- a/pandas/_libs/tslibs/parsing.pyx +++ b/pandas/_libs/tslibs/parsing.pyx @@ -378,7 +378,7 @@ cpdef bint _does_string_look_like_datetime(str py_string): cdef inline object _parse_dateabbr_string(object date_string, datetime default, - object freq): + str freq=None): cdef: object ret # year initialized to prevent compiler warnings @@ -438,21 +438,13 @@ cdef inline object _parse_dateabbr_string(object date_string, datetime default, f'quarter must be ' f'between 1 and 4: {date_string}') - if freq is not None: - # TODO: hack attack, #1228 - freq = getattr(freq, "freqstr", freq) - try: - mnum = c_MONTH_NUMBERS[get_rule_month(freq)] + 1 - except (KeyError, ValueError): - raise DateParseError(f'Unable to retrieve month ' - f'information from given ' - f'freq: {freq}') - - month = (mnum + (quarter - 1) * 3) % 12 + 1 - if month > mnum: - year -= 1 - else: - month = (quarter - 1) * 3 + 1 + try: + # TODO: hack attack, GH#1228 + year, month = quarter_to_myear(year, quarter, freq) + except KeyError: + raise DateParseError("Unable to retrieve month " + "information from given " + f"freq: {freq}") ret = default.replace(year=year, month=month) return ret, 'quarter' @@ -482,6 +474,41 @@ cdef inline object _parse_dateabbr_string(object date_string, datetime default, raise ValueError(f'Unable to parse {date_string}') +cpdef quarter_to_myear(int year, int quarter, str freq): + """ + A quarterly frequency defines a "year" which may not coincide with + the calendar-year. Find the calendar-year and calendar-month associated + with the given year and quarter under the `freq`-derived calendar. + + Parameters + ---------- + year : int + quarter : int + freq : str or None + + Returns + ------- + year : int + month : int + + See Also + -------- + Period.qyear + """ + if quarter <= 0 or quarter > 4: + raise ValueError("Quarter must be 1 <= q <= 4") + + if freq is not None: + mnum = c_MONTH_NUMBERS[get_rule_month(freq)] + 1 + month = (mnum + (quarter - 1) * 3) % 12 + 1 + if month > mnum: + year -= 1 + else: + month = (quarter - 1) * 3 + 1 + + return year, month + + cdef dateutil_parse( str timestr, object default, diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index cbd4e2e6704a9..f0d21a3a7a957 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -74,8 +74,7 @@ from pandas._libs.tslibs.dtypes cimport ( PeriodDtypeBase, attrname_to_abbrevs, ) -from pandas._libs.tslibs.parsing cimport get_rule_month - +from pandas._libs.tslibs.parsing cimport quarter_to_myear from pandas._libs.tslibs.parsing import parse_time_string from pandas._libs.tslibs.nattype cimport ( @@ -2461,40 +2460,6 @@ cdef int64_t _ordinal_from_fields(int year, int month, quarter, int day, minute, second, 0, 0, base) -def quarter_to_myear(year: int, quarter: int, freqstr: str): - """ - A quarterly frequency defines a "year" which may not coincide with - the calendar-year. Find the calendar-year and calendar-month associated - with the given year and quarter under the `freq`-derived calendar. - - Parameters - ---------- - year : int - quarter : int - freqstr : str - Equivalent to freq.freqstr - - Returns - ------- - year : int - month : int - - See Also - -------- - Period.qyear - """ - if quarter <= 0 or quarter > 4: - raise ValueError('Quarter must be 1 <= q <= 4') - - mnum = c_MONTH_NUMBERS[get_rule_month(freqstr)] + 1 - month = (mnum + (quarter - 1) * 3) % 12 + 1 - if month > mnum: - year -= 1 - - return year, month - # TODO: This whole func is really similar to parsing.pyx L434-L450 - - def validate_end_alias(how): how_dict = {'S': 'S', 'E': 'E', 'START': 'S', 'FINISH': 'E', diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index e0e40a666896d..94d36aef8da52 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -12,6 +12,7 @@ delta_to_nanoseconds, dt64arr_to_periodarr as c_dt64arr_to_periodarr, iNaT, + parsing, period as libperiod, to_offset, ) @@ -1074,7 +1075,7 @@ def _range_from_fields( freqstr = freq.freqstr year, quarter = _make_field_arrays(year, quarter) for y, q in zip(year, quarter): - y, m = libperiod.quarter_to_myear(y, q, freqstr) + y, m = parsing.quarter_to_myear(y, q, freqstr) val = libperiod.period_ordinal(y, m, 1, 1, 1, 1, 0, 0, base) ordinals.append(val) else: From 5a6a334cc80268db53e24c5310536e4887543145 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 3 Jan 2021 10:11:06 -0800 Subject: [PATCH 2/2] update comment --- pandas/_libs/tslibs/parsing.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/parsing.pyx b/pandas/_libs/tslibs/parsing.pyx index cea241347d774..5c3417ee2d93c 100644 --- a/pandas/_libs/tslibs/parsing.pyx +++ b/pandas/_libs/tslibs/parsing.pyx @@ -439,7 +439,7 @@ cdef inline object _parse_dateabbr_string(object date_string, datetime default, f'between 1 and 4: {date_string}') try: - # TODO: hack attack, GH#1228 + # GH#1228 year, month = quarter_to_myear(year, quarter, freq) except KeyError: raise DateParseError("Unable to retrieve month "