# HG changeset patch # User Paul Boddie # Date 1412547426 -7200 # Node ID 3d748f9c9c29dccf489c698fbce89d8e1a8f6c7f # Parent 953c5b53d8accca1d2d517a2e8c77843a86e989e Changed the weekday filter to be context-specific, producing occurrences for days in years, months and weeks. Propagated the underlying datetime context to all qualifiers unconditionally. Introduced sorting of the results since the selection process may combine them from processes producing different orderings. Changed the BYDAY indexes to be 1-based for positive indexes. Added the expected result for each test output. diff -r 953c5b53d8ac -r 3d748f9c9c29 tests/qualifiers.py --- a/tests/qualifiers.py Sat Oct 04 20:05:17 2014 +0200 +++ b/tests/qualifiers.py Mon Oct 06 00:17:06 2014 +0200 @@ -21,9 +21,9 @@ s = process(l) l = s.materialise(dt, (2003, 12, 24)) -print len(l) == 7, len(l) -print l[0] == (1997, 11, 2), l[0] -print l[-1] == (2003, 11, 2), l[-1] +print len(l) == 7, 7, len(l) +print l[0] == (1997, 11, 2), (1997, 11, 2), l[0] +print l[-1] == (2003, 11, 2), (2003, 11, 2), l[-1] print qualifiers = [ @@ -44,9 +44,9 @@ s = process(l) l = s.materialise(dt, (2003, 12, 24, 0, 0, 0)) -print len(l) == 32, len(l) -print l[0] == (1997, 1, 11, 8, 30, 0), l[0] -print l[-1] == (2003, 1, 25, 9, 30, 0), l[-1] +print len(l) == 32, 32, len(l) +print l[0] == (1997, 1, 11, 8, 30, 0), (1997, 1, 11, 8, 30, 0), l[0] +print l[-1] == (2003, 1, 25, 9, 30, 0), (2003, 1, 25, 9, 30, 0), l[-1] print qualifiers = [ @@ -63,9 +63,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 9, 11, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1997, 9, 11, 9, 0, 0), (1997, 9, 11, 9, 0, 0), l[-1] print qualifiers = [ @@ -82,9 +82,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) -print len(l) == 113, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] +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] print qualifiers = [ @@ -101,9 +101,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) -print len(l) == 57, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] +print len(l) == 57, 57, 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] print qualifiers = [ @@ -120,9 +120,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) -print len(l) == 17, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] +print len(l) == 17, 17, 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] print qualifiers = [ @@ -139,9 +139,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), 5) -print len(l) == 5, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 10, 12, 9, 0, 0), l[-1] +print len(l) == 5, 5, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1997, 10, 12, 9, 0, 0), (1997, 10, 12, 9, 0, 0), l[-1] print qualifiers = [ @@ -160,9 +160,9 @@ s = process(l) l = s.materialise(dt, (2000, 1, 31, 14, 0, 0)) -print len(l) == 93, len(l) -print l[0] == (1998, 1, 1, 9, 0, 0), l[0] -print l[-1] == (2000, 1, 31, 9, 0, 0), l[-1] +print len(l) == 93, 93, len(l) +print l[0] == (1998, 1, 1, 9, 0, 0), (1998, 1, 1, 9, 0, 0), l[0] +print l[-1] == (2000, 1, 31, 9, 0, 0), (2000, 1, 31, 9, 0, 0), l[-1] print qualifiers = [ @@ -180,9 +180,9 @@ s = process(l) l = s.materialise(dt, (2000, 1, 31, 14, 0, 0)) -print len(l) == 93, len(l) -print l[0] == (1998, 1, 1, 9, 0, 0), l[0] -print l[-1] == (2000, 1, 31, 9, 0, 0), l[-1] +print len(l) == 93, 93, len(l) +print l[0] == (1998, 1, 1, 9, 0, 0), (1998, 1, 1, 9, 0, 0), l[0] +print l[-1] == (2000, 1, 31, 9, 0, 0), (2000, 1, 31, 9, 0, 0), l[-1] print qualifiers = [ @@ -199,9 +199,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 11, 4, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1997, 11, 4, 9, 0, 0), (1997, 11, 4, 9, 0, 0), l[-1] print qualifiers = [ @@ -218,9 +218,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) -print len(l) == 17, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] +print len(l) == 17, 17, 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] print qualifiers = [ @@ -237,9 +237,9 @@ s = process(l) l = s.materialise(dt, (1998, 2, 20, 0, 0, 0)) -print len(l) == 13, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1998, 2, 17, 9, 0, 0), l[-1] +print len(l) == 13, 13, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1998, 2, 17, 9, 0, 0), (1998, 2, 17, 9, 0, 0), l[-1] print qualifiers = [ @@ -257,9 +257,9 @@ s = process(l) l = s.materialise(dt, (1997, 10, 7, 9, 0, 0)) -print len(l) == 10, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 10, 2, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1997, 10, 2, 9, 0, 0), (1997, 10, 2, 9, 0, 0), l[-1] print qualifiers = [ @@ -277,9 +277,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1997, 10, 2, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1997, 10, 2, 9, 0, 0), (1997, 10, 2, 9, 0, 0), l[-1] print qualifiers = [ @@ -297,9 +297,9 @@ s = process(l) l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) -print len(l) == 25, len(l) -print l[0] == (1997, 9, 1, 9, 0, 0), l[0] -print l[-1] == (1997, 12, 22, 9, 0, 0), l[-1] +print len(l) == 25, 25, len(l) +print l[0] == (1997, 9, 1, 9, 0, 0), (1997, 9, 1, 9, 0, 0), l[0] +print l[-1] == (1997, 12, 22, 9, 0, 0), (1997, 12, 22, 9, 0, 0), l[-1] print qualifiers = [ @@ -317,14 +317,14 @@ s = process(l) l = s.materialise(dt, (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 len(l) == 8, 8, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1997, 10, 16, 9, 0, 0), (1997, 10, 16, 9, 0, 0), l[-1] print qualifiers = [ ("MONTHLY", {"interval" : 1}), - ("BYDAY", {"values" : [(5, 0)]}) + ("BYDAY", {"values" : [(5, 1)]}) ] l = order_qualifiers(qualifiers) @@ -337,14 +337,14 @@ s = process(l) l = s.materialise(dt, (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 len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 5, 9, 0, 0), (1997, 9, 5, 9, 0, 0), l[0] +print l[-1] == (1998, 6, 5, 9, 0, 0), (1998, 6, 5, 9, 0, 0), l[-1] print qualifiers = [ ("MONTHLY", {"interval" : 1}), - ("BYDAY", {"values" : [(5, 0)]}) + ("BYDAY", {"values" : [(5, 1)]}) ] l = order_qualifiers(qualifiers) @@ -357,14 +357,14 @@ s = process(l) l = s.materialise(dt, (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 len(l) == 4, 4, len(l) +print l[0] == (1997, 9, 5, 9, 0, 0), (1997, 9, 5, 9, 0, 0), l[0] +print l[-1] == (1997, 12, 5, 9, 0, 0), (1997, 12, 5, 9, 0, 0), l[-1] print qualifiers = [ ("MONTHLY", {"interval" : 2}), - ("BYDAY", {"values" : [(7, 0), (7, -1)]}) + ("BYDAY", {"values" : [(7, 1), (7, -1)]}) ] l = order_qualifiers(qualifiers) @@ -377,9 +377,9 @@ s = process(l) l = s.materialise(dt, (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 len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 7, 9, 0, 0), (1997, 9, 7, 9, 0, 0), l[0] +print l[-1] == (1998, 5, 31, 9, 0, 0), (1998, 5, 31, 9, 0, 0), l[-1] print qualifiers = [ @@ -397,9 +397,9 @@ s = process(l) l = s.materialise(dt, (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 len(l) == 6, 6, len(l) +print l[0] == (1997, 9, 22, 9, 0, 0), (1997, 9, 22, 9, 0, 0), l[0] +print l[-1] == (1998, 2, 16, 9, 0, 0), (1998, 2, 16, 9, 0, 0), l[-1] print qualifiers = [ @@ -417,9 +417,9 @@ s = process(l) l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 6) -print len(l) == 6, len(l) -print l[0] == (1997, 9, 28, 9, 0, 0), l[0] -print l[-1] == (1998, 2, 26, 9, 0, 0), l[-1] +print len(l) == 6, 6, len(l) +print l[0] == (1997, 9, 28, 9, 0, 0), (1997, 9, 28, 9, 0, 0), l[0] +print l[-1] == (1998, 2, 26, 9, 0, 0), (1998, 2, 26, 9, 0, 0), l[-1] print qualifiers = [ @@ -437,9 +437,9 @@ s = process(l) l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1998, 1, 15, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1998, 1, 15, 9, 0, 0), (1998, 1, 15, 9, 0, 0), l[-1] print qualifiers = [ @@ -457,9 +457,9 @@ s = process(l) l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 9, 30, 9, 0, 0), l[0] -print l[-1] == (1998, 2, 1, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 30, 9, 0, 0), (1997, 9, 30, 9, 0, 0), l[0] +print l[-1] == (1998, 2, 1, 9, 0, 0), (1998, 2, 1, 9, 0, 0), l[-1] print qualifiers = [ @@ -477,9 +477,9 @@ s = process(l) l = s.materialise(dt, (1999, 12, 24, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 9, 10, 9, 0, 0), l[0] -print l[-1] == (1999, 3, 13, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 9, 10, 9, 0, 0), (1997, 9, 10, 9, 0, 0), l[0] +print l[-1] == (1999, 3, 13, 9, 0, 0), (1999, 3, 13, 9, 0, 0), l[-1] print qualifiers = [ @@ -497,9 +497,9 @@ s = process(l) l = s.materialise(dt, (1998, 4, 1, 0, 0, 0)) -print len(l) == 18, len(l) -print l[0] == (1997, 9, 2, 9, 0, 0), l[0] -print l[-1] == (1998, 3, 31, 9, 0, 0), l[-1] +print len(l) == 18, 18, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1998, 3, 31, 9, 0, 0), (1998, 3, 31, 9, 0, 0), l[-1] print qualifiers = [ @@ -517,9 +517,9 @@ s = process(l) l = s.materialise(dt, (2001, 12, 24, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 6, 10, 9, 0, 0), l[0] -print l[-1] == (2001, 7, 10, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 6, 10, 9, 0, 0), (1997, 6, 10, 9, 0, 0), l[0] +print l[-1] == (2001, 7, 10, 9, 0, 0), (2001, 7, 10, 9, 0, 0), l[-1] print qualifiers = [ @@ -537,9 +537,9 @@ s = process(l) l = s.materialise(dt, (2003, 12, 24, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 3, 10, 9, 0, 0), l[0] -print l[-1] == (2003, 3, 10, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 3, 10, 9, 0, 0), (1997, 3, 10, 9, 0, 0), l[0] +print l[-1] == (2003, 3, 10, 9, 0, 0), (2003, 3, 10, 9, 0, 0), l[-1] print qualifiers = [ @@ -557,9 +557,29 @@ s = process(l) l = s.materialise(dt, (2006, 2, 1, 0, 0, 0), 10) -print len(l) == 10, len(l) -print l[0] == (1997, 1, 1, 9, 0, 0), l[0] -print l[-1] == (2006, 1, 1, 9, 0, 0), l[-1] +print len(l) == 10, 10, len(l) +print l[0] == (1997, 1, 1, 9, 0, 0), (1997, 1, 1, 9, 0, 0), l[0] +print l[-1] == (2006, 1, 1, 9, 0, 0), (2006, 1, 1, 9, 0, 0), l[-1] +print + +qualifiers = [ + ("YEARLY", {"interval" : 1}), + ("BYDAY", {"values" : [(1, 20)]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 5, 19, 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(dt, (1999, 12, 24, 0, 0, 0)) +print len(l) == 3, 3, len(l) +print l[0] == (1997, 5, 19, 9, 0, 0), (1997, 5, 19, 9, 0, 0), l[0] +print l[-1] == (1999, 5, 17, 9, 0, 0), (1999, 5, 17, 9, 0, 0), l[-1] print # vim: tabstop=4 expandtab shiftwidth=4 diff -r 953c5b53d8ac -r 3d748f9c9c29 vRecurrence.py --- a/vRecurrence.py Sat Oct 04 20:05:17 2014 +0200 +++ b/vRecurrence.py Mon Oct 06 00:17:06 2014 +0200 @@ -157,6 +157,7 @@ have_q = False context = [] + context.append(from_dt.args["values"][0]) # Consume from both lists, merging entries. @@ -167,8 +168,8 @@ # Datetime value at wider resolution. if _pos < pos: + from_dt = get_next(iter_dt) context.append(from_dt.args["values"][0]) - from_dt = get_next(iter_dt) # Qualifier at wider or same resolution as datetime value. @@ -190,9 +191,9 @@ # Or combine the qualifier and value details. else: - context.append(from_dt.args["values"][0]) l.append(combine_context_with_qualifier(context, from_q)) from_dt = get_next(iter_dt) + context.append(from_dt.args["values"][0]) from_q = get_next(iter_q) @@ -223,8 +224,7 @@ imposing the datetime value information on any qualifiers. """ - if not from_q.args.has_key("values"): - from_q.context = tuple(context) + from_q.context = tuple(context) return from_q # Datetime arithmetic. @@ -276,7 +276,26 @@ d = datetime(*updated_for_months) s = timedelta(step[2], get_seconds(step)) - return (d + s).timetuple()[:len(t)] + return to_tuple(d + s, len(t)) + +def to_tuple(d, n): + return d.timetuple()[:n] + +def get_first_day(first_day, weekday): + first_day = date(*first_day) + first_weekday = first_day.isoweekday() + if first_weekday > weekday: + return first_day + timedelta(7 - first_weekday + weekday) + else: + return first_day + timedelta(weekday - first_weekday) + +def get_last_day(last_day, weekday): + last_day = date(*last_day) + last_weekday = last_day.isoweekday() + if last_weekday < weekday: + return last_day - timedelta(last_weekday + 7 - weekday) + else: + return last_day - timedelta(last_weekday - weekday) # Classes for producing instances from recurrence structures. @@ -293,7 +312,9 @@ def materialise(self, start, end, count=None): counter = count and [0, count] - return self.materialise_items(self.context, start, end, counter) + results = self.materialise_items(self.context, start, end, counter) + results.sort() + return results def materialise_item(self, current, last, next, counter): if counter is None or counter[0] < counter[1]: @@ -332,37 +353,66 @@ class WeekDayFilter(Selector): def materialise_items(self, context, start, end, counter): - first = scale(bases[self.pos], self.pos) - - # Define the step between items to be tested. - - step = scale(1, self.pos) - - current = combine(context, first) + step = scale(1, 2) results = [] - while current < end and (counter is None or counter[0] < counter[1]): - next = update(current, step) - results += self.materialise_item(current, max(current, start), min(next, end), counter) - current = next + # Get weekdays in the year. + + if len(context) == 1: + first_day = (context[0], 1, 1) + last_day = (context[0], 12, 31) + + # Get weekdays in the month. + + elif len(context) == 2: + first_day = (context[0], context[1], 1) + last_day = update((context[0], context[1], 1), (0, 1, 0)) + last_day = update(last_day, (0, 0, -1)) + + # Get weekdays in the week. + + else: + current = context + values = [value for (value, index) in self.args["values"]] + + while current < end and (counter is None or counter[0] < counter[1]): + next = update(current, step) + if date(*current).isoweekday() in values: + results += self.materialise_item(current, max(current, start), min(next, end), counter) + current = next + return results + + # Find each of the given days. + + for value, index in self.args["values"]: + if index is not None: + offset = timedelta(7 * (abs(index) - 1)) + + if index < 0: + current = to_tuple(get_last_day(last_day, value) - offset, 3) + else: + current = to_tuple(get_first_day(first_day, value) + offset, 3) + + if current < end and (counter is None or counter[0] < counter[1]): + next = update(current, step) + results += self.materialise_item(current, max(current, start), min(next, end), counter) + + else: + if index < 0: + current = to_tuple(get_last_day(last_day, value), 3) + direction = operator.sub + else: + current = to_tuple(get_first_day(first_day, value), 3) + direction = operator.add + + while first_day <= current <= last_day: + if current < end and (counter is None or counter[0] < counter[1]): + next = update(current, step) + results += self.materialise_item(current, max(current, start), min(next, end), counter) + current = to_tuple(direction(date(*current), timedelta(7)), 3) return results - def materialise_item(self, current, last, next, counter): - values = self.args["values"] - - # Select by day in week, also by occurrence in month. - - 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, last, next, counter) - - return [] - class Enum(Selector): def materialise_items(self, context, start, end, counter): step = scale(1, self.pos) @@ -398,7 +448,7 @@ for value in self.args["values"]: if value < 0: value = year_length + 1 + value - current = (first_day + timedelta(value - 1)).timetuple()[:3] + current = to_tuple(first_day + timedelta(value - 1), 3) if current < end and (counter is None or counter[0] < counter[1]): next = update(current, step) results += self.materialise_item(current, max(current, start), min(next, end), counter)