# HG changeset patch # User Paul Boddie # Date 1412377666 -7200 # Node ID 23eeb0e8f116ef15bf7680f636bf394eaf55ec00 # Parent fef95bc3063a3cbfa345e5e4dfba41160575f73a Added support for selecting particular occurrences of days in months. diff -r fef95bc3063a -r 23eeb0e8f116 tests/qualifiers.py --- a/tests/qualifiers.py Sat Oct 04 00:06:07 2014 +0200 +++ b/tests/qualifiers.py Sat Oct 04 01:07:46 2014 +0200 @@ -29,7 +29,7 @@ qualifiers = [ ("YEARLY", {"interval" : 2}), ("BYMONTH", {"values" : [1]}), - ("BYDAY", {"values" : [6]}), + ("BYDAY", {"values" : [(6, None)]}), ("BYHOUR", {"values" : [8, 9]}), ("BYMINUTE", {"values" : [30]}) ] @@ -147,7 +147,7 @@ qualifiers = [ ("YEARLY", {"interval" : 1}), ("BYMONTH", {"values" : [1]}), - ("BYDAY", {"values" : [1, 2, 3, 4, 5, 6, 7]}) + ("BYDAY", {"values" : [(1, None), (2, None), (3, None), (4, None), (5, None), (6, None), (7, None)]}) ] l = order_qualifiers(qualifiers) @@ -244,7 +244,7 @@ qualifiers = [ ("WEEKLY", {"interval" : 1}), - ("BYDAY", {"values" : [2, 4]}) + ("BYDAY", {"values" : [(2, None), (4, None)]}) ] l = order_qualifiers(qualifiers) @@ -264,7 +264,7 @@ qualifiers = [ ("WEEKLY", {"interval" : 1}), - ("BYDAY", {"values" : [2, 4]}) + ("BYDAY", {"values" : [(2, None), (4, None)]}) ] l = order_qualifiers(qualifiers) @@ -284,7 +284,7 @@ qualifiers = [ ("WEEKLY", {"interval" : 2}), - ("BYDAY", {"values" : [1, 3, 5]}) + ("BYDAY", {"values" : [(1, None), (3, None), (5, None)]}) ] l = order_qualifiers(qualifiers) @@ -302,4 +302,104 @@ print l[-1] == (1997, 12, 22, 9, 0, 0), l[-1] print +qualifiers = [ + ("WEEKLY", {"interval" : 2}), + ("BYDAY", {"values" : [(2, None), (4, None)]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 2, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise((1997, 12, 24, 0, 0, 0), 8) +print len(l) == 8, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1997, 10, 16, 9, 0, 0), l[-1] +print + +qualifiers = [ + ("MONTHLY", {"interval" : 1}), + ("BYDAY", {"values" : [(5, 0)]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 5, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise((1998, 12, 24, 0, 0, 0), 10) +print len(l) == 10, len(l) +print l[0] == (1997, 9, 5, 9, 0, 0), l[0] +print l[-1] == (1998, 6, 5, 9, 0, 0), l[-1] +print + +qualifiers = [ + ("MONTHLY", {"interval" : 1}), + ("BYDAY", {"values" : [(5, 0)]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 5, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise((1997, 12, 24, 0, 0, 0)) +print len(l) == 4, len(l) +print l[0] == (1997, 9, 5, 9, 0, 0), l[0] +print l[-1] == (1997, 12, 5, 9, 0, 0), l[-1] +print + +qualifiers = [ + ("MONTHLY", {"interval" : 2}), + ("BYDAY", {"values" : [(7, 0), (7, -1)]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 7, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise((1998, 12, 24, 0, 0, 0), 10) +print len(l) == 10, len(l) +print l[0] == (1997, 9, 7, 9, 0, 0), l[0] +print l[-1] == (1998, 5, 31, 9, 0, 0), l[-1] +print + +qualifiers = [ + ("MONTHLY", {"interval" : 1}), + ("BYDAY", {"values" : [(1, -2)]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 22, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise((1998, 12, 24, 0, 0, 0), 6) +print len(l) == 6, len(l) +print l[0] == (1997, 9, 22, 9, 0, 0), l[0] +print l[-1] == (1998, 2, 16, 9, 0, 0), l[-1] +print + # vim: tabstop=4 expandtab shiftwidth=4 diff -r fef95bc3063a -r 23eeb0e8f116 vRecurrence.py --- a/vRecurrence.py Sat Oct 04 00:06:07 2014 +0200 +++ b/vRecurrence.py Sat Oct 04 01:07:46 2014 +0200 @@ -51,6 +51,7 @@ resolution (or decreasing scope). """ +from calendar import monthrange from datetime import date, datetime, timedelta import operator @@ -299,48 +300,73 @@ return self.materialise_items(self.context, end, counter) def materialise_item(self, current, next, counter): - if self.selecting: - return self.selecting.materialise_items(current, next, counter) - elif counter is None or counter[0] < counter[1]: - if counter is not None: - counter[0] += 1 - return [current] + if counter is None or counter[0] < counter[1]: + if self.selecting: + return self.selecting.materialise_items(current, next, counter) + else: + if counter is not None: + counter[0] += 1 + return [current] else: return [] class Pattern(Selector): def materialise_items(self, context, end, counter): - start = self.args["values"][0] + first = scale(self.args["values"][0], self.pos) + + # Define the step between items. + interval = self.args.get("interval", 1) * units.get(self.qualifier, 1) step = scale(interval, self.pos) + + # Define the scale of a single item. + unit_interval = units.get(self.qualifier, 1) unit_step = scale(unit_interval, self.pos) - current = combine(context, scale(start, self.pos)) + + current = combine(context, first) results = [] + while current < end and (counter is None or counter[0] < counter[1]): next = update(current, step) current_end = update(current, unit_step) results += self.materialise_item(current, min(current_end, end), counter) current = next + return results class PatternFilter(Selector): def materialise_items(self, context, end, counter): - start = bases[self.pos] + first = scale(bases[self.pos], self.pos) + + # Define the step between items to be tested. + step = scale(1, self.pos) - current = combine(context, scale(start, self.pos)) + + current = combine(context, first) results = [] + while current < end and (counter is None or counter[0] < counter[1]): next = update(current, step) results += self.materialise_item(current, min(next, end), counter) current = next + return results def materialise_item(self, current, next, counter): values = self.args["values"] + + # Select by day in week, also by occurrence in month. + if self.qualifier == "BYDAY": - if datetime(*current).isoweekday() in values: - return Selector.materialise_item(self, current, next, counter) + for value, index in values: + if datetime(*current).isoweekday() == value: + last_day = monthrange(current[0], current[1])[1] + index_from_start = (current[2] - 1) / 7 + index_from_end = -(1 + (last_day - current[2]) / 7) + if index is None or index in (index_from_start, index_from_end): + return Selector.materialise_item(self, current, next, counter) + return [] class Enum(Selector):