1.1 --- a/vRecurrence.py Sat Oct 04 20:05:17 2014 +0200
1.2 +++ b/vRecurrence.py Mon Oct 06 00:17:06 2014 +0200
1.3 @@ -157,6 +157,7 @@
1.4
1.5 have_q = False
1.6 context = []
1.7 + context.append(from_dt.args["values"][0])
1.8
1.9 # Consume from both lists, merging entries.
1.10
1.11 @@ -167,8 +168,8 @@
1.12 # Datetime value at wider resolution.
1.13
1.14 if _pos < pos:
1.15 + from_dt = get_next(iter_dt)
1.16 context.append(from_dt.args["values"][0])
1.17 - from_dt = get_next(iter_dt)
1.18
1.19 # Qualifier at wider or same resolution as datetime value.
1.20
1.21 @@ -190,9 +191,9 @@
1.22 # Or combine the qualifier and value details.
1.23
1.24 else:
1.25 - context.append(from_dt.args["values"][0])
1.26 l.append(combine_context_with_qualifier(context, from_q))
1.27 from_dt = get_next(iter_dt)
1.28 + context.append(from_dt.args["values"][0])
1.29
1.30 from_q = get_next(iter_q)
1.31
1.32 @@ -223,8 +224,7 @@
1.33 imposing the datetime value information on any qualifiers.
1.34 """
1.35
1.36 - if not from_q.args.has_key("values"):
1.37 - from_q.context = tuple(context)
1.38 + from_q.context = tuple(context)
1.39 return from_q
1.40
1.41 # Datetime arithmetic.
1.42 @@ -276,7 +276,26 @@
1.43 d = datetime(*updated_for_months)
1.44 s = timedelta(step[2], get_seconds(step))
1.45
1.46 - return (d + s).timetuple()[:len(t)]
1.47 + return to_tuple(d + s, len(t))
1.48 +
1.49 +def to_tuple(d, n):
1.50 + return d.timetuple()[:n]
1.51 +
1.52 +def get_first_day(first_day, weekday):
1.53 + first_day = date(*first_day)
1.54 + first_weekday = first_day.isoweekday()
1.55 + if first_weekday > weekday:
1.56 + return first_day + timedelta(7 - first_weekday + weekday)
1.57 + else:
1.58 + return first_day + timedelta(weekday - first_weekday)
1.59 +
1.60 +def get_last_day(last_day, weekday):
1.61 + last_day = date(*last_day)
1.62 + last_weekday = last_day.isoweekday()
1.63 + if last_weekday < weekday:
1.64 + return last_day - timedelta(last_weekday + 7 - weekday)
1.65 + else:
1.66 + return last_day - timedelta(last_weekday - weekday)
1.67
1.68 # Classes for producing instances from recurrence structures.
1.69
1.70 @@ -293,7 +312,9 @@
1.71
1.72 def materialise(self, start, end, count=None):
1.73 counter = count and [0, count]
1.74 - return self.materialise_items(self.context, start, end, counter)
1.75 + results = self.materialise_items(self.context, start, end, counter)
1.76 + results.sort()
1.77 + return results
1.78
1.79 def materialise_item(self, current, last, next, counter):
1.80 if counter is None or counter[0] < counter[1]:
1.81 @@ -332,37 +353,66 @@
1.82
1.83 class WeekDayFilter(Selector):
1.84 def materialise_items(self, context, start, end, counter):
1.85 - first = scale(bases[self.pos], self.pos)
1.86 -
1.87 - # Define the step between items to be tested.
1.88 -
1.89 - step = scale(1, self.pos)
1.90 -
1.91 - current = combine(context, first)
1.92 + step = scale(1, 2)
1.93 results = []
1.94
1.95 - while current < end and (counter is None or counter[0] < counter[1]):
1.96 - next = update(current, step)
1.97 - results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.98 - current = next
1.99 + # Get weekdays in the year.
1.100 +
1.101 + if len(context) == 1:
1.102 + first_day = (context[0], 1, 1)
1.103 + last_day = (context[0], 12, 31)
1.104 +
1.105 + # Get weekdays in the month.
1.106 +
1.107 + elif len(context) == 2:
1.108 + first_day = (context[0], context[1], 1)
1.109 + last_day = update((context[0], context[1], 1), (0, 1, 0))
1.110 + last_day = update(last_day, (0, 0, -1))
1.111 +
1.112 + # Get weekdays in the week.
1.113 +
1.114 + else:
1.115 + current = context
1.116 + values = [value for (value, index) in self.args["values"]]
1.117 +
1.118 + while current < end and (counter is None or counter[0] < counter[1]):
1.119 + next = update(current, step)
1.120 + if date(*current).isoweekday() in values:
1.121 + results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.122 + current = next
1.123 + return results
1.124 +
1.125 + # Find each of the given days.
1.126 +
1.127 + for value, index in self.args["values"]:
1.128 + if index is not None:
1.129 + offset = timedelta(7 * (abs(index) - 1))
1.130 +
1.131 + if index < 0:
1.132 + current = to_tuple(get_last_day(last_day, value) - offset, 3)
1.133 + else:
1.134 + current = to_tuple(get_first_day(first_day, value) + offset, 3)
1.135 +
1.136 + if current < end and (counter is None or counter[0] < counter[1]):
1.137 + next = update(current, step)
1.138 + results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.139 +
1.140 + else:
1.141 + if index < 0:
1.142 + current = to_tuple(get_last_day(last_day, value), 3)
1.143 + direction = operator.sub
1.144 + else:
1.145 + current = to_tuple(get_first_day(first_day, value), 3)
1.146 + direction = operator.add
1.147 +
1.148 + while first_day <= current <= last_day:
1.149 + if current < end and (counter is None or counter[0] < counter[1]):
1.150 + next = update(current, step)
1.151 + results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.152 + current = to_tuple(direction(date(*current), timedelta(7)), 3)
1.153
1.154 return results
1.155
1.156 - def materialise_item(self, current, last, next, counter):
1.157 - values = self.args["values"]
1.158 -
1.159 - # Select by day in week, also by occurrence in month.
1.160 -
1.161 - for value, index in values:
1.162 - if datetime(*current).isoweekday() == value:
1.163 - last_day = monthrange(current[0], current[1])[1]
1.164 - index_from_start = (current[2] - 1) / 7
1.165 - index_from_end = -(1 + (last_day - current[2]) / 7)
1.166 - if index is None or index in (index_from_start, index_from_end):
1.167 - return Selector.materialise_item(self, current, last, next, counter)
1.168 -
1.169 - return []
1.170 -
1.171 class Enum(Selector):
1.172 def materialise_items(self, context, start, end, counter):
1.173 step = scale(1, self.pos)
1.174 @@ -398,7 +448,7 @@
1.175 for value in self.args["values"]:
1.176 if value < 0:
1.177 value = year_length + 1 + value
1.178 - current = (first_day + timedelta(value - 1)).timetuple()[:3]
1.179 + current = to_tuple(first_day + timedelta(value - 1), 3)
1.180 if current < end and (counter is None or counter[0] < counter[1]):
1.181 next = update(current, step)
1.182 results += self.materialise_item(current, max(current, start), min(next, end), counter)