I am trying to print a recurrence rule as a string specified by iCalendar format (see RFC 5545). Im using python dateutils, in particular dateutil.rrule to create the recurr
Although this is written four years after the question was asked, dateutil
by now has a __str__
method (see source code) which allows one to print
its objects in such a form:
In [1]: from dateutil.rrule import *
In [2]: my_rrule = rrule(DAILY, count=5)
In [3]: print(my_rrule)
DTSTART:20161202T184513
FREQ=DAILY;COUNT=5
Here's a subclass of rrule
that incorporates two suggested python-dateutil
patches which enables rrule
output. Beware that there might be good reasons that the patches have not been accepted, and I have only tested this for the simplest cases. Line folding is not handled.
See the bug tracker for discussion:
https://bugs.launchpad.net/dateutil/+bug/943512
https://bugs.launchpad.net/dateutil/+bug/943509
FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY']
class ConvertibleRRule(rrule.rrule):
# Subclass of the `rrule class that provides a sensible __str__()
# method, outputting ical formatted rrules.
# Combined from the patches in these dateutil issues:
# https://bugs.launchpad.net/dateutil/+bug/943512
# https://bugs.launchpad.net/dateutil/+bug/943509
_bysecond_internal = False
_byminute_internal = False
_byhour = False
_bymonth_internal = False
_bymonthday_internal = False
_byweekday_internal = False
def __init__(self, freq, dtstart=None,
interval=1, wkst=None, count=None, until=None, bysetpos=None,
bymonth=None, bymonthday=None, byyearday=None, byeaster=None,
byweekno=None, byweekday=None,
byhour=None, byminute=None, bysecond=None,
cache=False):
super(ConvertibleRRule, self).__init__(
freq, dtstart=dtstart,
interval=interval, wkst=wkst, count=count, until=until, bysetpos=bysetpos,
bymonth=bymonth, bymonthday=bymonthday, byyearday=byyearday, byeaster=byeaster,
byweekno=byweekno, byweekday=byweekday,
byhour=byhour, byminute=byminute, bysecond=bysecond,
cache=cache)
if (byweekno is None and byyearday is None and bymonthday is None and
byweekday is None and byeaster is None):
if freq == rrule.YEARLY:
if not bymonth:
self._bymonth_internal = True
self._bymonthday_internal = True
elif freq == rrule.MONTHLY:
self._bymonthday_internal = True
elif freq == rrule.WEEKLY:
self._byweekday_internal = True
# byhour
if byhour is None:
if freq < rrule.HOURLY:
self._byhour_internal = True
# byminute
if byminute is None:
if freq < rrule.MINUTELY:
self._byminute_internal = True
# bysecond
if bysecond is None:
if freq < rrule.SECONDLY:
self._bysecond_internal = True
freq = property(lambda s: s._freq)
dtstart = property(lambda s: s._dtstart)
interval = property(lambda s: s._interval)
@property
def wkst(self):
if self._wkst == rrule.calendar.firstweekday():
return None
return rrule.weekday(self._wkst)
count = property(lambda s: s._count)
until = property(lambda s: s._until)
bysetpos = property(lambda s: s._bysetpos)
@property
def bymonth(self):
if self._bymonth_internal:
return None
return self._bymonth
@property
def bymonthday(self):
if self._bymonthday_internal:
return None
return self._bymonthday + self._bynmonthday
byyearday = property(lambda s: s._byyearday)
byeaster = property(lambda s: s._byeaster)
byweekno = property(lambda s: s._byweekno)
@property
def byweekday(self):
if self._byweekday_internal:
return None
bynweekday, byweekday = (), ()
if self._bynweekday:
bynweekday = tuple(rrule.weekday(d, n) for d, n in self._bynweekday)
if self._byweekday:
byweekday = tuple(rrule.weekday(d) for d in self._byweekday)
return bynweekday + byweekday
@property
def byhour(self):
if self._byhour_internal:
return None
return self._byhour
@property
def byminute(self):
if self._byminute_internal:
return None
return self._byminute
@property
def bysecond(self):
if self._bysecond_internal:
return None
return self._bysecond
def __str__(self):
parts = ['FREQ=' + FREQNAMES[self.freq]]
if self.interval != 1:
parts.append('INTERVAL=' + str(self.interval))
if self.wkst:
parts.append('WKST=' + str(self.wkst))
if self.count:
parts.append('COUNT=' + str(self.count))
for name, value in [
('BYSETPOS', self.bysetpos),
('BYMONTH', self.bymonth),
('BYMONTHDAY', self.bymonthday),
('BYYEARDAY', self.byyearday),
('BYWEEKNO', self.byweekno),
('BYWEEKDAY', self.byweekday),
('BYHOUR', self.byhour),
('BYMINUTE', self.byminute),
('BYSECOND', self.bysecond),
]:
if value:
parts.append(name + '=' + ','.join(str(v) for v in value))
return ';'.join(parts)
There is no method or function in the python-dateutil
package to do this. See this bug for a patch that may help: https://bugs.launchpad.net/dateutil/+bug/943512.