@@ -225,12 +225,12 @@ def _should_cache(self):
225
225
return self .isAnchored () and self ._cacheable
226
226
227
227
def _params (self ):
228
- attrs = [(k , v ) for k , v in compat .iteritems (vars (self ))
229
- if (k not in ['kwds' , 'name' , 'normalize' ,
230
- 'busdaycalendar' ]) and (k [0 ] != '_' )]
231
- attrs .extend (list (self .kwds .items ()))
228
+ all_paras = dict (list (vars (self ).items ()) + list (self .kwds .items ()))
229
+ if 'holidays' in all_paras and not all_paras ['holidays' ]:
230
+ all_paras .pop ('holidays' )
231
+ exclude = ['kwds' , 'name' ,'normalize' , 'calendar' ]
232
+ attrs = [(k , v ) for k , v in all_paras .items () if (k not in exclude ) and (k [0 ] != '_' )]
232
233
attrs = sorted (set (attrs ))
233
-
234
234
params = tuple ([str (self .__class__ )] + attrs )
235
235
return params
236
236
@@ -547,38 +547,57 @@ class CustomBusinessDay(BusinessDay):
547
547
holidays : list
548
548
list/array of dates to exclude from the set of valid business days,
549
549
passed to ``numpy.busdaycalendar``
550
- calendar : HolidayCalendar instance
551
- instance of AbstractHolidayCalendar that provide the list of holidays
550
+ calendar : pd.HolidayCalendar or np.busdaycalendar
552
551
"""
553
-
554
552
_cacheable = False
555
553
_prefix = 'C'
556
554
557
- def __init__ (self , n = 1 , normalize = False , ** kwds ):
555
+ def __init__ (self , n = 1 , normalize = False , weekmask = 'Mon Tue Wed Thu Fri' ,
556
+ holidays = None , calendar = None , ** kwds ):
558
557
self .n = int (n )
559
558
self .normalize = normalize
560
559
self .kwds = kwds
561
560
self .offset = kwds .get ('offset' , timedelta (0 ))
562
- self .weekmask = kwds .get ('weekmask' , 'Mon Tue Wed Thu Fri' )
563
-
564
- if 'calendar' in kwds :
565
- holidays = kwds ['calendar' ].holidays ()
566
- else :
567
- holidays = kwds .get ('holidays' , [])
561
+ calendar , holidays = self .get_calendar (weekmask = weekmask ,
562
+ holidays = holidays ,
563
+ calendar = calendar )
564
+ # CustomBusinessDay instances are identified by the
565
+ # following two attributes. See DateOffset._params()
566
+ # holidays, weekmask
567
+
568
+ self .kwds ['weekmask' ] = self .weekmask = weekmask
569
+ self .kwds ['holidays' ] = self .holidays = holidays
570
+ self .kwds ['calendar' ] = self .calendar = calendar
571
+
572
+ def get_calendar (self , weekmask , holidays , calendar ):
573
+ '''Generate busdaycalendar'''
574
+ if isinstance (calendar , np .busdaycalendar ):
575
+ if not holidays :
576
+ holidays = tuple (calendar .holidays )
577
+ elif not isinstance (holidays , tuple ):
578
+ holidays = tuple (holidays )
579
+ else :
580
+ # trust that calendar.holidays and holidays are
581
+ # consistent
582
+ pass
583
+ return calendar , holidays
584
+
585
+ if holidays is None :
586
+ holidays = []
587
+ try :
588
+ holidays = holidays + calendar .holidays ().tolist ()
589
+ except AttributeError :
590
+ pass
568
591
holidays = [self ._to_dt64 (dt , dtype = 'datetime64[D]' ) for dt in
569
592
holidays ]
570
- self .holidays = tuple (sorted (holidays ))
571
- self .kwds ['holidays' ] = self .holidays
593
+ holidays = tuple (sorted (holidays ))
572
594
573
- self ._set_busdaycalendar ()
595
+ kwargs = {'weekmask' : weekmask }
596
+ if holidays :
597
+ kwargs ['holidays' ] = holidays
574
598
575
- def _set_busdaycalendar (self ):
576
- if self .holidays :
577
- kwargs = {'weekmask' :self .weekmask ,'holidays' :self .holidays }
578
- else :
579
- kwargs = {'weekmask' :self .weekmask }
580
599
try :
581
- self . busdaycalendar = np .busdaycalendar (** kwargs )
600
+ busdaycalendar = np .busdaycalendar (** kwargs )
582
601
except :
583
602
# Check we have the required numpy version
584
603
from distutils .version import LooseVersion
@@ -589,17 +608,23 @@ def _set_busdaycalendar(self):
589
608
np .__version__ )
590
609
else :
591
610
raise
611
+ return busdaycalendar , holidays
592
612
593
613
def __getstate__ (self ):
594
614
"""Return a pickleable state"""
595
615
state = self .__dict__ .copy ()
596
- del state ['busdaycalendar ' ]
616
+ del state ['calendar ' ]
597
617
return state
598
618
599
619
def __setstate__ (self , state ):
600
620
"""Reconstruct an instance from a pickled state"""
601
621
self .__dict__ = state
602
- self ._set_busdaycalendar ()
622
+ calendar , holidays = self .get_calendar (weekmask = self .weekmask ,
623
+ holidays = self .holidays ,
624
+ calendar = None )
625
+ self .kwds ['calendar' ] = self .calendar = calendar
626
+ self .kwds ['holidays' ] = self .holidays = holidays
627
+ self .kwds ['weekmask' ] = state ['weekmask' ]
603
628
604
629
@apply_wraps
605
630
def apply (self , other ):
@@ -613,7 +638,7 @@ def apply(self, other):
613
638
np_dt = np .datetime64 (date_in .date ())
614
639
615
640
np_incr_dt = np .busday_offset (np_dt , self .n , roll = roll ,
616
- busdaycal = self .busdaycalendar )
641
+ busdaycal = self .calendar )
617
642
618
643
dt_date = np_incr_dt .astype (datetime )
619
644
result = datetime .combine (dt_date , date_in .time ())
@@ -635,7 +660,6 @@ def _to_dt64(dt, dtype='datetime64'):
635
660
# > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]')
636
661
# numpy.datetime64('2013-05-01T02:00:00.000000+0200')
637
662
# Thus astype is needed to cast datetime to datetime64[D]
638
-
639
663
if getattr (dt , 'tzinfo' , None ) is not None :
640
664
i8 = tslib .pydt_to_i8 (dt )
641
665
dt = tslib .tz_convert_single (i8 , 'UTC' , dt .tzinfo )
@@ -649,7 +673,7 @@ def onOffset(self, dt):
649
673
if self .normalize and not _is_normalized (dt ):
650
674
return False
651
675
day64 = self ._to_dt64 (dt ,'datetime64[D]' )
652
- return np .is_busday (day64 , busdaycal = self .busdaycalendar )
676
+ return np .is_busday (day64 , busdaycal = self .calendar )
653
677
654
678
655
679
class MonthOffset (SingleConstructorOffset ):
@@ -767,7 +791,6 @@ def onOffset(self, dt):
767
791
_prefix = 'BMS'
768
792
769
793
770
-
771
794
class CustomBusinessMonthEnd (BusinessMixin , MonthOffset ):
772
795
"""
773
796
**EXPERIMENTAL** DateOffset of one custom business month
@@ -788,18 +811,22 @@ class CustomBusinessMonthEnd(BusinessMixin, MonthOffset):
788
811
holidays : list
789
812
list/array of dates to exclude from the set of valid business days,
790
813
passed to ``numpy.busdaycalendar``
814
+ calendar : pd.HolidayCalendar or np.busdaycalendar
791
815
"""
792
816
793
817
_cacheable = False
794
818
_prefix = 'CBM'
795
- def __init__ (self , n = 1 , normalize = False , ** kwds ):
819
+ def __init__ (self , n = 1 , normalize = False , weekmask = 'Mon Tue Wed Thu Fri' ,
820
+ holidays = None , calendar = None , ** kwds ):
796
821
self .n = int (n )
797
822
self .normalize = normalize
798
823
self .kwds = kwds
799
824
self .offset = kwds .get ('offset' , timedelta (0 ))
800
- self .weekmask = kwds .get ('weekmask' , 'Mon Tue Wed Thu Fri' )
801
- self .cbday = CustomBusinessDay (n = self .n , ** kwds )
802
- self .m_offset = MonthEnd ()
825
+ self .cbday = CustomBusinessDay (n = self .n , normalize = normalize ,
826
+ weekmask = weekmask , holidays = holidays ,
827
+ calendar = calendar , ** kwds )
828
+ self .m_offset = MonthEnd (n = 1 , normalize = normalize , ** kwds )
829
+ self .kwds ['calendar' ] = self .cbday .calendar # cache numpy calendar
803
830
804
831
@apply_wraps
805
832
def apply (self ,other ):
@@ -817,11 +844,11 @@ def apply(self,other):
817
844
n -= 1
818
845
elif other > cur_cmend and n <= - 1 :
819
846
n += 1
820
-
821
- new = cur_mend + n * MonthEnd ()
847
+
848
+ new = cur_mend + n * self . m_offset
822
849
result = self .cbday .rollback (new )
823
850
return result
824
-
851
+
825
852
class CustomBusinessMonthBegin (BusinessMixin , MonthOffset ):
826
853
"""
827
854
**EXPERIMENTAL** DateOffset of one custom business month
@@ -842,18 +869,22 @@ class CustomBusinessMonthBegin(BusinessMixin, MonthOffset):
842
869
holidays : list
843
870
list/array of dates to exclude from the set of valid business days,
844
871
passed to ``numpy.busdaycalendar``
872
+ calendar : pd.HolidayCalendar or np.busdaycalendar
845
873
"""
846
874
847
875
_cacheable = False
848
876
_prefix = 'CBMS'
849
- def __init__ (self , n = 1 , normalize = False , ** kwds ):
877
+ def __init__ (self , n = 1 , normalize = False , weekmask = 'Mon Tue Wed Thu Fri' ,
878
+ holidays = None , calendar = None , ** kwds ):
850
879
self .n = int (n )
851
880
self .normalize = normalize
852
881
self .kwds = kwds
853
882
self .offset = kwds .get ('offset' , timedelta (0 ))
854
- self .weekmask = kwds .get ('weekmask' , 'Mon Tue Wed Thu Fri' )
855
- self .cbday = CustomBusinessDay (n = self .n , normalize = normalize , ** kwds )
856
- self .m_offset = MonthBegin (normalize = normalize )
883
+ self .cbday = CustomBusinessDay (n = self .n , normalize = normalize ,
884
+ weekmask = weekmask , holidays = holidays ,
885
+ calendar = calendar , ** kwds )
886
+ self .m_offset = MonthBegin (n = 1 , normalize = normalize , ** kwds )
887
+ self .kwds ['calendar' ] = self .cbday .calendar # cache numpy calendar
857
888
858
889
@apply_wraps
859
890
def apply (self ,other ):
@@ -872,8 +903,8 @@ def apply(self,other):
872
903
n += 1
873
904
elif dt_in < cur_cmbegin and n >= 1 :
874
905
n -= 1
875
-
876
- new = cur_mbegin + n * MonthBegin ()
906
+
907
+ new = cur_mbegin + n * self . m_offset
877
908
result = self .cbday .rollforward (new )
878
909
return result
879
910
0 commit comments