# HG changeset patch # User Paul Boddie # Date 1513457531 -3600 # Node ID 2181c56d858117bce7b62b75369467d8145e4dea # Parent 4d0d5b25bbbd7d5cf8ce25d8dc5d0e278aeaf0d1 Introduced initial support for UNTIL as an observed qualifier. diff -r 4d0d5b25bbbd -r 2181c56d8581 tests/internal/qualifiers.py --- a/tests/internal/qualifiers.py Sat Dec 16 21:50:17 2017 +0100 +++ b/tests/internal/qualifiers.py Sat Dec 16 21:52:11 2017 +0100 @@ -102,7 +102,7 @@ dt = (1997, 9, 2, 9, 0, 0) s = select(dt, qualifiers) - l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), True) + l = s.materialise(dt, (1997, 12, 25, 0, 0, 0), True) print len(l) == 113, 113, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 12, 23, 9, 0, 0), (1997, 12, 23, 9, 0, 0), l[-1] diff -r 4d0d5b25bbbd -r 2181c56d8581 vRecurrence.py --- a/vRecurrence.py Sat Dec 16 21:50:17 2017 +0100 +++ b/vRecurrence.py Sat Dec 16 21:52:11 2017 +0100 @@ -87,7 +87,7 @@ # Add labels corresponding to negative indexes. -level_labels += ("COUNT", "DTSTART") +level_labels += ("COUNT", "UNTIL", "DTSTART") # Symbols corresponding to resolution levels. @@ -99,7 +99,7 @@ # Special levels used by non-qualifier-originating selectors. -COUNT, DTSTART, BYSETPOS = -2, -1, None +COUNT, UNTIL, DTSTART, BYSETPOS = -3, -2, -1, None # Levels defining days. @@ -207,6 +207,30 @@ qualifier = (key, {"values" : values}) + # Accept until datetimes. + # NOTE: This should be a UTC datetime unless DTSTART is a date or + # NOTE: floating datetime. + + elif key == "UNTIL": + try: + # YYYYMMDD + + if len(value) == 8: + end = map(int, (value[:4], value[4:6], value[6:])) + + # YYYYMMDDTHHMMSS[Z] + + elif len(value) in (15, 16): + end = map(int, (value[:4], value[4:6], value[6:8], value[9:11], value[11:13], value[13:15])) + + else: + continue + + qualifier = (key, {"end" : tuple(end)}) + + except ValueError: + continue + # Ignore other items. else: @@ -308,6 +332,9 @@ elif qualifier == "COUNT": return LimitSelector(COUNT, args, "COUNT") + elif qualifier == "UNTIL": + return UntilSelector(UNTIL, args, "UNTIL") + else: return Pattern(freq[qualifier], args, qualifier) @@ -1154,6 +1181,17 @@ def get_start(self): return self.args["start"] +class UntilSelector(Selector): + + "A selector ensuring that the until datetime is not passed." + + def materialise_items(self, context, start, end, inclusive=False): + return UntilIterator(self, context, start, end, inclusive, + self.get_end()) + + def get_end(self): + return self.args["end"] + special_enum_levels = { "BYDAY" : WeekDayFilter, "BYMONTHDAY" : MonthDayFilter, @@ -1548,6 +1586,26 @@ return result +class UntilIterator(SelectorIterator): + + "An iterator ensuring that the until datetime is not passed." + + def __init__(self, selector, current, start, end, inclusive, until): + SelectorIterator.__init__(self, selector, current, start, end, inclusive) + self.until = until + + def next(self): + + "Return the next value, stopping if it is beyond the until limit." + + while True: + current = self.next_item(self.start, self.end) + if current >= self.until: + break + return current + + raise StopIteration + def connect_selectors(selectors): """