@@ -413,8 +413,27 @@ def _from_name(cls, suffix=None):
413
413
return cls ()
414
414
415
415
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
+
416
435
class BusinessMixin (object ):
417
- """ mixin to business types to provide related functions """
436
+ """ Mixin to business types to provide related functions """
418
437
419
438
@property
420
439
def offset (self ):
@@ -572,9 +591,26 @@ def __init__(self, start='09:00', end='17:00', offset=timedelta(0)):
572
591
kwds = {'offset' : offset }
573
592
self .start = kwds ['start' ] = _validate_business_time (start )
574
593
self .end = kwds ['end' ] = _validate_business_time (end )
575
- self .kwds = kwds
594
+ self .kwds . update ( kwds )
576
595
self ._offset = offset
577
596
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
578
614
def _get_daytime_flag (self ):
579
615
if self .start == self .end :
580
616
raise ValueError ('start and end must not be the same' )
@@ -616,6 +652,7 @@ def _prev_opening_time(self, other):
616
652
return datetime (other .year , other .month , other .day ,
617
653
self .start .hour , self .start .minute )
618
654
655
+ # TODO: cache this once offsets are immutable
619
656
def _get_business_hours_by_sec (self ):
620
657
"""
621
658
Return business hours in a day by seconds.
@@ -784,19 +821,11 @@ def __init__(self, n=1, normalize=False, start='09:00',
784
821
end = '17:00' , offset = timedelta (0 )):
785
822
self .n = self ._validate_n (n )
786
823
self .normalize = normalize
824
+ self .kwds = {}
787
825
super (BusinessHour , self ).__init__ (start = start , end = end , offset = offset )
788
826
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
-
798
827
799
- class CustomBusinessDay (BusinessDay ):
828
+ class CustomBusinessDay (_CustomMixin , BusinessDay ):
800
829
"""
801
830
DateOffset subclass representing possibly n custom business days,
802
831
excluding holidays
@@ -822,19 +851,9 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
822
851
self .n = self ._validate_n (n )
823
852
self .normalize = normalize
824
853
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 }
833
855
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 )
838
857
839
858
@apply_wraps
840
859
def apply (self , other ):
@@ -874,7 +893,8 @@ def onOffset(self, dt):
874
893
return np .is_busday (day64 , busdaycal = self .calendar )
875
894
876
895
877
- class CustomBusinessHour (BusinessHourMixin , SingleConstructorOffset ):
896
+ class CustomBusinessHour (_CustomMixin , BusinessHourMixin ,
897
+ SingleConstructorOffset ):
878
898
"""
879
899
DateOffset subclass representing possibly n custom business days
880
900
@@ -889,27 +909,11 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
889
909
start = '09:00' , end = '17:00' , offset = timedelta (0 )):
890
910
self .n = self ._validate_n (n )
891
911
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 }
901
914
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 )
913
917
914
918
915
919
# ---------------------------------------------------------------------
@@ -981,10 +985,10 @@ class BusinessMonthBegin(MonthOffset):
981
985
_day_opt = 'business_start'
982
986
983
987
984
- class CustomBusinessMonthEnd ( BusinessMixin , MonthOffset ):
988
+ class _CustomBusinessMonth ( _CustomMixin , BusinessMixin , MonthOffset ):
985
989
"""
986
990
DateOffset subclass representing one custom business month, incrementing
987
- between end of month dates
991
+ between [BEGIN/END] of month dates
988
992
989
993
Parameters
990
994
----------
@@ -999,27 +1003,19 @@ class CustomBusinessMonthEnd(BusinessMixin, MonthOffset):
999
1003
passed to ``numpy.busdaycalendar``
1000
1004
calendar : pd.HolidayCalendar or np.busdaycalendar
1001
1005
"""
1002
-
1003
1006
_cacheable = False
1004
- _prefix = 'CBM'
1005
1007
1006
- onOffset = DateOffset .onOffset # override MonthOffset method
1008
+ onOffset = DateOffset .onOffset # override MonthOffset method
1007
1009
apply_index = DateOffset .apply_index # override MonthOffset method
1008
1010
1009
1011
def __init__ (self , n = 1 , normalize = False , weekmask = 'Mon Tue Wed Thu Fri' ,
1010
1012
holidays = None , calendar = None , offset = timedelta (0 )):
1011
1013
self .n = self ._validate_n (n )
1012
1014
self .normalize = normalize
1013
1015
self ._offset = offset
1014
- self .kwds = {}
1016
+ self .kwds = {'offset' : offset }
1015
1017
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 )
1023
1019
1024
1020
@cache_readonly
1025
1021
def cbday (self ):
@@ -1028,7 +1024,17 @@ def cbday(self):
1028
1024
1029
1025
@cache_readonly
1030
1026
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'
1032
1038
1033
1039
@apply_wraps
1034
1040
def apply (self , other ):
@@ -1054,57 +1060,10 @@ def apply(self, other):
1054
1060
return result
1055
1061
1056
1062
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' )
1077
1065
_prefix = 'CBMS'
1078
1066
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
-
1108
1067
@apply_wraps
1109
1068
def apply (self , other ):
1110
1069
n = self .n
@@ -1707,13 +1666,16 @@ def onOffset(self, dt):
1707
1666
return dt .month == self .month and dt .day == self ._get_offset_day (dt )
1708
1667
1709
1668
def __init__ (self , n = 1 , normalize = False , month = None ):
1669
+ self .n = self ._validate_n (n )
1670
+ self .normalize = normalize
1671
+
1710
1672
month = month if month is not None else self ._default_month
1711
1673
self .month = month
1712
1674
1713
1675
if self .month < 1 or self .month > 12 :
1714
1676
raise ValueError ('Month must go from 1 to 12' )
1715
1677
1716
- DateOffset . __init__ ( self , n = n , normalize = normalize , month = month )
1678
+ self . kwds = { 'month' : month }
1717
1679
1718
1680
@classmethod
1719
1681
def _from_name (cls , suffix = None ):
0 commit comments