1.1 --- a/vRecurrence.py Sat Dec 16 21:50:17 2017 +0100
1.2 +++ b/vRecurrence.py Sat Dec 16 21:52:11 2017 +0100
1.3 @@ -87,7 +87,7 @@
1.4
1.5 # Add labels corresponding to negative indexes.
1.6
1.7 -level_labels += ("COUNT", "DTSTART")
1.8 +level_labels += ("COUNT", "UNTIL", "DTSTART")
1.9
1.10 # Symbols corresponding to resolution levels.
1.11
1.12 @@ -99,7 +99,7 @@
1.13
1.14 # Special levels used by non-qualifier-originating selectors.
1.15
1.16 -COUNT, DTSTART, BYSETPOS = -2, -1, None
1.17 +COUNT, UNTIL, DTSTART, BYSETPOS = -3, -2, -1, None
1.18
1.19 # Levels defining days.
1.20
1.21 @@ -207,6 +207,30 @@
1.22
1.23 qualifier = (key, {"values" : values})
1.24
1.25 + # Accept until datetimes.
1.26 + # NOTE: This should be a UTC datetime unless DTSTART is a date or
1.27 + # NOTE: floating datetime.
1.28 +
1.29 + elif key == "UNTIL":
1.30 + try:
1.31 + # YYYYMMDD
1.32 +
1.33 + if len(value) == 8:
1.34 + end = map(int, (value[:4], value[4:6], value[6:]))
1.35 +
1.36 + # YYYYMMDDTHHMMSS[Z]
1.37 +
1.38 + elif len(value) in (15, 16):
1.39 + end = map(int, (value[:4], value[4:6], value[6:8], value[9:11], value[11:13], value[13:15]))
1.40 +
1.41 + else:
1.42 + continue
1.43 +
1.44 + qualifier = (key, {"end" : tuple(end)})
1.45 +
1.46 + except ValueError:
1.47 + continue
1.48 +
1.49 # Ignore other items.
1.50
1.51 else:
1.52 @@ -308,6 +332,9 @@
1.53 elif qualifier == "COUNT":
1.54 return LimitSelector(COUNT, args, "COUNT")
1.55
1.56 + elif qualifier == "UNTIL":
1.57 + return UntilSelector(UNTIL, args, "UNTIL")
1.58 +
1.59 else:
1.60 return Pattern(freq[qualifier], args, qualifier)
1.61
1.62 @@ -1154,6 +1181,17 @@
1.63 def get_start(self):
1.64 return self.args["start"]
1.65
1.66 +class UntilSelector(Selector):
1.67 +
1.68 + "A selector ensuring that the until datetime is not passed."
1.69 +
1.70 + def materialise_items(self, context, start, end, inclusive=False):
1.71 + return UntilIterator(self, context, start, end, inclusive,
1.72 + self.get_end())
1.73 +
1.74 + def get_end(self):
1.75 + return self.args["end"]
1.76 +
1.77 special_enum_levels = {
1.78 "BYDAY" : WeekDayFilter,
1.79 "BYMONTHDAY" : MonthDayFilter,
1.80 @@ -1548,6 +1586,26 @@
1.81
1.82 return result
1.83
1.84 +class UntilIterator(SelectorIterator):
1.85 +
1.86 + "An iterator ensuring that the until datetime is not passed."
1.87 +
1.88 + def __init__(self, selector, current, start, end, inclusive, until):
1.89 + SelectorIterator.__init__(self, selector, current, start, end, inclusive)
1.90 + self.until = until
1.91 +
1.92 + def next(self):
1.93 +
1.94 + "Return the next value, stopping if it is beyond the until limit."
1.95 +
1.96 + while True:
1.97 + current = self.next_item(self.start, self.end)
1.98 + if current >= self.until:
1.99 + break
1.100 + return current
1.101 +
1.102 + raise StopIteration
1.103 +
1.104 def connect_selectors(selectors):
1.105
1.106 """