@@ -413,8 +413,27 @@ def _from_name(cls, suffix=None):
413413 return cls ()
414414
415415
416+ class _CustomMixin (object ):
417+ """
418+ Mixin for classes that define and validate calendar, holidays,
419+ and weekdays attributes
420+ """
421+ def __init__ (self , weekmask , holidays , calendar ):
422+ calendar , holidays = _get_calendar (weekmask = weekmask ,
423+ holidays = holidays ,
424+ calendar = calendar )
425+ # Custom offset instances are identified by the
426+ # following two attributes. See DateOffset._params()
427+ # holidays, weekmask
428+
429+ # assumes self.kwds already exists
430+ self .kwds ['weekmask' ] = self .weekmask = weekmask
431+ self .kwds ['holidays' ] = self .holidays = holidays
432+ self .kwds ['calendar' ] = self .calendar = calendar
433+
434+
416435class BusinessMixin (object ):
417- """ mixin to business types to provide related functions """
436+ """ Mixin to business types to provide related functions """
418437
419438 @property
420439 def offset (self ):
@@ -572,9 +591,26 @@ def __init__(self, start='09:00', end='17:00', offset=timedelta(0)):
572591 kwds = {'offset' : offset }
573592 self .start = kwds ['start' ] = _validate_business_time (start )
574593 self .end = kwds ['end' ] = _validate_business_time (end )
575- self .kwds = kwds
594+ self .kwds . update ( kwds )
576595 self ._offset = offset
577596
597+ @cache_readonly
598+ def next_bday (self ):
599+ """used for moving to next businessday"""
600+ if self .n >= 0 :
601+ nb_offset = 1
602+ else :
603+ nb_offset = - 1
604+ if self ._prefix .startswith ('C' ):
605+ # CustomBusinessHour
606+ return CustomBusinessDay (n = nb_offset ,
607+ weekmask = self .weekmask ,
608+ holidays = self .holidays ,
609+ calendar = self .calendar )
610+ else :
611+ return BusinessDay (n = nb_offset )
612+
613+ # TODO: Cache this once offsets are immutable
578614 def _get_daytime_flag (self ):
579615 if self .start == self .end :
580616 raise ValueError ('start and end must not be the same' )
@@ -616,6 +652,7 @@ def _prev_opening_time(self, other):
616652 return datetime (other .year , other .month , other .day ,
617653 self .start .hour , self .start .minute )
618654
655+ # TODO: cache this once offsets are immutable
619656 def _get_business_hours_by_sec (self ):
620657 """
621658 Return business hours in a day by seconds.
@@ -784,19 +821,11 @@ def __init__(self, n=1, normalize=False, start='09:00',
784821 end = '17:00' , offset = timedelta (0 )):
785822 self .n = self ._validate_n (n )
786823 self .normalize = normalize
824+ self .kwds = {}
787825 super (BusinessHour , self ).__init__ (start = start , end = end , offset = offset )
788826
789- @cache_readonly
790- def next_bday (self ):
791- # used for moving to next businessday
792- if self .n >= 0 :
793- nb_offset = 1
794- else :
795- nb_offset = - 1
796- return BusinessDay (n = nb_offset )
797-
798827
799- class CustomBusinessDay (BusinessDay ):
828+ class CustomBusinessDay (_CustomMixin , BusinessDay ):
800829 """
801830 DateOffset subclass representing possibly n custom business days,
802831 excluding holidays
@@ -822,19 +851,9 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
822851 self .n = self ._validate_n (n )
823852 self .normalize = normalize
824853 self ._offset = offset
825- self .kwds = {}
826-
827- calendar , holidays = _get_calendar (weekmask = weekmask ,
828- holidays = holidays ,
829- calendar = calendar )
830- # CustomBusinessDay instances are identified by the
831- # following two attributes. See DateOffset._params()
832- # holidays, weekmask
854+ self .kwds = {'offset' : offset }
833855
834- self .kwds ['weekmask' ] = self .weekmask = weekmask
835- self .kwds ['holidays' ] = self .holidays = holidays
836- self .kwds ['calendar' ] = self .calendar = calendar
837- self .kwds ['offset' ] = offset
856+ _CustomMixin .__init__ (self , weekmask , holidays , calendar )
838857
839858 @apply_wraps
840859 def apply (self , other ):
@@ -874,7 +893,8 @@ def onOffset(self, dt):
874893 return np .is_busday (day64 , busdaycal = self .calendar )
875894
876895
877- class CustomBusinessHour (BusinessHourMixin , SingleConstructorOffset ):
896+ class CustomBusinessHour (_CustomMixin , BusinessHourMixin ,
897+ SingleConstructorOffset ):
878898 """
879899 DateOffset subclass representing possibly n custom business days
880900
@@ -889,27 +909,11 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
889909 start = '09:00' , end = '17:00' , offset = timedelta (0 )):
890910 self .n = self ._validate_n (n )
891911 self .normalize = normalize
892- super (CustomBusinessHour , self ).__init__ (start = start ,
893- end = end , offset = offset )
894-
895- calendar , holidays = _get_calendar (weekmask = weekmask ,
896- holidays = holidays ,
897- calendar = calendar )
898- self .kwds ['weekmask' ] = self .weekmask = weekmask
899- self .kwds ['holidays' ] = self .holidays = holidays
900- self .kwds ['calendar' ] = self .calendar = calendar
912+ self ._offset = offset
913+ self .kwds = {'offset' : offset }
901914
902- @cache_readonly
903- def next_bday (self ):
904- # used for moving to next businessday
905- if self .n >= 0 :
906- nb_offset = 1
907- else :
908- nb_offset = - 1
909- return CustomBusinessDay (n = nb_offset ,
910- weekmask = self .weekmask ,
911- holidays = self .holidays ,
912- calendar = self .calendar )
915+ _CustomMixin .__init__ (self , weekmask , holidays , calendar )
916+ BusinessHourMixin .__init__ (self , start = start , end = end , offset = offset )
913917
914918
915919# ---------------------------------------------------------------------
@@ -981,10 +985,10 @@ class BusinessMonthBegin(MonthOffset):
981985 _day_opt = 'business_start'
982986
983987
984- class CustomBusinessMonthEnd ( BusinessMixin , MonthOffset ):
988+ class _CustomBusinessMonth ( _CustomMixin , BusinessMixin , MonthOffset ):
985989 """
986990 DateOffset subclass representing one custom business month, incrementing
987- between end of month dates
991+ between [BEGIN/END] of month dates
988992
989993 Parameters
990994 ----------
@@ -999,27 +1003,19 @@ class CustomBusinessMonthEnd(BusinessMixin, MonthOffset):
9991003 passed to ``numpy.busdaycalendar``
10001004 calendar : pd.HolidayCalendar or np.busdaycalendar
10011005 """
1002-
10031006 _cacheable = False
1004- _prefix = 'CBM'
10051007
1006- onOffset = DateOffset .onOffset # override MonthOffset method
1008+ onOffset = DateOffset .onOffset # override MonthOffset method
10071009 apply_index = DateOffset .apply_index # override MonthOffset method
10081010
10091011 def __init__ (self , n = 1 , normalize = False , weekmask = 'Mon Tue Wed Thu Fri' ,
10101012 holidays = None , calendar = None , offset = timedelta (0 )):
10111013 self .n = self ._validate_n (n )
10121014 self .normalize = normalize
10131015 self ._offset = offset
1014- self .kwds = {}
1016+ self .kwds = {'offset' : offset }
10151017
1016- calendar , holidays = _get_calendar (weekmask = weekmask ,
1017- holidays = holidays ,
1018- calendar = calendar )
1019- self .kwds ['weekmask' ] = self .weekmask = weekmask
1020- self .kwds ['holidays' ] = self .holidays = holidays
1021- self .kwds ['calendar' ] = self .calendar = calendar
1022- self .kwds ['offset' ] = offset
1018+ _CustomMixin .__init__ (self , weekmask , holidays , calendar )
10231019
10241020 @cache_readonly
10251021 def cbday (self ):
@@ -1028,7 +1024,17 @@ def cbday(self):
10281024
10291025 @cache_readonly
10301026 def m_offset (self ):
1031- return MonthEnd (n = 1 , normalize = self .normalize )
1027+ if self ._prefix .endswith ('S' ):
1028+ # MonthBegin:
1029+ return MonthBegin (n = 1 , normalize = self .normalize )
1030+ else :
1031+ # MonthEnd
1032+ return MonthEnd (n = 1 , normalize = self .normalize )
1033+
1034+
1035+ class CustomBusinessMonthEnd (_CustomBusinessMonth ):
1036+ __doc__ = _CustomBusinessMonth .__doc__ .replace ('[BEGIN/END]' , 'end' )
1037+ _prefix = 'CBM'
10321038
10331039 @apply_wraps
10341040 def apply (self , other ):
@@ -1054,57 +1060,10 @@ def apply(self, other):
10541060 return result
10551061
10561062
1057- class CustomBusinessMonthBegin (BusinessMixin , MonthOffset ):
1058- """
1059- DateOffset subclass representing one custom business month, incrementing
1060- between beginning of month dates
1061-
1062- Parameters
1063- ----------
1064- n : int, default 1
1065- offset : timedelta, default timedelta(0)
1066- normalize : bool, default False
1067- Normalize start/end dates to midnight before generating date range
1068- weekmask : str, Default 'Mon Tue Wed Thu Fri'
1069- weekmask of valid business days, passed to ``numpy.busdaycalendar``
1070- holidays : list
1071- list/array of dates to exclude from the set of valid business days,
1072- passed to ``numpy.busdaycalendar``
1073- calendar : pd.HolidayCalendar or np.busdaycalendar
1074- """
1075-
1076- _cacheable = False
1063+ class CustomBusinessMonthBegin (_CustomBusinessMonth ):
1064+ __doc__ = _CustomBusinessMonth .__doc__ .replace ('[BEGIN/END]' , 'beginning' )
10771065 _prefix = 'CBMS'
10781066
1079- onOffset = DateOffset .onOffset # override MonthOffset method
1080- apply_index = DateOffset .apply_index # override MonthOffset method
1081-
1082- def __init__ (self , n = 1 , normalize = False , weekmask = 'Mon Tue Wed Thu Fri' ,
1083- holidays = None , calendar = None , offset = timedelta (0 )):
1084- self .n = self ._validate_n (n )
1085- self .normalize = normalize
1086- self ._offset = offset
1087- self .kwds = {}
1088-
1089- # _get_calendar does validation and possible transformation
1090- # of calendar and holidays.
1091- calendar , holidays = _get_calendar (weekmask = weekmask ,
1092- holidays = holidays ,
1093- calendar = calendar )
1094- self .kwds ['calendar' ] = self .calendar = calendar
1095- self .kwds ['weekmask' ] = self .weekmask = weekmask
1096- self .kwds ['holidays' ] = self .holidays = holidays
1097- self .kwds ['offset' ] = offset
1098-
1099- @cache_readonly
1100- def cbday (self ):
1101- kwds = self .kwds
1102- return CustomBusinessDay (n = self .n , normalize = self .normalize , ** kwds )
1103-
1104- @cache_readonly
1105- def m_offset (self ):
1106- return MonthBegin (n = 1 , normalize = self .normalize )
1107-
11081067 @apply_wraps
11091068 def apply (self , other ):
11101069 n = self .n
@@ -1707,13 +1666,16 @@ def onOffset(self, dt):
17071666 return dt .month == self .month and dt .day == self ._get_offset_day (dt )
17081667
17091668 def __init__ (self , n = 1 , normalize = False , month = None ):
1669+ self .n = self ._validate_n (n )
1670+ self .normalize = normalize
1671+
17101672 month = month if month is not None else self ._default_month
17111673 self .month = month
17121674
17131675 if self .month < 1 or self .month > 12 :
17141676 raise ValueError ('Month must go from 1 to 12' )
17151677
1716- DateOffset . __init__ ( self , n = n , normalize = normalize , month = month )
1678+ self . kwds = { 'month' : month }
17171679
17181680 @classmethod
17191681 def _from_name (cls , suffix = None ):
0 commit comments