1.1 --- a/imiptools/data.py Sat Aug 01 01:25:21 2015 +0200
1.2 +++ b/imiptools/data.py Sat Aug 01 01:35:03 2015 +0200
1.3 @@ -26,7 +26,8 @@
1.4 get_datetime_item as get_item_from_datetime, \
1.5 get_datetime_tzid, \
1.6 get_duration, get_period, \
1.7 - get_tzid, to_timezone, to_utc_datetime
1.8 + get_recurrence_start_point, \
1.9 + get_tzid, to_datetime, to_timezone, to_utc_datetime
1.10 from imiptools.period import Period, RecurringPeriod, period_overlaps
1.11 from vCalendar import iterwrite, parse, ParseError, to_dict, to_node
1.12 from vRecurrence import get_parameters, get_rule
1.13 @@ -51,12 +52,44 @@
1.14
1.15 """
1.16 Return the recurrence identifier, normalised to a UTC datetime if
1.17 - specified as a datetime, converted to a date object otherwise. If no
1.18 - recurrence identifier is present, None is returned.
1.19 + specified as a datetime or date with accompanying time zone information,
1.20 + maintained as a date or floating datetime otherwise. If no recurrence
1.21 + identifier is present, None is returned.
1.22 +
1.23 + Note that this normalised form of the identifier may well not be the
1.24 + same as the originally-specified identifier because that could have been
1.25 + specified using an accompanying TZID attribute, whereas the normalised
1.26 + form is effectively a converted datetime value.
1.27 """
1.28
1.29 - recurrenceid = self.get_utc_datetime("RECURRENCE-ID")
1.30 - return recurrenceid and format_datetime(recurrenceid)
1.31 + if not self.has_key("RECURRENCE-ID"):
1.32 + return None
1.33 + dt, attr = self.get_datetime_item("RECURRENCE-ID")
1.34 + tzid = attr.get("TZID")
1.35 + if tzid:
1.36 + dt = to_timezone(to_datetime(dt, tzid), "UTC")
1.37 + return format_datetime(dt)
1.38 +
1.39 + def get_recurrence_start_point(self, recurrenceid, tzid):
1.40 +
1.41 + """
1.42 + Return the start point corresponding to the given 'recurrenceid', using
1.43 + the fallback 'tzid' to define the specific point in time referenced by
1.44 + the recurrence identifier if the identifier has a date representation.
1.45 +
1.46 + If 'recurrenceid' is given as None, this object's recurrence identifier
1.47 + is used to obtain a start point, but if this object does not provide a
1.48 + recurrence, None is returned.
1.49 +
1.50 + A start point is typically used to match free/busy periods which are
1.51 + themselves defined in terms of UTC datetimes.
1.52 + """
1.53 +
1.54 + recurrenceid = recurrenceid or self.get_recurrenceid()
1.55 + if recurrenceid:
1.56 + return get_recurrence_start_point(recurrenceid, tzid)
1.57 + else:
1.58 + return None
1.59
1.60 # Structure access.
1.61
1.62 @@ -94,20 +127,6 @@
1.63 dt, attr = t
1.64 return dt
1.65
1.66 - def set_datetime(self, name, dt, tzid):
1.67 -
1.68 - """
1.69 - Set a datetime for property 'name' using 'dt' and 'tzid', returning
1.70 - whether an update has occurred.
1.71 - """
1.72 -
1.73 - if dt:
1.74 - old_value = self.get_value(name)
1.75 - self[name] = [get_item_from_datetime(dt, tzid)]
1.76 - return format_datetime(dt) != old_value
1.77 -
1.78 - return False
1.79 -
1.80 def get_datetime_item(self, name):
1.81 return get_datetime_item(self.details, name)
1.82
1.83 @@ -159,14 +178,6 @@
1.84
1.85 return get_periods(self, tzid, end)
1.86
1.87 - def set_period(self, period):
1.88 -
1.89 - "Set the given 'period' as the main start and end."
1.90 -
1.91 - result = self.set_datetime("DTSTART", period.get_start(), period.start_attr().get("TZID"))
1.92 - result = self.set_datetime("DTEND", period.get_end(), period.end_attr().get("TZID")) or result
1.93 - return result
1.94 -
1.95 def get_tzid(self):
1.96
1.97 """
1.98 @@ -189,6 +200,54 @@
1.99
1.100 return self.get_value("SEQUENCE") is not None
1.101
1.102 + # Modification methods.
1.103 +
1.104 + def set_datetime(self, name, dt, tzid=None):
1.105 +
1.106 + """
1.107 + Set a datetime for property 'name' using 'dt' and the optional fallback
1.108 + 'tzid', returning whether an update has occurred.
1.109 + """
1.110 +
1.111 + if dt:
1.112 + old_value = self.get_value(name)
1.113 + self[name] = [get_item_from_datetime(dt, tzid)]
1.114 + return format_datetime(dt) != old_value
1.115 +
1.116 + return False
1.117 +
1.118 + def set_period(self, period):
1.119 +
1.120 + "Set the given 'period' as the main start and end."
1.121 +
1.122 + result = self.set_datetime("DTSTART", period.get_start())
1.123 + result = self.set_datetime("DTEND", period.get_end()) or result
1.124 + return result
1.125 +
1.126 + def set_periods(self, periods):
1.127 +
1.128 + """
1.129 + Set the given 'periods' as recurrence date properties, replacing the
1.130 + previous RDATE properties and ignoring any RRULE properties.
1.131 + """
1.132 +
1.133 + update = False
1.134 +
1.135 + old_values = self.get_values("RDATE")
1.136 + new_rdates = []
1.137 +
1.138 + if self.has_key("RDATE"):
1.139 + del self["RDATE"]
1.140 +
1.141 + for p in periods:
1.142 + if p.origin != "RRULE":
1.143 + new_rdates.append(get_period_item(p.get_start(), p.get_end()))
1.144 +
1.145 + self["RDATE"] = new_rdates
1.146 +
1.147 + # NOTE: To do: calculate the update status.
1.148 + return update
1.149 +
1.150 # Construction and serialisation.
1.151
1.152 def make_calendar(nodes, method=None):