@@ -94,6 +94,66 @@ def _is_normalized(dt):
94
94
return False
95
95
return True
96
96
97
+ def _to_dt64 (dt , dtype = 'datetime64' ):
98
+ # Currently
99
+ # > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]')
100
+ # numpy.datetime64('2013-05-01T02:00:00.000000+0200')
101
+ # Thus astype is needed to cast datetime to datetime64[D]
102
+
103
+ if getattr (dt , 'tzinfo' , None ) is not None :
104
+ i8 = tslib .pydt_to_i8 (dt )
105
+ dt = tslib .tz_convert_single (i8 , 'UTC' , dt .tzinfo )
106
+ dt = Timestamp (dt )
107
+ dt = np .datetime64 (dt )
108
+ if dt .dtype .name != dtype :
109
+ dt = dt .astype (dtype )
110
+ return dt
111
+
112
+ def from_various_func (cls ,** kwds ):
113
+ """
114
+ Converts various holiday inputs to format readable for np.busdaycalendar
115
+ and creates an instance of CustomBusinessDay, CustomBusinessMonth...
116
+
117
+ Parameters
118
+ ----------
119
+ weekmask : str, Default 'Mon Tue Wed Thu Fri'
120
+ weekmask of valid business days, passed to ``numpy.busdaycalendar``
121
+ holidays : list
122
+ list/array of dates to exclude from the set of valid business days,
123
+ passed to ``numpy.busdaycalendar``
124
+ calendar : HolidayCalendar instance
125
+ instance of AbstractHolidayCalendar that provide the list of holidays
126
+ """
127
+ bdaycal_kwds = {}
128
+ if 'calendar' in kwds or 'holidays' in kwds : # generate holiday list
129
+ if 'calendar' in kwds :
130
+ holidays = kwds .pop ('calendar' ).holidays ()
131
+ else :
132
+ holidays = kwds .pop ('holidays' )
133
+ holidays = [_to_dt64 (dt , dtype = 'datetime64[D]' ) for dt in
134
+ holidays ]
135
+ holidays = tuple (sorted (holidays ))
136
+ bdaycal_kwds ['holidays' ] = holidays
137
+ if 'weekmask' in kwds :
138
+ bdaycal_kwds ['weekmask' ] = kwds .pop ('weekmask' ) # simply pass on to numpy
139
+
140
+ try :
141
+ busdaycalendar = np .busdaycalendar (** bdaycal_kwds )
142
+ except :
143
+ # Check we have the required numpy version
144
+ from distutils .version import LooseVersion
145
+
146
+ if LooseVersion (np .__version__ ) < '1.7.0' :
147
+ raise NotImplementedError ("CustomBusinessDay requires numpy >= "
148
+ "1.7.0. Current version: " +
149
+ np .__version__ )
150
+ else :
151
+ raise
152
+
153
+ return cls (busdaycalendar = busdaycalendar , ** kwds )
154
+
155
+
156
+
97
157
#----------------------------------------------------------------------
98
158
# DateOffset
99
159
@@ -344,17 +404,23 @@ def rollforward(self, dt):
344
404
return dt
345
405
346
406
def onOffset (self , dt ):
407
+ # print('in onOffset')
347
408
if self .normalize and not _is_normalized (dt ):
409
+ print ('here' )
348
410
return False
349
411
# XXX, see #1395
350
412
if type (self ) == DateOffset or isinstance (self , Tick ):
413
+ # print('Dateoffset')
351
414
return True
352
415
353
416
# Default (slow) method for determining if some date is a member of the
354
417
# date range generated by this offset. Subclasses may have this
355
418
# re-implemented in a nicer way.
356
419
a = dt
357
420
b = ((dt + self ) - self )
421
+ # print(self)
422
+ # print('a={}'.format(a))
423
+ # print('b={}'.format(b))
358
424
return a == b
359
425
360
426
# way to get around weirdness with rule_code
@@ -542,13 +608,9 @@ class CustomBusinessDay(BusinessDay):
542
608
offset : timedelta, default timedelta(0)
543
609
normalize : bool, default False
544
610
Normalize start/end dates to midnight before generating date range
545
- weekmask : str, Default 'Mon Tue Wed Thu Fri'
546
- weekmask of valid business days, passed to ``numpy.busdaycalendar``
547
- holidays : list
548
- list/array of dates to exclude from the set of valid business days,
549
- passed to ``numpy.busdaycalendar``
550
- calendar : HolidayCalendar instance
551
- instance of AbstractHolidayCalendar that provide the list of holidays
611
+ busdaycalendar: np.busdaycalendar instance
612
+ Use `CustomBusinessDay.from_various` to instantiate from weekmask, list of holidays
613
+ or `HolidayCalendar` instance.
552
614
"""
553
615
554
616
_cacheable = False
@@ -559,36 +621,9 @@ def __init__(self, n=1, normalize=False, **kwds):
559
621
self .normalize = normalize
560
622
self .kwds = kwds
561
623
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' , [])
568
- holidays = [self ._to_dt64 (dt , dtype = 'datetime64[D]' ) for dt in
569
- holidays ]
570
- self .holidays = tuple (sorted (holidays ))
571
- self .kwds ['holidays' ] = self .holidays
624
+ self .busdaycalendar = kwds .get ('busdaycalendar' , np .busdaycalendar ())
572
625
573
- self ._set_busdaycalendar ()
574
-
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
- try :
581
- self .busdaycalendar = np .busdaycalendar (** kwargs )
582
- except :
583
- # Check we have the required numpy version
584
- from distutils .version import LooseVersion
585
-
586
- if LooseVersion (np .__version__ ) < '1.7.0' :
587
- raise NotImplementedError ("CustomBusinessDay requires numpy >= "
588
- "1.7.0. Current version: " +
589
- np .__version__ )
590
- else :
591
- raise
626
+ from_various = classmethod (from_various_func )
592
627
593
628
def __getstate__ (self ):
594
629
"""Return a pickleable state"""
@@ -783,26 +818,26 @@ class CustomBusinessMonthEnd(BusinessMixin, MonthOffset):
783
818
offset : timedelta, default timedelta(0)
784
819
normalize : bool, default False
785
820
Normalize start/end dates to midnight before generating date range
786
- weekmask : str, Default 'Mon Tue Wed Thu Fri'
787
- weekmask of valid business days, passed to ``numpy.busdaycalendar``
788
- holidays : list
789
- list/array of dates to exclude from the set of valid business days,
790
- passed to ``numpy.busdaycalendar``
821
+ busdaycalendar: np.busdaycalendar instance
822
+ Use `CustomBusinessDay.from_various` to instantiate from weekmask, list of holidays
823
+ or `HolidayCalendar` instance.
791
824
"""
792
825
793
826
_cacheable = False
794
827
_prefix = 'CBM'
795
828
def __init__ (self , n = 1 , normalize = False , ** kwds ):
796
829
self .n = int (n )
797
830
self .normalize = normalize
798
- self .kwds = kwds
831
+ self .kwds = kwds . copy ()
799
832
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 ()
833
+ self .cbday = CustomBusinessDay (n = 1 , normalize = normalize , ** kwds )
834
+ self .busdaycalendar = kwds .pop ('busdaycalendar' , np .busdaycalendar ())
835
+ self .m_offset = MonthEnd (n = 1 , normalize = normalize , ** kwds )
836
+
837
+ from_various = classmethod (from_various_func )
803
838
804
839
@apply_wraps
805
- def apply (self ,other ):
840
+ def apply (self , other ):
806
841
n = self .n
807
842
# First move to month offset
808
843
cur_mend = self .m_offset .rollforward (other )
@@ -817,11 +852,11 @@ def apply(self,other):
817
852
n -= 1
818
853
elif other > cur_cmend and n <= - 1 :
819
854
n += 1
820
-
821
- new = cur_mend + n * MonthEnd ()
855
+
856
+ new = cur_mend + n * self . m_offset
822
857
result = self .cbday .rollback (new )
823
858
return result
824
-
859
+
825
860
class CustomBusinessMonthBegin (BusinessMixin , MonthOffset ):
826
861
"""
827
862
**EXPERIMENTAL** DateOffset of one custom business month
@@ -837,26 +872,26 @@ class CustomBusinessMonthBegin(BusinessMixin, MonthOffset):
837
872
offset : timedelta, default timedelta(0)
838
873
normalize : bool, default False
839
874
Normalize start/end dates to midnight before generating date range
840
- weekmask : str, Default 'Mon Tue Wed Thu Fri'
841
- weekmask of valid business days, passed to ``numpy.busdaycalendar``
842
- holidays : list
843
- list/array of dates to exclude from the set of valid business days,
844
- passed to ``numpy.busdaycalendar``
875
+ busdaycalendar: np.busdaycalendar instance
876
+ Use `CustomBusinessDay.from_various` to instantiate from weekmask, list of holidays
877
+ or `HolidayCalendar` instance.
845
878
"""
846
879
847
880
_cacheable = False
848
881
_prefix = 'CBMS'
849
882
def __init__ (self , n = 1 , normalize = False , ** kwds ):
850
883
self .n = int (n )
851
884
self .normalize = normalize
852
- self .kwds = kwds
885
+ self .kwds = kwds . copy ()
853
886
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 )
887
+ self .cbday = CustomBusinessDay (n = 1 , normalize = normalize , ** kwds )
888
+ self .busdaycalendar = kwds .pop ('busdaycalendar' , np .busdaycalendar ())
889
+ self .m_offset = MonthBegin (n = 1 , normalize = normalize , ** kwds )
890
+
891
+ from_various = classmethod (from_various_func )
857
892
858
893
@apply_wraps
859
- def apply (self ,other ):
894
+ def apply (self , other ):
860
895
n = self .n
861
896
dt_in = other
862
897
# First move to month offset
@@ -872,8 +907,8 @@ def apply(self,other):
872
907
n += 1
873
908
elif dt_in < cur_cmbegin and n >= 1 :
874
909
n -= 1
875
-
876
- new = cur_mbegin + n * MonthBegin ()
910
+
911
+ new = cur_mbegin + n * self . m_offset
877
912
result = self .cbday .rollforward (new )
878
913
return result
879
914
0 commit comments