def test_quarter(self): dates = [ datetime.date(2019, 1, 1), datetime.date(2019, 6, 30), datetime.date(2019, 7, 1), datetime.date(2019, 12, 31), ] self.assertEqual([utils.quarter(date) for date in dates], [1, 2, 3, 4])
def eoq(self, date: datetime.date) -> datetime.date: """ Returns the last open date of the quarter Parameters ---------- date : datetime.date the date from which to compute the end of quarter Returns ------- datetime.date """ for i in range(self.index(date), len(self)): if (utils.quarter(self.__dates__[i]) == utils.quarter(date) and self.__dates__[i].year == date.year): continue return self.__dates__[i - 1] return self.__dates__[i]
def soq(self, date: datetime.date) -> datetime.date: """ Returns the first open day of the quarter given the date Parameters ---------- date : datetime.date the date from which to compute the start of the quarter Returns ------- datetime.date """ for i in range(self.index(date), -1, -1): if (utils.quarter(self.__dates__[i]) == utils.quarter(date) and self.__dates__[i].year == date.year): continue return self.__dates__[i + 1] return self.__dates__[i]
def groupby(self, grouper): """ Group dates by the grouper parameter. Allowed values for the grouper: - callable - the callable will receive the date and must return a hashable value - frequency criterion - a string representing a frequency Frequency criteria include: - :code:`W`: group by week number each year - :code:`M`: group by month each year - :code:`Q`: group by quarter each year - :code:`H`: group by semester each year - :code:`Y`: group by year each year Parameters ---------- grouper : str, callable the criterion to group dates by Returns ------- :class:`doubledate.calendar.Collection` Collection of calendars Example ------- Group dates by month >>> calendar = Calendar(dates) >>> calendar.groupby("M") <doubledate.Collection at 0x7fd0fa52c2e0> Group dates in half months >>> calendar = Calendar(dates) >>> calendar.groupby(lambda date: (date.year, date.month, date.day < 15)) <doubledate.Collection at 0x7fd0fa52c2e0> """ if isinstance(grouper, str): if grouper == "W": return self.groupby(lambda date: (date.year, date.isocalendar()[1])) elif grouper == "M": return self.groupby(lambda date: (date.year, date.month)) elif grouper == "Q": return self.groupby(lambda date: (date.year, utils.quarter(date))) elif grouper == "H": return self.groupby(lambda date: (date.year, date.month > 6)) elif grouper == "Y": return self.groupby(lambda date: date.year) raise ValueError( f"Expected one of 'W', 'M', 'Q', 'H' or 'Y'; '{grouper}' given" ) if callable(grouper): calendars = collections.defaultdict(lambda: []) for date in self: calendars[grouper(date)].append(date) return Collection( [Calendar(dates) for dates in calendars.values()]) raise ValueError(f"Expected string or function, received '{grouper}'")
def filter( self, func=None, *, year: int = None, semester: int = None, quarter: int = None, month: int = None, week: int = None, weekday: str = None, ): """ Filters and returns a new calendar from this calendar based on a criteria. Allowed criteria are: - either a filtering function (lambda) - one or several filtering frequencies as named arguments Parameters ---------- func : function, optional a filtering function (receives each date as argument) year : int, optional pass a value to filter dates of the given year only semester : int, optional (1 or 2) pass a value to filter dates of the given semester only quarter : int, optional (1, 2, 3, or 4) pass a value to filter dates of the given quarter only month : int, optional (1 through 12) pass a value to filter dates of the given month only week : int, optional (1 through 53) pass a value to filter dates of the given week number only weekday : int, optional (0 through 6) pass a value to filter dates of the given weekday only Monday = 0, Tuesday = 1... Sunday = 6 Returns ------- Calendar Example ------- Filter dates from 3Q 2020 >>> calendar = Calendar(dates) #assume dates is a list of dates >>> calendar.filter(year=2020, quarter=3) Filter Mondays >>> calendar = Calendar(dates) >>> calendar.filter(lambda date: date.weekday() == 0) """ if func is not None: if not callable(func): raise ValueError( "Filter accepts either a function, one or several named arguments" ) return Calendar([date for date in self.__dates__ if func(date)]) if all([ arg is None for arg in [year, semester, quarter, month, week, weekday] ]): raise ValueError( "You need to provide one of year, semester, quarter, month, week, weekday" ) dates = list(self.__dates__) if year is not None: dates = list(filter(lambda date: date.year == year, dates)) if semester is not None: dates = list( filter(lambda date: utils.semester(date) == semester, dates)) if quarter is not None: dates = list( filter(lambda date: utils.quarter(date) == quarter, dates)) if month is not None: dates = list(filter(lambda date: date.month == month, dates)) if week is not None: dates = list( filter(lambda date: date.isocalendar()[1] == week, dates)) if weekday is not None: dates = list(filter(lambda date: date.weekday() == weekday, dates)) return Calendar(dates)