Skip to content

Commit 14eda58

Browse files
authored
REF: move bits of offsets to liboffsets, de-privatize (pandas-dev#33936)
1 parent 862db64 commit 14eda58

File tree

11 files changed

+157
-151
lines changed

11 files changed

+157
-151
lines changed

asv_bench/benchmarks/io/parsers.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
try:
44
from pandas._libs.tslibs.parsing import (
5-
_concat_date_cols,
5+
concat_date_cols,
66
_does_string_look_like_datetime,
77
)
88
except ImportError:
@@ -39,4 +39,4 @@ def setup(self, value, dim):
3939
)
4040

4141
def time_check_concat(self, value, dim):
42-
_concat_date_cols(self.object)
42+
concat_date_cols(self.object)

pandas/_libs/tslibs/frequencies.pyx

+3-3
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,13 @@ cpdef get_freq_code(freqstr):
175175
if is_integer_object(freqstr):
176176
return freqstr, 1
177177

178-
base, stride = _base_and_stride(freqstr)
178+
base, stride = base_and_stride(freqstr)
179179
code = _period_str_to_code(base)
180180

181181
return code, stride
182182

183183

184-
cpdef _base_and_stride(str freqstr):
184+
cpdef base_and_stride(str freqstr):
185185
"""
186186
Return base freq and stride info from string representation
187187
@@ -267,7 +267,7 @@ cpdef str get_base_alias(freqstr):
267267
-------
268268
base_alias : str
269269
"""
270-
return _base_and_stride(freqstr)[0]
270+
return base_and_stride(freqstr)[0]
271271

272272

273273
cpdef int get_to_timestamp_base(int base):

pandas/_libs/tslibs/offsets.pyx

+109-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import cython
22

