11
11
from dateutil .relativedelta import relativedelta , weekday
12
12
from dateutil .easter import easter
13
13
from pandas ._libs import tslib , Timestamp , OutOfBoundsDatetime , Timedelta
14
+ from pandas .util ._decorators import cache_readonly
14
15
15
16
import functools
16
17
import operator
@@ -573,9 +574,9 @@ def __setstate__(self, state):
573
574
"""Reconstruct an instance from a pickled state"""
574
575
self .__dict__ = state
575
576
if 'weekmask' in state and 'holidays' in state :
576
- calendar , holidays = self . get_calendar (weekmask = self .weekmask ,
577
- holidays = self .holidays ,
578
- calendar = None )
577
+ calendar , holidays = _get_calendar (weekmask = self .weekmask ,
578
+ holidays = self .holidays ,
579
+ calendar = None )
579
580
self .kwds ['calendar' ] = self .calendar = calendar
580
581
self .kwds ['holidays' ] = self .holidays = holidays
581
582
self .kwds ['weekmask' ] = state ['weekmask' ]
@@ -978,9 +979,9 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
978
979
self .normalize = normalize
979
980
self .kwds = kwds
980
981
self .offset = kwds .get ('offset' , timedelta (0 ))
981
- calendar , holidays = self . get_calendar (weekmask = weekmask ,
982
- holidays = holidays ,
983
- calendar = calendar )
982
+ calendar , holidays = _get_calendar (weekmask = weekmask ,
983
+ holidays = holidays ,
984
+ calendar = calendar )
984
985
# CustomBusinessDay instances are identified by the
985
986
# following two attributes. See DateOffset._params()
986
987
# holidays, weekmask
@@ -989,36 +990,6 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
989
990
self .kwds ['holidays' ] = self .holidays = holidays
990
991
self .kwds ['calendar' ] = self .calendar = calendar
991
992
992
- def get_calendar (self , weekmask , holidays , calendar ):
993
- """Generate busdaycalendar"""
994
- if isinstance (calendar , np .busdaycalendar ):
995
- if not holidays :
996
- holidays = tuple (calendar .holidays )
997
- elif not isinstance (holidays , tuple ):
998
- holidays = tuple (holidays )
999
- else :
1000
- # trust that calendar.holidays and holidays are
1001
- # consistent
1002
- pass
1003
- return calendar , holidays
1004
-
1005
- if holidays is None :
1006
- holidays = []
1007
- try :
1008
- holidays = holidays + calendar .holidays ().tolist ()
1009
- except AttributeError :
1010
- pass
1011
- holidays = [self ._to_dt64 (dt , dtype = 'datetime64[D]' ) for dt in
1012
- holidays ]
1013
- holidays = tuple (sorted (holidays ))
1014
-
1015
- kwargs = {'weekmask' : weekmask }
1016
- if holidays :
1017
- kwargs ['holidays' ] = holidays
1018
-
1019
- busdaycalendar = np .busdaycalendar (** kwargs )
1020
- return busdaycalendar , holidays
1021
-
1022
993
@apply_wraps
1023
994
def apply (self , other ):
1024
995
if self .n <= 0 :
@@ -1050,25 +1021,10 @@ def apply(self, other):
1050
1021
def apply_index (self , i ):
1051
1022
raise NotImplementedError
1052
1023
1053
- @staticmethod
1054
- def _to_dt64 (dt , dtype = 'datetime64' ):
1055
- # Currently
1056
- # > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]')
1057
- # numpy.datetime64('2013-05-01T02:00:00.000000+0200')
1058
- # Thus astype is needed to cast datetime to datetime64[D]
1059
- if getattr (dt , 'tzinfo' , None ) is not None :
1060
- i8 = tslib .pydt_to_i8 (dt )
1061
- dt = tslib .tz_convert_single (i8 , 'UTC' , dt .tzinfo )
1062
- dt = Timestamp (dt )
1063
- dt = np .datetime64 (dt )
1064
- if dt .dtype .name != dtype :
1065
- dt = dt .astype (dtype )
1066
- return dt
1067
-
1068
1024
def onOffset (self , dt ):
1069
1025
if self .normalize and not _is_normalized (dt ):
1070
1026
return False
1071
- day64 = self . _to_dt64 (dt , 'datetime64[D]' )
1027
+ day64 = _to_dt64 (dt , 'datetime64[D]' )
1072
1028
return np .is_busday (day64 , busdaycal = self .calendar )
1073
1029
1074
1030
@@ -1087,19 +1043,25 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
1087
1043
self .n = int (n )
1088
1044
self .normalize = normalize
1089
1045
super (CustomBusinessHour , self ).__init__ (** kwds )
1046
+
1047
+ calendar , holidays = _get_calendar (weekmask = weekmask ,
1048
+ holidays = holidays ,
1049
+ calendar = calendar )
1050
+ self .kwds ['weekmask' ] = self .weekmask = weekmask
1051
+ self .kwds ['holidays' ] = self .holidays = holidays
1052
+ self .kwds ['calendar' ] = self .calendar = calendar
1053
+
1054
+ @cache_readonly
1055
+ def next_bday (self ):
1090
1056
# used for moving to next businessday
1091
1057
if self .n >= 0 :
1092
1058
nb_offset = 1
1093
1059
else :
1094
1060
nb_offset = - 1
1095
- self .next_bday = CustomBusinessDay (n = nb_offset ,
1096
- weekmask = weekmask ,
1097
- holidays = holidays ,
1098
- calendar = calendar )
1099
-
1100
- self .kwds ['weekmask' ] = self .next_bday .weekmask
1101
- self .kwds ['holidays' ] = self .next_bday .holidays
1102
- self .kwds ['calendar' ] = self .next_bday .calendar
1061
+ return CustomBusinessDay (n = nb_offset ,
1062
+ weekmask = self .weekmask ,
1063
+ holidays = self .holidays ,
1064
+ calendar = self .calendar )
1103
1065
1104
1066
1105
1067
class MonthOffset (SingleConstructorOffset ):
@@ -1471,11 +1433,25 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
1471
1433
self .normalize = normalize
1472
1434
self .kwds = kwds
1473
1435
self .offset = kwds .get ('offset' , timedelta (0 ))
1474
- self .cbday = CustomBusinessDay (n = self .n , normalize = normalize ,
1475
- weekmask = weekmask , holidays = holidays ,
1476
- calendar = calendar , ** kwds )
1477
- self .m_offset = MonthEnd (n = 1 , normalize = normalize , ** kwds )
1478
- self .kwds ['calendar' ] = self .cbday .calendar # cache numpy calendar
1436
+
1437
+ calendar , holidays = _get_calendar (weekmask = weekmask ,
1438
+ holidays = holidays ,
1439
+ calendar = calendar )
1440
+ self .kwds ['weekmask' ] = self .weekmask = weekmask
1441
+ self .kwds ['holidays' ] = self .holidays = holidays
1442
+ self .kwds ['calendar' ] = self .calendar = calendar
1443
+
1444
+ @cache_readonly
1445
+ def cbday (self ):
1446
+ kwds = self .kwds
1447
+ return CustomBusinessDay (n = self .n , normalize = self .normalize , ** kwds )
1448
+
1449
+ @cache_readonly
1450
+ def m_offset (self ):
1451
+ kwds = self .kwds
1452
+ kwds = {key : kwds [key ] for key in kwds
1453
+ if key not in ['calendar' , 'weekmask' , 'holidays' ]}
1454
+ return MonthEnd (n = 1 , normalize = self .normalize , ** kwds )
1479
1455
1480
1456
@apply_wraps
1481
1457
def apply (self , other ):
@@ -1531,11 +1507,27 @@ def __init__(self, n=1, normalize=False, weekmask='Mon Tue Wed Thu Fri',
1531
1507
self .normalize = normalize
1532
1508
self .kwds = kwds
1533
1509
self .offset = kwds .get ('offset' , timedelta (0 ))
1534
- self .cbday = CustomBusinessDay (n = self .n , normalize = normalize ,
1535
- weekmask = weekmask , holidays = holidays ,
1536
- calendar = calendar , ** kwds )
1537
- self .m_offset = MonthBegin (n = 1 , normalize = normalize , ** kwds )
1538
- self .kwds ['calendar' ] = self .cbday .calendar # cache numpy calendar
1510
+
1511
+ # _get_calendar does validation and possible transformation
1512
+ # of calendar and holidays.
1513
+ calendar , holidays = _get_calendar (weekmask = weekmask ,
1514
+ holidays = holidays ,
1515
+ calendar = calendar )
1516
+ kwds ['calendar' ] = self .calendar = calendar
1517
+ kwds ['weekmask' ] = self .weekmask = weekmask
1518
+ kwds ['holidays' ] = self .holidays = holidays
1519
+
1520
+ @cache_readonly
1521
+ def cbday (self ):
1522
+ kwds = self .kwds
1523
+ return CustomBusinessDay (n = self .n , normalize = self .normalize , ** kwds )
1524
+
1525
+ @cache_readonly
1526
+ def m_offset (self ):
1527
+ kwds = self .kwds
1528
+ kwds = {key : kwds [key ] for key in kwds
1529
+ if key not in ['calendar' , 'weekmask' , 'holidays' ]}
1530
+ return MonthBegin (n = 1 , normalize = self .normalize , ** kwds )
1539
1531
1540
1532
@apply_wraps
1541
1533
def apply (self , other ):
@@ -2861,6 +2853,54 @@ class Nano(Tick):
2861
2853
CBMonthBegin = CustomBusinessMonthBegin
2862
2854
CDay = CustomBusinessDay
2863
2855
2856
+ # ---------------------------------------------------------------------
2857
+ # Business Calendar helpers
2858
+
2859
+
2860
+ def _get_calendar (weekmask , holidays , calendar ):
2861
+ """Generate busdaycalendar"""
2862
+ if isinstance (calendar , np .busdaycalendar ):
2863
+ if not holidays :
2864
+ holidays = tuple (calendar .holidays )
2865
+ elif not isinstance (holidays , tuple ):
2866
+ holidays = tuple (holidays )
2867
+ else :
2868
+ # trust that calendar.holidays and holidays are
2869
+ # consistent
2870
+ pass
2871
+ return calendar , holidays
2872
+
2873
+ if holidays is None :
2874
+ holidays = []
2875
+ try :
2876
+ holidays = holidays + calendar .holidays ().tolist ()
2877
+ except AttributeError :
2878
+ pass
2879
+ holidays = [_to_dt64 (dt , dtype = 'datetime64[D]' ) for dt in holidays ]
2880
+ holidays = tuple (sorted (holidays ))
2881
+
2882
+ kwargs = {'weekmask' : weekmask }
2883
+ if holidays :
2884
+ kwargs ['holidays' ] = holidays
2885
+
2886
+ busdaycalendar = np .busdaycalendar (** kwargs )
2887
+ return busdaycalendar , holidays
2888
+
2889
+
2890
+ def _to_dt64 (dt , dtype = 'datetime64' ):
2891
+ # Currently
2892
+ # > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]')
2893
+ # numpy.datetime64('2013-05-01T02:00:00.000000+0200')
2894
+ # Thus astype is needed to cast datetime to datetime64[D]
2895
+ if getattr (dt , 'tzinfo' , None ) is not None :
2896
+ i8 = tslib .pydt_to_i8 (dt )
2897
+ dt = tslib .tz_convert_single (i8 , 'UTC' , dt .tzinfo )
2898
+ dt = Timestamp (dt )
2899
+ dt = np .datetime64 (dt )
2900
+ if dt .dtype .name != dtype :
2901
+ dt = dt .astype (dtype )
2902
+ return dt
2903
+
2864
2904
2865
2905
def _get_firstbday (wkday ):
2866
2906
"""
0 commit comments