diff env/lib/python3.7/site-packages/dateutil/rrule.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400
parents 79f47841a781
children
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/dateutil/rrule.py	Thu May 14 16:47:39 2020 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1735 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-The rrule module offers a small, complete, and very fast, implementation of
-the recurrence rules documented in the
-`iCalendar RFC <https://tools.ietf.org/html/rfc5545>`_,
-including support for caching of results.
-"""
-import itertools
-import datetime
-import calendar
-import re
-import sys
-
-try:
-    from math import gcd
-except ImportError:
-    from fractions import gcd
-
-from six import advance_iterator, integer_types
-from six.moves import _thread, range
-import heapq
-
-from ._common import weekday as weekdaybase
-
-# For warning about deprecation of until and count
-from warnings import warn
-
-__all__ = ["rrule", "rruleset", "rrulestr",
-           "YEARLY", "MONTHLY", "WEEKLY", "DAILY",
-           "HOURLY", "MINUTELY", "SECONDLY",
-           "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
-
-# Every mask is 7 days longer to handle cross-year weekly periods.
-M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 +
-                 [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7)
-M365MASK = list(M366MASK)
-M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32))
-MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7])
-MDAY365MASK = list(MDAY366MASK)
-M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0))
-NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7])
-NMDAY365MASK = list(NMDAY366MASK)
-M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366)
-M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
-WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55
-del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31]
-MDAY365MASK = tuple(MDAY365MASK)
-M365MASK = tuple(M365MASK)
-
-FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY']
-
-(YEARLY,
- MONTHLY,
- WEEKLY,
- DAILY,
- HOURLY,
- MINUTELY,
- SECONDLY) = list(range(7))
-
-# Imported on demand.
-easter = None
-parser = None
-
-
-class weekday(weekdaybase):
-    """
-    This version of weekday does not allow n = 0.
-    """
-    def __init__(self, wkday, n=None):
-        if n == 0:
-            raise ValueError("Can't create weekday with n==0")
-
-        super(weekday, self).__init__(wkday, n)
-
-
-MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7))
-
-
-def _invalidates_cache(f):
-    """
-    Decorator for rruleset methods which may invalidate the
-    cached length.
-    """
-    def inner_func(self, *args, **kwargs):
-        rv = f(self, *args, **kwargs)
-        self._invalidate_cache()
-        return rv
-
-    return inner_func
-
-
-class rrulebase(object):
-    def __init__(self, cache=False):
-        if cache:
-            self._cache = []
-            self._cache_lock = _thread.allocate_lock()
-            self._invalidate_cache()
-        else:
-            self._cache = None
-            self._cache_complete = False
-            self._len = None
-
-    def __iter__(self):
-        if self._cache_complete:
-            return iter(self._cache)
-        elif self._cache is None:
-            return self._iter()
-        else:
-            return self._iter_cached()
-
-    def _invalidate_cache(self):
-        if self._cache is not None:
-            self._cache = []
-            self._cache_complete = False
-            self._cache_gen = self._iter()
-
-            if self._cache_lock.locked():
-                self._cache_lock.release()
-
-        self._len = None
-
-    def _iter_cached(self):
-        i = 0
-        gen = self._cache_gen
-        cache = self._cache
-        acquire = self._cache_lock.acquire
-        release = self._cache_lock.release
-        while gen:
-            if i == len(cache):
-                acquire()
-                if self._cache_complete:
-                    break
-                try:
-                    for j in range(10):
-                        cache.append(advance_iterator(gen))
-                except StopIteration:
-                    self._cache_gen = gen = None
-                    self._cache_complete = True
-                    break
-                release()
-            yield cache[i]
-            i += 1
-        while i < self._len:
-            yield cache[i]
-            i += 1
-
-    def __getitem__(self, item):
-        if self._cache_complete:
-            return self._cache[item]
-        elif isinstance(item, slice):
-            if item.step and item.step < 0:
-                return list(iter(self))[item]
-            else:
-                return list(itertools.islice(self,
-                                             item.start or 0,
-                                             item.stop or sys.maxsize,
-                                             item.step or 1))
-        elif item >= 0:
-            gen = iter(self)
-            try:
-                for i in range(item+1):
-                    res = advance_iterator(gen)
-            except StopIteration:
-                raise IndexError
-            return res
-        else:
-            return list(iter(self))[item]
-
-    def __contains__(self, item):
-        if self._cache_complete:
-            return item in self._cache
-        else:
-            for i in self:
-                if i == item:
-                    return True
-                elif i > item:
-                    return False
-        return False
-
-    # __len__() introduces a large performance penalty.
-    def count(self):
-        """ Returns the number of recurrences in this set. It will have go
-            trough the whole recurrence, if this hasn't been done before. """
-        if self._len is None:
-            for x in self:
-                pass
-        return self._len
-
-    def before(self, dt, inc=False):
-        """ Returns the last recurrence before the given datetime instance. The
-            inc keyword defines what happens if dt is an occurrence. With
-            inc=True, if dt itself is an occurrence, it will be returned. """
-        if self._cache_complete:
-            gen = self._cache
-        else:
-            gen = self
-        last = None
-        if inc:
-            for i in gen:
-                if i > dt:
-                    break
-                last = i
-        else:
-            for i in gen:
-                if i >= dt:
-                    break
-                last = i
-        return last
-
-    def after(self, dt, inc=False):
-        """ Returns the first recurrence after the given datetime instance. The
-            inc keyword defines what happens if dt is an occurrence. With
-            inc=True, if dt itself is an occurrence, it will be returned.  """
-        if self._cache_complete:
-            gen = self._cache
-        else:
-            gen = self
-        if inc:
-            for i in gen:
-                if i >= dt:
-                    return i
-        else:
-            for i in gen:
-                if i > dt:
-                    return i
-        return None
-
-    def xafter(self, dt, count=None, inc=False):
-        """
-        Generator which yields up to `count` recurrences after the given
-        datetime instance, equivalent to `after`.
-
-        :param dt:
-            The datetime at which to start generating recurrences.
-
-        :param count:
-            The maximum number of recurrences to generate. If `None` (default),
-            dates are generated until the recurrence rule is exhausted.
-
-        :param inc:
-            If `dt` is an instance of the rule and `inc` is `True`, it is
-            included in the output.
-
-        :yields: Yields a sequence of `datetime` objects.
-        """
-
-        if self._cache_complete:
-            gen = self._cache
-        else:
-            gen = self
-
-        # Select the comparison function
-        if inc:
-            comp = lambda dc, dtc: dc >= dtc
-        else:
-            comp = lambda dc, dtc: dc > dtc
-
-        # Generate dates
-        n = 0
-        for d in gen:
-            if comp(d, dt):
-                if count is not None:
-                    n += 1
-                    if n > count:
-                        break
-
-                yield d
-
-    def between(self, after, before, inc=False, count=1):
-        """ Returns all the occurrences of the rrule between after and before.
-        The inc keyword defines what happens if after and/or before are
-        themselves occurrences. With inc=True, they will be included in the
-        list, if they are found in the recurrence set. """
-        if self._cache_complete:
-            gen = self._cache
-        else:
-            gen = self
-        started = False
-        l = []
-        if inc:
-            for i in gen:
-                if i > before:
-                    break
-                elif not started:
-                    if i >= after:
-                        started = True
-                        l.append(i)
-                else:
-                    l.append(i)
-        else:
-            for i in gen:
-                if i >= before:
-                    break
-                elif not started:
-                    if i > after:
-                        started = True
-                        l.append(i)
-                else:
-                    l.append(i)
-        return l
-
-
-class rrule(rrulebase):
-    """
-    That's the base of the rrule operation. It accepts all the keywords
-    defined in the RFC as its constructor parameters (except byday,
-    which was renamed to byweekday) and more. The constructor prototype is::
-
-            rrule(freq)
-
-    Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY,
-    or SECONDLY.
-
-    .. note::
-        Per RFC section 3.3.10, recurrence instances falling on invalid dates
-        and times are ignored rather than coerced:
-
-            Recurrence rules may generate recurrence instances with an invalid
-            date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM
-            on a day where the local time is moved forward by an hour at 1:00
-            AM).  Such recurrence instances MUST be ignored and MUST NOT be
-            counted as part of the recurrence set.
-
-        This can lead to possibly surprising behavior when, for example, the
-        start date occurs at the end of the month:
-
-        >>> from dateutil.rrule import rrule, MONTHLY
-        >>> from datetime import datetime
-        >>> start_date = datetime(2014, 12, 31)
-        >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date))
-        ... # doctest: +NORMALIZE_WHITESPACE
-        [datetime.datetime(2014, 12, 31, 0, 0),
-         datetime.datetime(2015, 1, 31, 0, 0),
-         datetime.datetime(2015, 3, 31, 0, 0),
-         datetime.datetime(2015, 5, 31, 0, 0)]
-
-    Additionally, it supports the following keyword arguments:
-
-    :param dtstart:
-        The recurrence start. Besides being the base for the recurrence,
-        missing parameters in the final recurrence instances will also be
-        extracted from this date. If not given, datetime.now() will be used
-        instead.
-    :param interval:
-        The interval between each freq iteration. For example, when using
-        YEARLY, an interval of 2 means once every two years, but with HOURLY,
-        it means once every two hours. The default interval is 1.
-    :param wkst:
-        The week start day. Must be one of the MO, TU, WE constants, or an
-        integer, specifying the first day of the week. This will affect
-        recurrences based on weekly periods. The default week start is got
-        from calendar.firstweekday(), and may be modified by
-        calendar.setfirstweekday().
-    :param count:
-        If given, this determines how many occurrences will be generated.
-
-        .. note::
-            As of version 2.5.0, the use of the keyword ``until`` in conjunction
-            with ``count`` is deprecated, to make sure ``dateutil`` is fully
-            compliant with `RFC-5545 Sec. 3.3.10 <https://tools.ietf.org/
-            html/rfc5545#section-3.3.10>`_. Therefore, ``until`` and ``count``
-            **must not** occur in the same call to ``rrule``.
-    :param until:
-        If given, this must be a datetime instance specifying the upper-bound
-        limit of the recurrence. The last recurrence in the rule is the greatest
-        datetime that is less than or equal to the value specified in the
-        ``until`` parameter.
-
-        .. note::
-            As of version 2.5.0, the use of the keyword ``until`` in conjunction
-            with ``count`` is deprecated, to make sure ``dateutil`` is fully
-            compliant with `RFC-5545 Sec. 3.3.10 <https://tools.ietf.org/
-            html/rfc5545#section-3.3.10>`_. Therefore, ``until`` and ``count``
-            **must not** occur in the same call to ``rrule``.
-    :param bysetpos:
-        If given, it must be either an integer, or a sequence of integers,
-        positive or negative. Each given integer will specify an occurrence
-        number, corresponding to the nth occurrence of the rule inside the
-        frequency period. For example, a bysetpos of -1 if combined with a
-        MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will
-        result in the last work day of every month.
-    :param bymonth:
-        If given, it must be either an integer, or a sequence of integers,
-        meaning the months to apply the recurrence to.
-    :param bymonthday:
-        If given, it must be either an integer, or a sequence of integers,
-        meaning the month days to apply the recurrence to.
-    :param byyearday:
-        If given, it must be either an integer, or a sequence of integers,
-        meaning the year days to apply the recurrence to.
-    :param byeaster:
-        If given, it must be either an integer, or a sequence of integers,
-        positive or negative. Each integer will define an offset from the
-        Easter Sunday. Passing the offset 0 to byeaster will yield the Easter
-        Sunday itself. This is an extension to the RFC specification.
-    :param byweekno:
-        If given, it must be either an integer, or a sequence of integers,
-        meaning the week numbers to apply the recurrence to. Week numbers
-        have the meaning described in ISO8601, that is, the first week of
-        the year is that containing at least four days of the new year.
-    :param byweekday:
-        If given, it must be either an integer (0 == MO), a sequence of
-        integers, one of the weekday constants (MO, TU, etc), or a sequence
-        of these constants. When given, these variables will define the
-        weekdays where the recurrence will be applied. It's also possible to
-        use an argument n for the weekday instances, which will mean the nth
-        occurrence of this weekday in the period. For example, with MONTHLY,
-        or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the
-        first friday of the month where the recurrence happens. Notice that in
-        the RFC documentation, this is specified as BYDAY, but was renamed to
-        avoid the ambiguity of that keyword.
-    :param byhour:
-        If given, it must be either an integer, or a sequence of integers,
-        meaning the hours to apply the recurrence to.
-    :param byminute:
-        If given, it must be either an integer, or a sequence of integers,
-        meaning the minutes to apply the recurrence to.
-    :param bysecond:
-        If given, it must be either an integer, or a sequence of integers,
-        meaning the seconds to apply the recurrence to.
-    :param cache:
-        If given, it must be a boolean value specifying to enable or disable
-        caching of results. If you will use the same rrule instance multiple
-        times, enabling caching will improve the performance considerably.
-     """
-    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(rrule, self).__init__(cache)
-        global easter
-        if not dtstart:
-            if until and until.tzinfo:
-                dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0)
-            else:
-                dtstart = datetime.datetime.now().replace(microsecond=0)
-        elif not isinstance(dtstart, datetime.datetime):
-            dtstart = datetime.datetime.fromordinal(dtstart.toordinal())
-        else:
-            dtstart = dtstart.replace(microsecond=0)
-        self._dtstart = dtstart
-        self._tzinfo = dtstart.tzinfo
-        self._freq = freq
-        self._interval = interval
-        self._count = count
-
-        # Cache the original byxxx rules, if they are provided, as the _byxxx
-        # attributes do not necessarily map to the inputs, and this can be
-        # a problem in generating the strings. Only store things if they've
-        # been supplied (the string retrieval will just use .get())
-        self._original_rule = {}
-
-        if until and not isinstance(until, datetime.datetime):
-            until = datetime.datetime.fromordinal(until.toordinal())
-        self._until = until
-
-        if self._dtstart and self._until:
-            if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None):
-                # According to RFC5545 Section 3.3.10:
-                # https://tools.ietf.org/html/rfc5545#section-3.3.10
-                #
-                # > If the "DTSTART" property is specified as a date with UTC
-                # > time or a date with local time and time zone reference,
-                # > then the UNTIL rule part MUST be specified as a date with
-                # > UTC time.
-                raise ValueError(
-                    'RRULE UNTIL values must be specified in UTC when DTSTART '
-                    'is timezone-aware'
-                )
-
-        if count is not None and until:
-            warn("Using both 'count' and 'until' is inconsistent with RFC 5545"
-                 " and has been deprecated in dateutil. Future versions will "
-                 "raise an error.", DeprecationWarning)
-
-        if wkst is None:
-            self._wkst = calendar.firstweekday()
-        elif isinstance(wkst, integer_types):
-            self._wkst = wkst
-        else:
-            self._wkst = wkst.weekday
-
-        if bysetpos is None:
-            self._bysetpos = None
-        elif isinstance(bysetpos, integer_types):
-            if bysetpos == 0 or not (-366 <= bysetpos <= 366):
-                raise ValueError("bysetpos must be between 1 and 366, "
-                                 "or between -366 and -1")
-            self._bysetpos = (bysetpos,)
-        else:
-            self._bysetpos = tuple(bysetpos)
-            for pos in self._bysetpos:
-                if pos == 0 or not (-366 <= pos <= 366):
-                    raise ValueError("bysetpos must be between 1 and 366, "
-                                     "or between -366 and -1")
-
-        if self._bysetpos:
-            self._original_rule['bysetpos'] = self._bysetpos
-
-        if (byweekno is None and byyearday is None and bymonthday is None and
-                byweekday is None and byeaster is None):
-            if freq == YEARLY:
-                if bymonth is None:
-                    bymonth = dtstart.month
-                    self._original_rule['bymonth'] = None
-                bymonthday = dtstart.day
-                self._original_rule['bymonthday'] = None
-            elif freq == MONTHLY:
-                bymonthday = dtstart.day
-                self._original_rule['bymonthday'] = None
-            elif freq == WEEKLY:
-                byweekday = dtstart.weekday()
-                self._original_rule['byweekday'] = None
-
-        # bymonth
-        if bymonth is None:
-            self._bymonth = None
-        else:
-            if isinstance(bymonth, integer_types):
-                bymonth = (bymonth,)
-
-            self._bymonth = tuple(sorted(set(bymonth)))
-
-            if 'bymonth' not in self._original_rule:
-                self._original_rule['bymonth'] = self._bymonth
-
-        # byyearday
-        if byyearday is None:
-            self._byyearday = None
-        else:
-            if isinstance(byyearday, integer_types):
-                byyearday = (byyearday,)
-
-            self._byyearday = tuple(sorted(set(byyearday)))
-            self._original_rule['byyearday'] = self._byyearday
-
-        # byeaster
-        if byeaster is not None:
-            if not easter:
-                from dateutil import easter
-            if isinstance(byeaster, integer_types):
-                self._byeaster = (byeaster,)
-            else:
-                self._byeaster = tuple(sorted(byeaster))
-
-            self._original_rule['byeaster'] = self._byeaster
-        else:
-            self._byeaster = None
-
-        # bymonthday
-        if bymonthday is None:
-            self._bymonthday = ()
-            self._bynmonthday = ()
-        else:
-            if isinstance(bymonthday, integer_types):
-                bymonthday = (bymonthday,)
-
-            bymonthday = set(bymonthday)            # Ensure it's unique
-
-            self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0))
-            self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0))
-
-            # Storing positive numbers first, then negative numbers
-            if 'bymonthday' not in self._original_rule:
-                self._original_rule['bymonthday'] = tuple(
-                    itertools.chain(self._bymonthday, self._bynmonthday))
-
-        # byweekno
-        if byweekno is None:
-            self._byweekno = None
-        else:
-            if isinstance(byweekno, integer_types):
-                byweekno = (byweekno,)
-
-            self._byweekno = tuple(sorted(set(byweekno)))
-
-            self._original_rule['byweekno'] = self._byweekno
-
-        # byweekday / bynweekday
-        if byweekday is None:
-            self._byweekday = None
-            self._bynweekday = None
-        else:
-            # If it's one of the valid non-sequence types, convert to a
-            # single-element sequence before the iterator that builds the
-            # byweekday set.
-            if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"):
-                byweekday = (byweekday,)
-
-            self._byweekday = set()
-            self._bynweekday = set()
-            for wday in byweekday:
-                if isinstance(wday, integer_types):
-                    self._byweekday.add(wday)
-                elif not wday.n or freq > MONTHLY:
-                    self._byweekday.add(wday.weekday)
-                else:
-                    self._bynweekday.add((wday.weekday, wday.n))
-
-            if not self._byweekday:
-                self._byweekday = None
-            elif not self._bynweekday:
-                self._bynweekday = None
-
-            if self._byweekday is not None:
-                self._byweekday = tuple(sorted(self._byweekday))
-                orig_byweekday = [weekday(x) for x in self._byweekday]
-            else:
-                orig_byweekday = ()
-
-            if self._bynweekday is not None:
-                self._bynweekday = tuple(sorted(self._bynweekday))
-                orig_bynweekday = [weekday(*x) for x in self._bynweekday]
-            else:
-                orig_bynweekday = ()
-
-            if 'byweekday' not in self._original_rule:
-                self._original_rule['byweekday'] = tuple(itertools.chain(
-                    orig_byweekday, orig_bynweekday))
-
-        # byhour
-        if byhour is None:
-            if freq < HOURLY:
-                self._byhour = {dtstart.hour}
-            else:
-                self._byhour = None
-        else:
-            if isinstance(byhour, integer_types):
-                byhour = (byhour,)
-
-            if freq == HOURLY:
-                self._byhour = self.__construct_byset(start=dtstart.hour,
-                                                      byxxx=byhour,
-                                                      base=24)
-            else:
-                self._byhour = set(byhour)
-
-            self._byhour = tuple(sorted(self._byhour))
-            self._original_rule['byhour'] = self._byhour
-
-        # byminute
-        if byminute is None:
-            if freq < MINUTELY:
-                self._byminute = {dtstart.minute}
-            else:
-                self._byminute = None
-        else:
-            if isinstance(byminute, integer_types):
-                byminute = (byminute,)
-
-            if freq == MINUTELY:
-                self._byminute = self.__construct_byset(start=dtstart.minute,
-                                                        byxxx=byminute,
-                                                        base=60)
-            else:
-                self._byminute = set(byminute)
-
-            self._byminute = tuple(sorted(self._byminute))
-            self._original_rule['byminute'] = self._byminute
-
-        # bysecond
-        if bysecond is None:
-            if freq < SECONDLY:
-                self._bysecond = ((dtstart.second,))
-            else:
-                self._bysecond = None
-        else:
-            if isinstance(bysecond, integer_types):
-                bysecond = (bysecond,)
-
-            self._bysecond = set(bysecond)
-
-            if freq == SECONDLY:
-                self._bysecond = self.__construct_byset(start=dtstart.second,
-                                                        byxxx=bysecond,
-                                                        base=60)
-            else:
-                self._bysecond = set(bysecond)
-
-            self._bysecond = tuple(sorted(self._bysecond))
-            self._original_rule['bysecond'] = self._bysecond
-
-        if self._freq >= HOURLY:
-            self._timeset = None
-        else:
-            self._timeset = []
-            for hour in self._byhour:
-                for minute in self._byminute:
-                    for second in self._bysecond:
-                        self._timeset.append(
-                            datetime.time(hour, minute, second,
-                                          tzinfo=self._tzinfo))
-            self._timeset.sort()
-            self._timeset = tuple(self._timeset)
-
-    def __str__(self):
-        """
-        Output a string that would generate this RRULE if passed to rrulestr.
-        This is mostly compatible with RFC5545, except for the
-        dateutil-specific extension BYEASTER.
-        """
-
-        output = []
-        h, m, s = [None] * 3
-        if self._dtstart:
-            output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S'))
-            h, m, s = self._dtstart.timetuple()[3:6]
-
-        parts = ['FREQ=' + FREQNAMES[self._freq]]
-        if self._interval != 1:
-            parts.append('INTERVAL=' + str(self._interval))
-
-        if self._wkst:
-            parts.append('WKST=' + repr(weekday(self._wkst))[0:2])
-
-        if self._count is not None:
-            parts.append('COUNT=' + str(self._count))
-
-        if self._until:
-            parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S'))
-
-        if self._original_rule.get('byweekday') is not None:
-            # The str() method on weekday objects doesn't generate
-            # RFC5545-compliant strings, so we should modify that.
-            original_rule = dict(self._original_rule)
-            wday_strings = []
-            for wday in original_rule['byweekday']:
-                if wday.n:
-                    wday_strings.append('{n:+d}{wday}'.format(
-                        n=wday.n,
-                        wday=repr(wday)[0:2]))
-                else:
-                    wday_strings.append(repr(wday))
-
-            original_rule['byweekday'] = wday_strings
-        else:
-            original_rule = self._original_rule
-
-        partfmt = '{name}={vals}'
-        for name, key in [('BYSETPOS', 'bysetpos'),
-                          ('BYMONTH', 'bymonth'),
-                          ('BYMONTHDAY', 'bymonthday'),
-                          ('BYYEARDAY', 'byyearday'),
-                          ('BYWEEKNO', 'byweekno'),
-                          ('BYDAY', 'byweekday'),
-                          ('BYHOUR', 'byhour'),
-                          ('BYMINUTE', 'byminute'),
-                          ('BYSECOND', 'bysecond'),
-                          ('BYEASTER', 'byeaster')]:
-            value = original_rule.get(key)
-            if value:
-                parts.append(partfmt.format(name=name, vals=(','.join(str(v)
-                                                             for v in value))))
-
-        output.append('RRULE:' + ';'.join(parts))
-        return '\n'.join(output)
-
-    def replace(self, **kwargs):
-        """Return new rrule with same attributes except for those attributes given new
-           values by whichever keyword arguments are specified."""
-        new_kwargs = {"interval": self._interval,
-                      "count": self._count,
-                      "dtstart": self._dtstart,
-                      "freq": self._freq,
-                      "until": self._until,
-                      "wkst": self._wkst,
-                      "cache": False if self._cache is None else True }
-        new_kwargs.update(self._original_rule)
-        new_kwargs.update(kwargs)
-        return rrule(**new_kwargs)
-
-    def _iter(self):
-        year, month, day, hour, minute, second, weekday, yearday, _ = \
-            self._dtstart.timetuple()
-
-        # Some local variables to speed things up a bit
-        freq = self._freq
-        interval = self._interval
-        wkst = self._wkst
-        until = self._until
-        bymonth = self._bymonth
-        byweekno = self._byweekno
-        byyearday = self._byyearday
-        byweekday = self._byweekday
-        byeaster = self._byeaster
-        bymonthday = self._bymonthday
-        bynmonthday = self._bynmonthday
-        bysetpos = self._bysetpos
-        byhour = self._byhour
-        byminute = self._byminute
-        bysecond = self._bysecond
-
-        ii = _iterinfo(self)
-        ii.rebuild(year, month)
-
-        getdayset = {YEARLY: ii.ydayset,
-                     MONTHLY: ii.mdayset,
-                     WEEKLY: ii.wdayset,
-                     DAILY: ii.ddayset,
-                     HOURLY: ii.ddayset,
-                     MINUTELY: ii.ddayset,
-                     SECONDLY: ii.ddayset}[freq]
-
-        if freq < HOURLY:
-            timeset = self._timeset
-        else:
-            gettimeset = {HOURLY: ii.htimeset,
-                          MINUTELY: ii.mtimeset,
-                          SECONDLY: ii.stimeset}[freq]
-            if ((freq >= HOURLY and
-                 self._byhour and hour not in self._byhour) or
-                (freq >= MINUTELY and
-                 self._byminute and minute not in self._byminute) or
-                (freq >= SECONDLY and
-                 self._bysecond and second not in self._bysecond)):
-                timeset = ()
-            else:
-                timeset = gettimeset(hour, minute, second)
-
-        total = 0
-        count = self._count
-        while True:
-            # Get dayset with the right frequency
-            dayset, start, end = getdayset(year, month, day)
-
-            # Do the "hard" work ;-)
-            filtered = False
-            for i in dayset[start:end]:
-                if ((bymonth and ii.mmask[i] not in bymonth) or
-                    (byweekno and not ii.wnomask[i]) or
-                    (byweekday and ii.wdaymask[i] not in byweekday) or
-                    (ii.nwdaymask and not ii.nwdaymask[i]) or
-                    (byeaster and not ii.eastermask[i]) or
-                    ((bymonthday or bynmonthday) and
-                     ii.mdaymask[i] not in bymonthday and
-                     ii.nmdaymask[i] not in bynmonthday) or
-                    (byyearday and
-                     ((i < ii.yearlen and i+1 not in byyearday and
-                       -ii.yearlen+i not in byyearday) or
-                      (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and
-                       -ii.nextyearlen+i-ii.yearlen not in byyearday)))):
-                    dayset[i] = None
-                    filtered = True
-
-            # Output results
-            if bysetpos and timeset:
-                poslist = []
-                for pos in bysetpos:
-                    if pos < 0:
-                        daypos, timepos = divmod(pos, len(timeset))
-                    else:
-                        daypos, timepos = divmod(pos-1, len(timeset))
-                    try:
-                        i = [x for x in dayset[start:end]
-                             if x is not None][daypos]
-                        time = timeset[timepos]
-                    except IndexError:
-                        pass
-                    else:
-                        date = datetime.date.fromordinal(ii.yearordinal+i)
-                        res = datetime.datetime.combine(date, time)
-                        if res not in poslist:
-                            poslist.append(res)
-                poslist.sort()
-                for res in poslist:
-                    if until and res > until:
-                        self._len = total
-                        return
-                    elif res >= self._dtstart:
-                        if count is not None:
-                            count -= 1
-                            if count < 0:
-                                self._len = total
-                                return
-                        total += 1
-                        yield res
-            else:
-                for i in dayset[start:end]:
-                    if i is not None:
-                        date = datetime.date.fromordinal(ii.yearordinal + i)
-                        for time in timeset:
-                            res = datetime.datetime.combine(date, time)
-                            if until and res > until:
-                                self._len = total
-                                return
-                            elif res >= self._dtstart:
-                                if count is not None:
-                                    count -= 1
-                                    if count < 0:
-                                        self._len = total
-                                        return
-
-                                total += 1
-                                yield res
-
-            # Handle frequency and interval
-            fixday = False
-            if freq == YEARLY:
-                year += interval
-                if year > datetime.MAXYEAR:
-                    self._len = total
-                    return
-                ii.rebuild(year, month)
-            elif freq == MONTHLY:
-                month += interval
-                if month > 12:
-                    div, mod = divmod(month, 12)
-                    month = mod
-                    year += div
-                    if month == 0:
-                        month = 12
-                        year -= 1
-                    if year > datetime.MAXYEAR:
-                        self._len = total
-                        return
-                ii.rebuild(year, month)
-            elif freq == WEEKLY:
-                if wkst > weekday:
-                    day += -(weekday+1+(6-wkst))+self._interval*7
-                else:
-                    day += -(weekday-wkst)+self._interval*7
-                weekday = wkst
-                fixday = True
-            elif freq == DAILY:
-                day += interval
-                fixday = True
-            elif freq == HOURLY:
-                if filtered:
-                    # Jump to one iteration before next day
-                    hour += ((23-hour)//interval)*interval
-
-                if byhour:
-                    ndays, hour = self.__mod_distance(value=hour,
-                                                      byxxx=self._byhour,
-                                                      base=24)
-                else:
-                    ndays, hour = divmod(hour+interval, 24)
-
-                if ndays:
-                    day += ndays
-                    fixday = True
-
-                timeset = gettimeset(hour, minute, second)
-            elif freq == MINUTELY:
-                if filtered:
-                    # Jump to one iteration before next day
-                    minute += ((1439-(hour*60+minute))//interval)*interval
-
-                valid = False
-                rep_rate = (24*60)
-                for j in range(rep_rate // gcd(interval, rep_rate)):
-                    if byminute:
-                        nhours, minute = \
-                            self.__mod_distance(value=minute,
-                                                byxxx=self._byminute,
-                                                base=60)
-                    else:
-                        nhours, minute = divmod(minute+interval, 60)
-
-                    div, hour = divmod(hour+nhours, 24)
-                    if div:
-                        day += div
-                        fixday = True
-                        filtered = False
-
-                    if not byhour or hour in byhour:
-                        valid = True
-                        break
-
-                if not valid:
-                    raise ValueError('Invalid combination of interval and ' +
-                                     'byhour resulting in empty rule.')
-
-                timeset = gettimeset(hour, minute, second)
-            elif freq == SECONDLY:
-                if filtered:
-                    # Jump to one iteration before next day
-                    second += (((86399 - (hour * 3600 + minute * 60 + second))
-                                // interval) * interval)
-
-                rep_rate = (24 * 3600)
-                valid = False
-                for j in range(0, rep_rate // gcd(interval, rep_rate)):
-                    if bysecond:
-                        nminutes, second = \
-                            self.__mod_distance(value=second,
-                                                byxxx=self._bysecond,
-                                                base=60)
-                    else:
-                        nminutes, second = divmod(second+interval, 60)
-
-                    div, minute = divmod(minute+nminutes, 60)
-                    if div:
-                        hour += div
-                        div, hour = divmod(hour, 24)
-                        if div:
-                            day += div
-                            fixday = True
-
-                    if ((not byhour or hour in byhour) and
-                            (not byminute or minute in byminute) and
-                            (not bysecond or second in bysecond)):
-                        valid = True
-                        break
-
-                if not valid:
-                    raise ValueError('Invalid combination of interval, ' +
-                                     'byhour and byminute resulting in empty' +
-                                     ' rule.')
-
-                timeset = gettimeset(hour, minute, second)
-
-            if fixday and day > 28:
-                daysinmonth = calendar.monthrange(year, month)[1]
-                if day > daysinmonth:
-                    while day > daysinmonth:
-                        day -= daysinmonth
-                        month += 1
-                        if month == 13:
-                            month = 1
-                            year += 1
-                            if year > datetime.MAXYEAR:
-                                self._len = total
-                                return
-                        daysinmonth = calendar.monthrange(year, month)[1]
-                    ii.rebuild(year, month)
-
-    def __construct_byset(self, start, byxxx, base):
-        """
-        If a `BYXXX` sequence is passed to the constructor at the same level as
-        `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some
-        specifications which cannot be reached given some starting conditions.
-
-        This occurs whenever the interval is not coprime with the base of a
-        given unit and the difference between the starting position and the
-        ending position is not coprime with the greatest common denominator
-        between the interval and the base. For example, with a FREQ of hourly
-        starting at 17:00 and an interval of 4, the only valid values for
-        BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not
-        coprime.
-
-        :param start:
-            Specifies the starting position.
-        :param byxxx:
-            An iterable containing the list of allowed values.
-        :param base:
-            The largest allowable value for the specified frequency (e.g.
-            24 hours, 60 minutes).
-
-        This does not preserve the type of the iterable, returning a set, since
-        the values should be unique and the order is irrelevant, this will
-        speed up later lookups.
-
-        In the event of an empty set, raises a :exception:`ValueError`, as this
-        results in an empty rrule.
-        """
-
-        cset = set()
-
-        # Support a single byxxx value.
-        if isinstance(byxxx, integer_types):
-            byxxx = (byxxx, )
-
-        for num in byxxx:
-            i_gcd = gcd(self._interval, base)
-            # Use divmod rather than % because we need to wrap negative nums.
-            if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0:
-                cset.add(num)
-
-        if len(cset) == 0:
-            raise ValueError("Invalid rrule byxxx generates an empty set.")
-
-        return cset
-
-    def __mod_distance(self, value, byxxx, base):
-        """
-        Calculates the next value in a sequence where the `FREQ` parameter is
-        specified along with a `BYXXX` parameter at the same "level"
-        (e.g. `HOURLY` specified with `BYHOUR`).
-
-        :param value:
-            The old value of the component.
-        :param byxxx:
-            The `BYXXX` set, which should have been generated by
-            `rrule._construct_byset`, or something else which checks that a
-            valid rule is present.
-        :param base:
-            The largest allowable value for the specified frequency (e.g.
-            24 hours, 60 minutes).
-
-        If a valid value is not found after `base` iterations (the maximum
-        number before the sequence would start to repeat), this raises a
-        :exception:`ValueError`, as no valid values were found.
-
-        This returns a tuple of `divmod(n*interval, base)`, where `n` is the
-        smallest number of `interval` repetitions until the next specified
-        value in `byxxx` is found.
-        """
-        accumulator = 0
-        for ii in range(1, base + 1):
-            # Using divmod() over % to account for negative intervals
-            div, value = divmod(value + self._interval, base)
-            accumulator += div
-            if value in byxxx:
-                return (accumulator, value)
-
-
-class _iterinfo(object):
-    __slots__ = ["rrule", "lastyear", "lastmonth",
-                 "yearlen", "nextyearlen", "yearordinal", "yearweekday",
-                 "mmask", "mrange", "mdaymask", "nmdaymask",
-                 "wdaymask", "wnomask", "nwdaymask", "eastermask"]
-
-    def __init__(self, rrule):
-        for attr in self.__slots__:
-            setattr(self, attr, None)
-        self.rrule = rrule
-
-    def rebuild(self, year, month):
-        # Every mask is 7 days longer to handle cross-year weekly periods.
-        rr = self.rrule
-        if year != self.lastyear:
-            self.yearlen = 365 + calendar.isleap(year)
-            self.nextyearlen = 365 + calendar.isleap(year + 1)
-            firstyday = datetime.date(year, 1, 1)
-            self.yearordinal = firstyday.toordinal()
-            self.yearweekday = firstyday.weekday()
-
-            wday = datetime.date(year, 1, 1).weekday()
-            if self.yearlen == 365:
-                self.mmask = M365MASK
-                self.mdaymask = MDAY365MASK
-                self.nmdaymask = NMDAY365MASK
-                self.wdaymask = WDAYMASK[wday:]
-                self.mrange = M365RANGE
-            else:
-                self.mmask = M366MASK
-                self.mdaymask = MDAY366MASK
-                self.nmdaymask = NMDAY366MASK
-                self.wdaymask = WDAYMASK[wday:]
-                self.mrange = M366RANGE
-
-            if not rr._byweekno:
-                self.wnomask = None
-            else:
-                self.wnomask = [0]*(self.yearlen+7)
-                # no1wkst = firstwkst = self.wdaymask.index(rr._wkst)
-                no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7
-                if no1wkst >= 4:
-                    no1wkst = 0
-                    # Number of days in the year, plus the days we got
-                    # from last year.
-                    wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7
-                else:
-                    # Number of days in the year, minus the days we
-                    # left in last year.
-                    wyearlen = self.yearlen-no1wkst
-                div, mod = divmod(wyearlen, 7)
-                numweeks = div+mod//4
-                for n in rr._byweekno:
-                    if n < 0:
-                        n += numweeks+1
-                    if not (0 < n <= numweeks):
-                        continue
-                    if n > 1:
-                        i = no1wkst+(n-1)*7
-                        if no1wkst != firstwkst:
-                            i -= 7-firstwkst
-                    else:
-                        i = no1wkst
-                    for j in range(7):
-                        self.wnomask[i] = 1
-                        i += 1
-                        if self.wdaymask[i] == rr._wkst:
-                            break
-                if 1 in rr._byweekno:
-                    # Check week number 1 of next year as well
-                    # TODO: Check -numweeks for next year.
-                    i = no1wkst+numweeks*7
-                    if no1wkst != firstwkst:
-                        i -= 7-firstwkst
-                    if i < self.yearlen:
-                        # If week starts in next year, we
-                        # don't care about it.
-                        for j in range(7):
-                            self.wnomask[i] = 1
-                            i += 1
-                            if self.wdaymask[i] == rr._wkst:
-                                break
-                if no1wkst:
-                    # Check last week number of last year as
-                    # well. If no1wkst is 0, either the year
-                    # started on week start, or week number 1
-                    # got days from last year, so there are no
-                    # days from last year's last week number in
-                    # this year.
-                    if -1 not in rr._byweekno:
-                        lyearweekday = datetime.date(year-1, 1, 1).weekday()
-                        lno1wkst = (7-lyearweekday+rr._wkst) % 7
-                        lyearlen = 365+calendar.isleap(year-1)
-                        if lno1wkst >= 4:
-                            lno1wkst = 0
-                            lnumweeks = 52+(lyearlen +
-                                            (lyearweekday-rr._wkst) % 7) % 7//4
-                        else:
-                            lnumweeks = 52+(self.yearlen-no1wkst) % 7//4
-                    else:
-                        lnumweeks = -1
-                    if lnumweeks in rr._byweekno:
-                        for i in range(no1wkst):
-                            self.wnomask[i] = 1
-
-        if (rr._bynweekday and (month != self.lastmonth or
-                                year != self.lastyear)):
-            ranges = []
-            if rr._freq == YEARLY:
-                if rr._bymonth:
-                    for month in rr._bymonth:
-                        ranges.append(self.mrange[month-1:month+1])
-                else:
-                    ranges = [(0, self.yearlen)]
-            elif rr._freq == MONTHLY:
-                ranges = [self.mrange[month-1:month+1]]
-            if ranges:
-                # Weekly frequency won't get here, so we may not
-                # care about cross-year weekly periods.
-                self.nwdaymask = [0]*self.yearlen
-                for first, last in ranges:
-                    last -= 1
-                    for wday, n in rr._bynweekday:
-                        if n < 0:
-                            i = last+(n+1)*7
-                            i -= (self.wdaymask[i]-wday) % 7
-                        else:
-                            i = first+(n-1)*7
-                            i += (7-self.wdaymask[i]+wday) % 7
-                        if first <= i <= last:
-                            self.nwdaymask[i] = 1
-
-        if rr._byeaster:
-            self.eastermask = [0]*(self.yearlen+7)
-            eyday = easter.easter(year).toordinal()-self.yearordinal
-            for offset in rr._byeaster:
-                self.eastermask[eyday+offset] = 1
-
-        self.lastyear = year
-        self.lastmonth = month
-
-    def ydayset(self, year, month, day):
-        return list(range(self.yearlen)), 0, self.yearlen
-
-    def mdayset(self, year, month, day):
-        dset = [None]*self.yearlen
-        start, end = self.mrange[month-1:month+1]
-        for i in range(start, end):
-            dset[i] = i
-        return dset, start, end
-
-    def wdayset(self, year, month, day):
-        # We need to handle cross-year weeks here.
-        dset = [None]*(self.yearlen+7)
-        i = datetime.date(year, month, day).toordinal()-self.yearordinal
-        start = i
-        for j in range(7):
-            dset[i] = i
-            i += 1
-            # if (not (0 <= i < self.yearlen) or
-            #    self.wdaymask[i] == self.rrule._wkst):
-            # This will cross the year boundary, if necessary.
-            if self.wdaymask[i] == self.rrule._wkst:
-                break
-        return dset, start, i
-
-    def ddayset(self, year, month, day):
-        dset = [None] * self.yearlen
-        i = datetime.date(year, month, day).toordinal() - self.yearordinal
-        dset[i] = i
-        return dset, i, i + 1
-
-    def htimeset(self, hour, minute, second):
-        tset = []
-        rr = self.rrule
-        for minute in rr._byminute:
-            for second in rr._bysecond:
-                tset.append(datetime.time(hour, minute, second,
-                                          tzinfo=rr._tzinfo))
-        tset.sort()
-        return tset
-
-    def mtimeset(self, hour, minute, second):
-        tset = []
-        rr = self.rrule
-        for second in rr._bysecond:
-            tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo))
-        tset.sort()
-        return tset
-
-    def stimeset(self, hour, minute, second):
-        return (datetime.time(hour, minute, second,
-                tzinfo=self.rrule._tzinfo),)
-
-
-class rruleset(rrulebase):
-    """ The rruleset type allows more complex recurrence setups, mixing
-    multiple rules, dates, exclusion rules, and exclusion dates. The type
-    constructor takes the following keyword arguments:
-
-    :param cache: If True, caching of results will be enabled, improving
-                  performance of multiple queries considerably. """
-
-    class _genitem(object):
-        def __init__(self, genlist, gen):
-            try:
-                self.dt = advance_iterator(gen)
-                genlist.append(self)
-            except StopIteration:
-                pass
-            self.genlist = genlist
-            self.gen = gen
-
-        def __next__(self):
-            try:
-                self.dt = advance_iterator(self.gen)
-            except StopIteration:
-                if self.genlist[0] is self:
-                    heapq.heappop(self.genlist)
-                else:
-                    self.genlist.remove(self)
-                    heapq.heapify(self.genlist)
-
-        next = __next__
-
-        def __lt__(self, other):
-            return self.dt < other.dt
-
-        def __gt__(self, other):
-            return self.dt > other.dt
-
-        def __eq__(self, other):
-            return self.dt == other.dt
-
-        def __ne__(self, other):
-            return self.dt != other.dt
-
-    def __init__(self, cache=False):
-        super(rruleset, self).__init__(cache)
-        self._rrule = []
-        self._rdate = []
-        self._exrule = []
-        self._exdate = []
-
-    @_invalidates_cache
-    def rrule(self, rrule):
-        """ Include the given :py:class:`rrule` instance in the recurrence set
-            generation. """
-        self._rrule.append(rrule)
-
-    @_invalidates_cache
-    def rdate(self, rdate):
-        """ Include the given :py:class:`datetime` instance in the recurrence
-            set generation. """
-        self._rdate.append(rdate)
-
-    @_invalidates_cache
-    def exrule(self, exrule):
-        """ Include the given rrule instance in the recurrence set exclusion
-            list. Dates which are part of the given recurrence rules will not
-            be generated, even if some inclusive rrule or rdate matches them.
-        """
-        self._exrule.append(exrule)
-
-    @_invalidates_cache
-    def exdate(self, exdate):
-        """ Include the given datetime instance in the recurrence set
-            exclusion list. Dates included that way will not be generated,
-            even if some inclusive rrule or rdate matches them. """
-        self._exdate.append(exdate)
-
-    def _iter(self):
-        rlist = []
-        self._rdate.sort()
-        self._genitem(rlist, iter(self._rdate))
-        for gen in [iter(x) for x in self._rrule]:
-            self._genitem(rlist, gen)
-        exlist = []
-        self._exdate.sort()
-        self._genitem(exlist, iter(self._exdate))
-        for gen in [iter(x) for x in self._exrule]:
-            self._genitem(exlist, gen)
-        lastdt = None
-        total = 0
-        heapq.heapify(rlist)
-        heapq.heapify(exlist)
-        while rlist:
-            ritem = rlist[0]
-            if not lastdt or lastdt != ritem.dt:
-                while exlist and exlist[0] < ritem:
-                    exitem = exlist[0]
-                    advance_iterator(exitem)
-                    if exlist and exlist[0] is exitem:
-                        heapq.heapreplace(exlist, exitem)
-                if not exlist or ritem != exlist[0]:
-                    total += 1
-                    yield ritem.dt
-                lastdt = ritem.dt
-            advance_iterator(ritem)
-            if rlist and rlist[0] is ritem:
-                heapq.heapreplace(rlist, ritem)
-        self._len = total
-
-
-
-
-class _rrulestr(object):
-    """ Parses a string representation of a recurrence rule or set of
-    recurrence rules.
-
-    :param s:
-        Required, a string defining one or more recurrence rules.
-
-    :param dtstart:
-        If given, used as the default recurrence start if not specified in the
-        rule string.
-
-    :param cache:
-        If set ``True`` caching of results will be enabled, improving
-        performance of multiple queries considerably.
-
-    :param unfold:
-        If set ``True`` indicates that a rule string is split over more
-        than one line and should be joined before processing.
-
-    :param forceset:
-        If set ``True`` forces a :class:`dateutil.rrule.rruleset` to
-        be returned.
-
-    :param compatible:
-        If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``.
-
-    :param ignoretz:
-        If set ``True``, time zones in parsed strings are ignored and a naive
-        :class:`datetime.datetime` object is returned.
-
-    :param tzids:
-        If given, a callable or mapping used to retrieve a
-        :class:`datetime.tzinfo` from a string representation.
-        Defaults to :func:`dateutil.tz.gettz`.
-
-    :param tzinfos:
-        Additional time zone names / aliases which may be present in a string
-        representation.  See :func:`dateutil.parser.parse` for more
-        information.
-
-    :return:
-        Returns a :class:`dateutil.rrule.rruleset` or
-        :class:`dateutil.rrule.rrule`
-    """
-
-    _freq_map = {"YEARLY": YEARLY,
-                 "MONTHLY": MONTHLY,
-                 "WEEKLY": WEEKLY,
-                 "DAILY": DAILY,
-                 "HOURLY": HOURLY,
-                 "MINUTELY": MINUTELY,
-                 "SECONDLY": SECONDLY}
-
-    _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3,
-                    "FR": 4, "SA": 5, "SU": 6}
-
-    def _handle_int(self, rrkwargs, name, value, **kwargs):
-        rrkwargs[name.lower()] = int(value)
-
-    def _handle_int_list(self, rrkwargs, name, value, **kwargs):
-        rrkwargs[name.lower()] = [int(x) for x in value.split(',')]
-
-    _handle_INTERVAL = _handle_int
-    _handle_COUNT = _handle_int
-    _handle_BYSETPOS = _handle_int_list
-    _handle_BYMONTH = _handle_int_list
-    _handle_BYMONTHDAY = _handle_int_list
-    _handle_BYYEARDAY = _handle_int_list
-    _handle_BYEASTER = _handle_int_list
-    _handle_BYWEEKNO = _handle_int_list
-    _handle_BYHOUR = _handle_int_list
-    _handle_BYMINUTE = _handle_int_list
-    _handle_BYSECOND = _handle_int_list
-
-    def _handle_FREQ(self, rrkwargs, name, value, **kwargs):
-        rrkwargs["freq"] = self._freq_map[value]
-
-    def _handle_UNTIL(self, rrkwargs, name, value, **kwargs):
-        global parser
-        if not parser:
-            from dateutil import parser
-        try:
-            rrkwargs["until"] = parser.parse(value,
-                                             ignoretz=kwargs.get("ignoretz"),
-                                             tzinfos=kwargs.get("tzinfos"))
-        except ValueError:
-            raise ValueError("invalid until date")
-
-    def _handle_WKST(self, rrkwargs, name, value, **kwargs):
-        rrkwargs["wkst"] = self._weekday_map[value]
-
-    def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs):
-        """
-        Two ways to specify this: +1MO or MO(+1)
-        """
-        l = []
-        for wday in value.split(','):
-            if '(' in wday:
-                # If it's of the form TH(+1), etc.
-                splt = wday.split('(')
-                w = splt[0]
-                n = int(splt[1][:-1])
-            elif len(wday):
-                # If it's of the form +1MO
-                for i in range(len(wday)):
-                    if wday[i] not in '+-0123456789':
-                        break
-                n = wday[:i] or None
-                w = wday[i:]
-                if n:
-                    n = int(n)
-            else:
-                raise ValueError("Invalid (empty) BYDAY specification.")
-
-            l.append(weekdays[self._weekday_map[w]](n))
-        rrkwargs["byweekday"] = l
-
-    _handle_BYDAY = _handle_BYWEEKDAY
-
-    def _parse_rfc_rrule(self, line,
-                         dtstart=None,
-                         cache=False,
-                         ignoretz=False,
-                         tzinfos=None):
-        if line.find(':') != -1:
-            name, value = line.split(':')
-            if name != "RRULE":
-                raise ValueError("unknown parameter name")
-        else:
-            value = line
-        rrkwargs = {}
-        for pair in value.split(';'):
-            name, value = pair.split('=')
-            name = name.upper()
-            value = value.upper()
-            try:
-                getattr(self, "_handle_"+name)(rrkwargs, name, value,
-                                               ignoretz=ignoretz,
-                                               tzinfos=tzinfos)
-            except AttributeError:
-                raise ValueError("unknown parameter '%s'" % name)
-            except (KeyError, ValueError):
-                raise ValueError("invalid '%s': %s" % (name, value))
-        return rrule(dtstart=dtstart, cache=cache, **rrkwargs)
-
-    def _parse_date_value(self, date_value, parms, rule_tzids,
-                          ignoretz, tzids, tzinfos):
-        global parser
-        if not parser:
-            from dateutil import parser
-
-        datevals = []
-        value_found = False
-        TZID = None
-
-        for parm in parms:
-            if parm.startswith("TZID="):
-                try:
-                    tzkey = rule_tzids[parm.split('TZID=')[-1]]
-                except KeyError:
-                    continue
-                if tzids is None:
-                    from . import tz
-                    tzlookup = tz.gettz
-                elif callable(tzids):
-                    tzlookup = tzids
-                else:
-                    tzlookup = getattr(tzids, 'get', None)
-                    if tzlookup is None:
-                        msg = ('tzids must be a callable, mapping, or None, '
-                               'not %s' % tzids)
-                        raise ValueError(msg)
-
-                TZID = tzlookup(tzkey)
-                continue
-
-            # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found
-            # only once.
-            if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}:
-                raise ValueError("unsupported parm: " + parm)
-            else:
-                if value_found:
-                    msg = ("Duplicate value parameter found in: " + parm)
-                    raise ValueError(msg)
-                value_found = True
-
-        for datestr in date_value.split(','):
-            date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos)
-            if TZID is not None:
-                if date.tzinfo is None:
-                    date = date.replace(tzinfo=TZID)
-                else:
-                    raise ValueError('DTSTART/EXDATE specifies multiple timezone')
-            datevals.append(date)
-
-        return datevals
-
-    def _parse_rfc(self, s,
-                   dtstart=None,
-                   cache=False,
-                   unfold=False,
-                   forceset=False,
-                   compatible=False,
-                   ignoretz=False,
-                   tzids=None,
-                   tzinfos=None):
-        global parser
-        if compatible:
-            forceset = True
-            unfold = True
-
-        TZID_NAMES = dict(map(
-            lambda x: (x.upper(), x),
-            re.findall('TZID=(?P<name>[^:]+):', s)
-        ))
-        s = s.upper()
-        if not s.strip():
-            raise ValueError("empty string")
-        if unfold:
-            lines = s.splitlines()
-            i = 0
-            while i < len(lines):
-                line = lines[i].rstrip()
-                if not line:
-                    del lines[i]
-                elif i > 0 and line[0] == " ":
-                    lines[i-1] += line[1:]
-                    del lines[i]
-                else:
-                    i += 1
-        else:
-            lines = s.split()
-        if (not forceset and len(lines) == 1 and (s.find(':') == -1 or
-                                                  s.startswith('RRULE:'))):
-            return self._parse_rfc_rrule(lines[0], cache=cache,
-                                         dtstart=dtstart, ignoretz=ignoretz,
-                                         tzinfos=tzinfos)
-        else:
-            rrulevals = []
-            rdatevals = []
-            exrulevals = []
-            exdatevals = []
-            for line in lines:
-                if not line:
-                    continue
-                if line.find(':') == -1:
-                    name = "RRULE"
-                    value = line
-                else:
-                    name, value = line.split(':', 1)
-                parms = name.split(';')
-                if not parms:
-                    raise ValueError("empty property name")
-                name = parms[0]
-                parms = parms[1:]
-                if name == "RRULE":
-                    for parm in parms:
-                        raise ValueError("unsupported RRULE parm: "+parm)
-                    rrulevals.append(value)
-                elif name == "RDATE":
-                    for parm in parms:
-                        if parm != "VALUE=DATE-TIME":
-                            raise ValueError("unsupported RDATE parm: "+parm)
-                    rdatevals.append(value)
-                elif name == "EXRULE":
-                    for parm in parms:
-                        raise ValueError("unsupported EXRULE parm: "+parm)
-                    exrulevals.append(value)
-                elif name == "EXDATE":
-                    exdatevals.extend(
-                        self._parse_date_value(value, parms,
-                                               TZID_NAMES, ignoretz,
-                                               tzids, tzinfos)
-                    )
-                elif name == "DTSTART":
-                    dtvals = self._parse_date_value(value, parms, TZID_NAMES,
-                                                    ignoretz, tzids, tzinfos)
-                    if len(dtvals) != 1:
-                        raise ValueError("Multiple DTSTART values specified:" +
-                                         value)
-                    dtstart = dtvals[0]
-                else:
-                    raise ValueError("unsupported property: "+name)
-            if (forceset or len(rrulevals) > 1 or rdatevals
-                    or exrulevals or exdatevals):
-                if not parser and (rdatevals or exdatevals):
-                    from dateutil import parser
-                rset = rruleset(cache=cache)
-                for value in rrulevals:
-                    rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart,
-                                                     ignoretz=ignoretz,
-                                                     tzinfos=tzinfos))
-                for value in rdatevals:
-                    for datestr in value.split(','):
-                        rset.rdate(parser.parse(datestr,
-                                                ignoretz=ignoretz,
-                                                tzinfos=tzinfos))
-                for value in exrulevals:
-                    rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart,
-                                                      ignoretz=ignoretz,
-                                                      tzinfos=tzinfos))
-                for value in exdatevals:
-                    rset.exdate(value)
-                if compatible and dtstart:
-                    rset.rdate(dtstart)
-                return rset
-            else:
-                return self._parse_rfc_rrule(rrulevals[0],
-                                             dtstart=dtstart,
-                                             cache=cache,
-                                             ignoretz=ignoretz,
-                                             tzinfos=tzinfos)
-
-    def __call__(self, s, **kwargs):
-        return self._parse_rfc(s, **kwargs)
-
-
-rrulestr = _rrulestr()
-
-# vim:ts=4:sw=4:et