@@ -2,6 +2,7 @@ import cython
2
2
3
3
import time
4
4
from typing import Any
5
+ import warnings
5
6
from cpython.datetime cimport (PyDateTime_IMPORT,
6
7
PyDateTime_Check,
7
8
PyDelta_Check,
@@ -103,7 +104,7 @@ def as_datetime(obj):
103
104
return obj
104
105
105
106
106
- cpdef bint _is_normalized (dt):
107
+ cpdef bint is_normalized (dt):
107
108
if (dt.hour != 0 or dt.minute != 0 or dt.second != 0 or
108
109
dt.microsecond != 0 or getattr (dt, ' nanosecond' , 0 ) != 0 ):
109
110
return False
@@ -230,7 +231,7 @@ def _get_calendar(weekmask, holidays, calendar):
230
231
holidays = holidays + calendar.holidays().tolist()
231
232
except AttributeError :
232
233
pass
233
- holidays = [_to_dt64D (dt) for dt in holidays]
234
+ holidays = [to_dt64D (dt) for dt in holidays]
234
235
holidays = tuple (sorted (holidays))
235
236
236
237
kwargs = {' weekmask' : weekmask}
@@ -241,7 +242,7 @@ def _get_calendar(weekmask, holidays, calendar):
241
242
return busdaycalendar, holidays
242
243
243
244
244
- def _to_dt64D (dt ):
245
+ def to_dt64D (dt ):
245
246
# Currently
246
247
# > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]')
247
248
# numpy.datetime64('2013-05-01T02:00:00.000000+0200')
@@ -264,7 +265,7 @@ def _to_dt64D(dt):
264
265
# Validation
265
266
266
267
267
- def _validate_business_time (t_input ):
268
+ def validate_business_time (t_input ):
268
269
if isinstance (t_input, str ):
269
270
try :
270
271
t = time.strptime(t_input, ' %H :%M ' )
@@ -440,6 +441,9 @@ class _BaseOffset:
440
441
# that allows us to use methods that can go in a `cdef class`
441
442
return self * 1
442
443
444
+ # ------------------------------------------------------------------
445
+ # Name and Rendering Methods
446
+
443
447
def __repr__ (self ) -> str:
444
448
className = getattr (self , ' _outputName' , type (self ).__name__)
445
449
@@ -455,6 +459,44 @@ class _BaseOffset:
455
459
out = f' <{n_str}{className}{plural}{self._repr_attrs()}>'
456
460
return out
457
461
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
+
458
500
def _get_offset_day(self , datetime other ):
459
501
# subclass must implement `_day_opt`; calling from the base class
460
502
# will raise NotImplementedError.
@@ -530,6 +572,26 @@ class _BaseOffset:
530
572
531
573
return state
532
574
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
+
533
595
534
596
class BaseOffset(_BaseOffset ):
535
597
# Here we add __rfoo__ methods that don't play well with cdef classes
@@ -564,6 +626,49 @@ class _Tick:
564
626
return _wrap_timedelta_result(result)
565
627
566
628
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
+
567
672
# ----------------------------------------------------------------------
568
673
# RelativeDelta Arithmetic
569
674
0 commit comments