|
| 1 | +import sys |
1 | 2 | from datetime import date, datetime, timedelta
|
2 | 3 | from pandas.compat import range
|
3 | 4 | from pandas import compat
|
|
16 | 17 | import functools
|
17 | 18 |
|
18 | 19 | __all__ = ['Day', 'BusinessDay', 'BDay', 'CustomBusinessDay', 'CDay',
|
| 20 | + 'CBMonthEnd','CBMonthBegin', |
19 | 21 | 'MonthBegin', 'BMonthBegin', 'MonthEnd', 'BMonthEnd',
|
20 | 22 | 'YearBegin', 'BYearBegin', 'YearEnd', 'BYearEnd',
|
21 | 23 | 'QuarterBegin', 'BQuarterBegin', 'QuarterEnd', 'BQuarterEnd',
|
@@ -703,6 +705,132 @@ def onOffset(cls, dt):
|
703 | 705 | _prefix = 'BMS'
|
704 | 706 |
|
705 | 707 |
|
| 708 | + |
| 709 | +class CustomBusinessMonthEnd(MonthOffset): |
| 710 | + """ |
| 711 | + **EXPERIMENTAL** DateOffset of one custom business month |
| 712 | +
|
| 713 | + .. warning:: EXPERIMENTAL |
| 714 | +
|
| 715 | + This class is not officially supported and the API is likely to change |
| 716 | + in future versions. Use this at your own risk. |
| 717 | +
|
| 718 | + Parameters |
| 719 | + ---------- |
| 720 | + n : int, default 1 |
| 721 | + offset : timedelta, default timedelta(0) |
| 722 | + normalize : bool, default False |
| 723 | + Normalize start/end dates to midnight before generating date range |
| 724 | + weekmask : str, Default 'Mon Tue Wed Thu Fri' |
| 725 | + weekmask of valid business days, passed to ``numpy.busdaycalendar`` |
| 726 | + holidays : list |
| 727 | + list/array of dates to exclude from the set of valid business days, |
| 728 | + passed to ``numpy.busdaycalendar`` |
| 729 | + """ |
| 730 | + |
| 731 | + _cacheable = False |
| 732 | + _prefix = 'CBM' |
| 733 | + def __init__(self, n=1, **kwds): |
| 734 | + self.n = int(n) |
| 735 | + self.kwds = kwds |
| 736 | + self.offset = kwds.get('offset', timedelta(0)) |
| 737 | + self.normalize = kwds.get('normalize', False) |
| 738 | + self.weekmask = kwds.get('weekmask', 'Mon Tue Wed Thu Fri') |
| 739 | + holidays = kwds.get('holidays', []) |
| 740 | + self.cbday = CustomBusinessDay(n=self.n,**kwds) |
| 741 | + self.m_offset = MonthEnd() |
| 742 | + |
| 743 | + @apply_nat |
| 744 | + def apply(self,other): |
| 745 | + n = self.n |
| 746 | + dt_in = other |
| 747 | + # First move to month offset |
| 748 | + cur_mend = self.m_offset.rollforward(dt_in) |
| 749 | + # Find this custom month offset |
| 750 | + cur_cmend = self.cbday.rollback(cur_mend) |
| 751 | + |
| 752 | + # handle zero case. arbitrarily rollforward |
| 753 | + if n == 0 and dt_in != cur_cmend: |
| 754 | + n += 1 |
| 755 | + |
| 756 | + if dt_in < cur_cmend and n >= 1: |
| 757 | + n -= 1 |
| 758 | + elif dt_in > cur_cmend and n <= -1: |
| 759 | + n += 1 |
| 760 | + |
| 761 | + new = cur_mend + n * MonthEnd() |
| 762 | + result = self.cbday.rollback(new) |
| 763 | + return as_timestamp(result) |
| 764 | + |
| 765 | + def __repr__(self): |
| 766 | + if sys.version_info.major < 3: |
| 767 | + return BusinessDay.__repr__.__func__(self) |
| 768 | + else: |
| 769 | + return BusinessDay.__repr__(self) |
| 770 | + |
| 771 | +class CustomBusinessMonthBegin(MonthOffset): |
| 772 | + """ |
| 773 | + **EXPERIMENTAL** DateOffset of one custom business month |
| 774 | +
|
| 775 | + .. warning:: EXPERIMENTAL |
| 776 | +
|
| 777 | + This class is not officially supported and the API is likely to change |
| 778 | + in future versions. Use this at your own risk. |
| 779 | +
|
| 780 | + Parameters |
| 781 | + ---------- |
| 782 | + n : int, default 1 |
| 783 | + offset : timedelta, default timedelta(0) |
| 784 | + normalize : bool, default False |
| 785 | + 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`` |
| 791 | + """ |
| 792 | + |
| 793 | + _cacheable = False |
| 794 | + _prefix = 'CBMS' |
| 795 | + def __init__(self, n=1, **kwds): |
| 796 | + self.n = int(n) |
| 797 | + self.kwds = kwds |
| 798 | + self.offset = kwds.get('offset', timedelta(0)) |
| 799 | + self.normalize = kwds.get('normalize', False) |
| 800 | + self.weekmask = kwds.get('weekmask', 'Mon Tue Wed Thu Fri') |
| 801 | + holidays = kwds.get('holidays', []) |
| 802 | + self.cbday = CustomBusinessDay(n=self.n,**kwds) |
| 803 | + self.m_offset = MonthBegin() |
| 804 | + |
| 805 | + @apply_nat |
| 806 | + def apply(self,other): |
| 807 | + n = self.n |
| 808 | + dt_in = other |
| 809 | + # First move to month offset |
| 810 | + cur_mbegin = self.m_offset.rollback(dt_in) |
| 811 | + # Find this custom month offset |
| 812 | + cur_cmbegin = self.cbday.rollforward(cur_mbegin) |
| 813 | + |
| 814 | + # handle zero case. arbitrarily rollforward |
| 815 | + if n == 0 and dt_in != cur_cmbegin: |
| 816 | + n += 1 |
| 817 | + |
| 818 | + if dt_in > cur_cmbegin and n <= -1: |
| 819 | + n += 1 |
| 820 | + elif dt_in < cur_cmbegin and n >= 1: |
| 821 | + n -= 1 |
| 822 | + |
| 823 | + new = cur_mbegin + n * MonthBegin() |
| 824 | + result = self.cbday.rollforward(new) |
| 825 | + return as_timestamp(result) |
| 826 | + |
| 827 | + |
| 828 | + def __repr__(self): |
| 829 | + if sys.version_info.major < 3: |
| 830 | + return BusinessDay.__repr__.__func__(self) |
| 831 | + else: |
| 832 | + return BusinessDay.__repr__(self) |
| 833 | + |
706 | 834 | class Week(DateOffset):
|
707 | 835 | """
|
708 | 836 | Weekly offset
|
@@ -1906,6 +2034,8 @@ class Nano(Tick):
|
1906 | 2034 | BDay = BusinessDay
|
1907 | 2035 | BMonthEnd = BusinessMonthEnd
|
1908 | 2036 | BMonthBegin = BusinessMonthBegin
|
| 2037 | +CBMonthEnd = CustomBusinessMonthEnd |
| 2038 | +CBMonthBegin = CustomBusinessMonthBegin |
1909 | 2039 | CDay = CustomBusinessDay
|
1910 | 2040 |
|
1911 | 2041 |
|
@@ -1988,28 +2118,30 @@ def generate_range(start=None, end=None, periods=None,
|
1988 | 2118 | cur = next_date
|
1989 | 2119 |
|
1990 | 2120 | prefix_mapping = dict((offset._prefix, offset) for offset in [
|
1991 |
| - YearBegin, # 'AS' |
1992 |
| - YearEnd, # 'A' |
1993 |
| - BYearBegin, # 'BAS' |
1994 |
| - BYearEnd, # 'BA' |
1995 |
| - BusinessDay, # 'B' |
1996 |
| - BusinessMonthBegin, # 'BMS' |
1997 |
| - BusinessMonthEnd, # 'BM' |
1998 |
| - BQuarterEnd, # 'BQ' |
1999 |
| - BQuarterBegin, # 'BQS' |
2000 |
| - CustomBusinessDay, # 'C' |
2001 |
| - MonthEnd, # 'M' |
2002 |
| - MonthBegin, # 'MS' |
2003 |
| - Week, # 'W' |
2004 |
| - Second, # 'S' |
2005 |
| - Minute, # 'T' |
2006 |
| - Micro, # 'U' |
2007 |
| - QuarterEnd, # 'Q' |
2008 |
| - QuarterBegin, # 'QS' |
2009 |
| - Milli, # 'L' |
2010 |
| - Hour, # 'H' |
2011 |
| - Day, # 'D' |
2012 |
| - WeekOfMonth, # 'WOM' |
| 2121 | + YearBegin, # 'AS' |
| 2122 | + YearEnd, # 'A' |
| 2123 | + BYearBegin, # 'BAS' |
| 2124 | + BYearEnd, # 'BA' |
| 2125 | + BusinessDay, # 'B' |
| 2126 | + BusinessMonthBegin, # 'BMS' |
| 2127 | + BusinessMonthEnd, # 'BM' |
| 2128 | + BQuarterEnd, # 'BQ' |
| 2129 | + BQuarterBegin, # 'BQS' |
| 2130 | + CustomBusinessDay, # 'C' |
| 2131 | + CustomBusinessMonthEnd, # 'CBM' |
| 2132 | + CustomBusinessMonthBegin, # 'CBMS' |
| 2133 | + MonthEnd, # 'M' |
| 2134 | + MonthBegin, # 'MS' |
| 2135 | + Week, # 'W' |
| 2136 | + Second, # 'S' |
| 2137 | + Minute, # 'T' |
| 2138 | + Micro, # 'U' |
| 2139 | + QuarterEnd, # 'Q' |
| 2140 | + QuarterBegin, # 'QS' |
| 2141 | + Milli, # 'L' |
| 2142 | + Hour, # 'H' |
| 2143 | + Day, # 'D' |
| 2144 | + WeekOfMonth, # 'WOM' |
2013 | 2145 | FY5253,
|
2014 | 2146 | FY5253Quarter,
|
2015 | 2147 | ])
|
|
0 commit comments