33
import time
44
from typing import Any
5+
import warnings
56
from cpython.datetime cimport (PyDateTime_IMPORT,
67
PyDateTime_Check,
78
PyDelta_Check,
@@ -103,7 +104,7 @@ def as_datetime(obj):
103104
return obj
104105

105106

106-
cpdef bint _is_normalized(dt):
107+
cpdef bint is_normalized(dt):
107108
if (dt.hour != 0 or dt.minute != 0 or dt.second != 0 or
108109
dt.microsecond != 0 or getattr(dt, 'nanosecond', 0) != 0):
109110
return False
@@ -230,7 +231,7 @@ def _get_calendar(weekmask, holidays, calendar):
230231
holidays = holidays + calendar.holidays().tolist()
231232
except AttributeError:
232233
pass
233-
holidays = [_to_dt64D(dt) for dt in holidays]
234+
holidays = [to_dt64D(dt) for dt in holidays]
234235
holidays = tuple(sorted(holidays))
235236

236237
kwargs = {'weekmask': weekmask}
@@ -241,7 +242,7 @@ def _get_calendar(weekmask, holidays, calendar):
241242
return busdaycalendar, holidays
242243

243244

244-
def _to_dt64D(dt):
245+
def to_dt64D(dt):
245246
# Currently
246247
# > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]')
247248
# numpy.datetime64('2013-05-01T02:00:00.000000+0200')
@@ -264,7 +265,7 @@ def _to_dt64D(dt):
264265
# Validation
265266

266267

267-
def _validate_business_time(t_input):
268+
def validate_business_time(t_input):
268269
if isinstance(t_input, str):
269270
try:
270271
t = time.strptime(t_input, '%H:%M')
@@ -440,6 +441,9 @@ class _BaseOffset:
440441
# that allows us to use methods that can go in a `cdef class`
441442
return self * 1
442443

444+
# ------------------------------------------------------------------
445+
# Name and Rendering Methods
446+
443447
def __repr__(self) -> str:
444448
className = getattr(self, '_outputName', type(self).__name__)
445449

@@ -455,6 +459,44 @@ class _BaseOffset:
455459
out = f'<{n_str}{className}{plural}{self._repr_attrs()}>'
456460
return out
457461

462+
@property
463+
def name(self) -> str:
464+
return self.rule_code
465+
466+
@property
467+
def _prefix(self) -> str:
468+
raise NotImplementedError("Prefix not defined")
469+
470+
@property
471+
def rule_code(self) -> str:
472+
return self._prefix
473+
474+
@property
475+
def freqstr(self) -> str:
476+
try:
477+
code = self.rule_code
478+
except NotImplementedError:
479+
return str(repr(self))
480+
481+
if self.n != 1:
482+
fstr = f"{self.n}{code}"
483+
else:
484+
fstr = code
485+
486+
try:
487+
if self._offset:
488+
fstr += self._offset_str()
489+
except AttributeError:
490+
# TODO: standardize `_offset` vs `offset` naming convention
491+
pass
492+
493+
return fstr
494+
495+
def _offset_str(self) -> str:
496+
return ""
497+
498+
# ------------------------------------------------------------------
499+
458500
def _get_offset_day(self, datetime other):
459501
# subclass must implement `_day_opt`; calling from the base class
460502
# will raise NotImplementedError.
@@ -530,6 +572,26 @@ class _BaseOffset:
530572

531573
return state
532574

575+
@property
576+
def nanos(self):
577+
raise ValueError(f"{self} is a non-fixed frequency")
578+
579+
def onOffset(self, dt) -> bool:
580+
warnings.warn(
581+
"onOffset is a deprecated, use is_on_offset instead",
582+
FutureWarning,
583+
stacklevel=1,
584+
)
585+
return self.is_on_offset(dt)
586+
587+
def isAnchored(self) -> bool:
588+
warnings.warn(
589+
"isAnchored is a deprecated, use is_anchored instead",
590+
FutureWarning,
591+
stacklevel=1,
592+
)
593+
return self.is_anchored()
594+
533595

534596
class BaseOffset(_BaseOffset):
535597
# Here we add __rfoo__ methods that don't play well with cdef classes
@@ -564,6 +626,49 @@ class _Tick:
564626
return _wrap_timedelta_result(result)
565627

566628

629+
class BusinessMixin:
630+
"""
631+
Mixin to business types to provide related functions.
632+
"""
633+
634+
@property
635+
def offset(self):
636+
"""
637+
Alias for self._offset.
638+
"""
639+
# Alias for backward compat
640+
return self._offset
641+
642+
def _repr_attrs(self) -> str:
643+
if self.offset:
644+
attrs = [f"offset={repr(self.offset)}"]
645+
else:
646+
attrs = []
647+
out = ""
648+
if attrs:
649+
out += ": " + ", ".join(attrs)
650+
return out
651+
652+
653+
class CustomMixin:
654+
"""
655+
Mixin for classes that define and validate calendar, holidays,
656+
and weekdays attributes.
657+
"""
658+
659+
def __init__(self, weekmask, holidays, calendar):
660+
calendar, holidays = _get_calendar(
661+
weekmask=weekmask, holidays=holidays, calendar=calendar
662+
)
663+
# Custom offset instances are identified by the
664+
# following two attributes. See DateOffset._params()
665+
# holidays, weekmask
666+
667+
object.__setattr__(self, "weekmask", weekmask)
668+
object.__setattr__(self, "holidays", holidays)
669+
object.__setattr__(self, "calendar", calendar)
670+
671+
567672
# ----------------------------------------------------------------------
568673
# RelativeDelta Arithmetic
569674

pandas/_libs/tslibs/parsing.pyx

+2-2
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ cdef inline object convert_to_unicode(object item, bint keep_trivial_numbers):
938938

939939
@cython.wraparound(False)
940940
@cython.boundscheck(False)
941-
def _concat_date_cols(tuple date_cols, bint keep_trivial_numbers=True):
941+
def concat_date_cols(tuple date_cols, bint keep_trivial_numbers=True):
942942
"""
943943
Concatenates elements from numpy arrays in `date_cols` into strings.
944944
@@ -957,7 +957,7 @@ def _concat_date_cols(tuple date_cols, bint keep_trivial_numbers=True):
957957
--------
958958
>>> dates=np.array(['3/31/2019', '4/31/2019'], dtype=object)
959959
>>> times=np.array(['11:20', '10:45'], dtype=object)
960-
>>> result = _concat_date_cols((dates, times))
960+
>>> result = concat_date_cols((dates, times))
961961
>>> result
962962
array(['3/31/2019 11:20', '4/31/2019 10:45'], dtype=object)
963963
"""

pandas/_libs/tslibs/period.pyx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1685,7 +1685,7 @@ cdef class _Period:
16851685
resampled : Period
16861686
"""
16871687
freq = self._maybe_convert_freq(freq)
1688-
how = _validate_end_alias(how)
1688+
how = validate_end_alias(how)
16891689
base1, mult1 = get_freq_code(self.freq)
16901690
base2, mult2 = get_freq_code(freq)
16911691

@@ -1758,7 +1758,7 @@ cdef class _Period:
17581758
"""
17591759
if freq is not None:
17601760
freq = self._maybe_convert_freq(freq)
1761-
how = _validate_end_alias(how)
1761+
how = validate_end_alias(how)
17621762

17631763
end = how == 'E'
17641764
if end:
@@ -2509,7 +2509,7 @@ def quarter_to_myear(year: int, quarter: int, freq):
25092509
return year, month
25102510

25112511

2512-
def _validate_end_alias(how):
2512+
def validate_end_alias(how):
25132513
how_dict = {'S': 'S', 'E': 'E',
25142514
'START': 'S', 'FINISH': 'E',
25152515
'BEGIN': 'S', 'END': 'E'}

pandas/core/arrays/datetimes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,7 @@ def to_period(self, freq=None):
11151115

11161116
# https://github.com/pandas-dev/pandas/issues/33358
11171117
if res is None:
1118-
base, stride = libfrequencies._base_and_stride(freq)
1118+
base, stride = libfrequencies.base_and_stride(freq)
11191119
res = f"{stride}{base}"
11201120

11211121
freq = res

pandas/core/arrays/period.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ def to_timestamp(self, freq=None, how="start"):
431431
"""
432432
from pandas.core.arrays import DatetimeArray
433433

434-
how = libperiod._validate_end_alias(how)
434+
how = libperiod.validate_end_alias(how)
435435

436436
end = how == "E"
437437
if end:
@@ -523,7 +523,7 @@ def asfreq(self, freq=None, how: str = "E") -> "PeriodArray":
523523
PeriodIndex(['2010-01', '2011-01', '2012-01', '2013-01', '2014-01',
524524
'2015-01'], dtype='period[M]', freq='M')
525525
"""
526-
how = libperiod._validate_end_alias(how)
526+
how = libperiod.validate_end_alias(how)
527527

528528
freq = Period._maybe_convert_freq(freq)
529529

pandas/io/parsers.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3264,7 +3264,7 @@ def _make_date_converter(
32643264
):
32653265
def converter(*date_cols):
32663266
if date_parser is None:
3267-
strs = parsing._concat_date_cols(date_cols)
3267+
strs = parsing.concat_date_cols(date_cols)
32683268

32693269
try:
32703270
return tools.to_datetime(
@@ -3292,7 +3292,7 @@ def converter(*date_cols):
32923292
try:
32933293
return tools.to_datetime(
32943294
parsing.try_parse_dates(
3295-
parsing._concat_date_cols(date_cols),
3295+
parsing.concat_date_cols(date_cols),
32963296
parser=date_parser,
32973297
dayfirst=dayfirst,
32983298
),

pandas/tests/io/parser/test_parse_dates.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def date_parser(*date_cols):
8181
-------
8282
parsed : Series
8383
"""
84-
return parsing.try_parse_dates(parsing._concat_date_cols(date_cols))
84+
return parsing.try_parse_dates(parsing.concat_date_cols(date_cols))
8585

8686
result = parser.read_csv(
8787
StringIO(data),
@@ -208,7 +208,7 @@ def test_concat_date_col_fail(container, dim):
208208
date_cols = tuple(container([value]) for _ in range(dim))
209209

210210
with pytest.raises(ValueError, match=msg):
211-
parsing._concat_date_cols(date_cols)
211+
parsing.concat_date_cols(date_cols)
212212

213213

214214
@pytest.mark.parametrize("keep_date_col", [True, False])

pandas/tseries/frequencies.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def to_offset(freq) -> Optional[DateOffset]:
124124
stride = freq[1]
125125
if isinstance(stride, str):
126126
name, stride = stride, name
127-
name, _ = libfreqs._base_and_stride(name)
127+
name, _ = libfreqs.base_and_stride(name)
128128
delta = _get_offset(name) * stride
129129

130130
elif isinstance(freq, timedelta):

0 commit comments

Comments
 (0)