1.1 --- a/vRecurrence.py Fri Jan 12 19:33:18 2018 +0100
1.2 +++ b/vRecurrence.py Fri Jan 12 19:35:17 2018 +0100
1.3 @@ -165,6 +165,7 @@
1.4
1.5 qualifiers = []
1.6 frequency = None
1.7 + have_yearday = False
1.8 interval = 1
1.9 keys = set()
1.10
1.11 @@ -198,6 +199,9 @@
1.12 # Accept result set selection, truncation and enumerators as qualifiers.
1.13
1.14 elif key in ("BYSETPOS", "COUNT") or enum.has_key(key):
1.15 + if key == "BYYEARDAY":
1.16 + have_yearday = True
1.17 +
1.18 values = get_qualifier_values(key, value)
1.19
1.20 # Ignore bad qualifier values.
1.21 @@ -213,21 +217,7 @@
1.22
1.23 elif key == "UNTIL":
1.24 try:
1.25 - # YYYYMMDD
1.26 -
1.27 - if len(value) == 8:
1.28 - end = map(int, (value[:4], value[4:6], value[6:]))
1.29 -
1.30 - # YYYYMMDDTHHMMSS[Z]
1.31 -
1.32 - elif len(value) in (15, 16):
1.33 - end = map(int, (value[:4], value[4:6], value[6:8], value[9:11], value[11:13], value[13:15]))
1.34 -
1.35 - else:
1.36 - continue
1.37 -
1.38 - qualifier = (key, {"end" : tuple(end)})
1.39 -
1.40 + qualifier = (key, {"end" : string_to_tuple(value)})
1.41 except ValueError:
1.42 continue
1.43
1.44 @@ -238,6 +228,13 @@
1.45
1.46 qualifiers.append(qualifier)
1.47
1.48 + # Forbid certain qualifiers with certain frequencies, coercing to
1.49 + # appropriate frequencies.
1.50 +
1.51 + if have_yearday and frequency and frequency[0] in ("WEEKLY", "DAILY"):
1.52 + i = qualifiers.index(frequency)
1.53 + del qualifiers[i]
1.54 +
1.55 # Parameterise any frequency qualifier with the interval.
1.56
1.57 if frequency:
1.58 @@ -588,7 +585,10 @@
1.59
1.60 "Return whether 'from_sel' can be restricted using datetime information."
1.61
1.62 - return not isinstance(from_sel, Pattern) and from_sel.qualifier != "BYDAY"
1.63 + # Patterns are not restricted by higher-scale datetime information.
1.64 + # BYDAY qualifiers are also not restricted by such information.
1.65 +
1.66 + return not isinstance(from_sel, Pattern) and from_sel.qualifier not in ("BYDAY", "BYYEARDAY")
1.67
1.68 def add_initial_selector(from_sel, level, l):
1.69
1.70 @@ -843,6 +843,33 @@
1.71 else:
1.72 return last_day - timedelta(last_weekday - weekday)
1.73
1.74 +def tuple_to_string(t):
1.75 +
1.76 + "Return datetime or date tuple 't' as a string."
1.77 +
1.78 + if len(t) >= 6:
1.79 + return "%04d%02d%02dT%02d%02d%02d" % t
1.80 + elif len(t) >= 3:
1.81 + return "%04d%02d%02d" % t
1.82 + else:
1.83 + return None
1.84 +
1.85 +def string_to_tuple(s):
1.86 +
1.87 + "Return 's' as a datetime or date tuple."
1.88 +
1.89 + # YYYYMMDD
1.90 +
1.91 + if len(s) == 8:
1.92 + return tuple(map(int, (s[:4], s[4:6], s[6:])))
1.93 +
1.94 + # YYYYMMDDTHHMMSS[Z]
1.95 +
1.96 + elif len(s) in (15, 16):
1.97 + return tuple(map(int, (s[:4], s[4:6], s[6:8], s[9:11], s[11:13], s[13:15])))
1.98 +
1.99 + raise ValueError
1.100 +
1.101 # Value expansion and sorting.
1.102
1.103 def sort_values(values, limit=None):
1.104 @@ -1046,6 +1073,14 @@
1.105 def as_tuple(self):
1.106 return self.level, self.args, self.qualifier, self.selecting, self.first
1.107
1.108 + # Serialisation support.
1.109 +
1.110 + def to_property(self):
1.111 + return ("FREQ=%s" % self.qualifier, "INTERVAL=%d" % self.get_interval())
1.112 +
1.113 + def to_string(self):
1.114 + return ";".join(self.to_property())
1.115 +
1.116 class Enum(Selector):
1.117
1.118 "A generic value selector."
1.119 @@ -1061,6 +1096,11 @@
1.120 def get_values(self, limit=None):
1.121 return sort_values(self.args["values"], limit)
1.122
1.123 + # Serialisation support.
1.124 +
1.125 + def to_property(self):
1.126 + return ("%s=%s" % (self.qualifier, ",".join(map(str, self.get_values()))), )
1.127 +
1.128 class WeekDayFilter(Enum):
1.129
1.130 "A selector of instances specified in terms of day numbers."
1.131 @@ -1113,6 +1153,14 @@
1.132 values.append(value)
1.133 return values
1.134
1.135 + # Serialisation support.
1.136 +
1.137 + def to_property(self):
1.138 + values = []
1.139 + for (value, index) in self.get_values():
1.140 + values.append("%d%s" % (index, weekday_values[value - 1]))
1.141 + return ("%s=%s" % (self.qualifier, ",".join(values)), )
1.142 +
1.143 class MonthDayFilter(Enum):
1.144
1.145 "A selector of month days."
1.146 @@ -1145,6 +1193,11 @@
1.147 def set_limit(self, limit):
1.148 self.args["values"] = [limit]
1.149
1.150 + # Serialisation support.
1.151 +
1.152 + def to_property(self):
1.153 + return ("%s=%d" % (self.qualifier, self.get_limit()), )
1.154 +
1.155 class PositionSelector(Selector):
1.156
1.157 "A result set position selector."
1.158 @@ -1170,6 +1223,11 @@
1.159 def set_positions(self, positions):
1.160 self.args["values"] = positions
1.161
1.162 + # Serialisation support.
1.163 +
1.164 + def to_property(self):
1.165 + return ("%s=%s" % (self.qualifier, ",".join(self.get_positions())), )
1.166 +
1.167 class StartSelector(Selector):
1.168
1.169 "A selector ensuring that the start occurrence is included."
1.170 @@ -1181,6 +1239,11 @@
1.171 def get_start(self):
1.172 return self.args["start"]
1.173
1.174 + # Serialisation support.
1.175 +
1.176 + def to_property(self):
1.177 + return ("%s=%s" % (self.qualifier, tuple_to_string(self.get_start())), )
1.178 +
1.179 class UntilSelector(Selector):
1.180
1.181 "A selector ensuring that the until datetime is not passed."
1.182 @@ -1192,6 +1255,11 @@
1.183 def get_end(self):
1.184 return self.args["end"]
1.185
1.186 + # Serialisation support.
1.187 +
1.188 + def to_property(self):
1.189 + return ("%s=%s" % (self.qualifier, tuple_to_string(self.get_end())), )
1.190 +
1.191 special_enum_levels = {
1.192 "BYDAY" : WeekDayFilter,
1.193 "BYMONTHDAY" : MonthDayFilter,
1.194 @@ -1666,4 +1734,24 @@
1.195 else:
1.196 return [selector]
1.197
1.198 +def to_property(selectors):
1.199 +
1.200 + "Return a list of qualifier assignments for 'selectors'."
1.201 +
1.202 + if isinstance(selectors, Selector):
1.203 + selectors = get_selectors_from_selector(selectors)
1.204 +
1.205 + t = ()
1.206 +
1.207 + for selector in selectors:
1.208 + t += selector.to_property()
1.209 +
1.210 + return t
1.211 +
1.212 +def to_string(selectors):
1.213 +
1.214 + "Return a string representation of 'selectors'."
1.215 +
1.216 + return ";".join(to_property(selectors))
1.217 +
1.218 # vim: tabstop=4 expandtab shiftwidth=4