Example #1
0
 def isMonthDay(self, day):
     if day > 0:
         return self.mDay == day
     elif day < 0:
         return self.mDay - 1 - utils.daysInMonth(self.mMonth, self.mYear) == day
     else:
         return False
Example #2
0
    def setDayOfWeekInMonth(self, offset, day, allow_invalid=False):
        # Set to first day in month
        self.mDay = 1

        # Determine first weekday in month
        first_day = self.getDayOfWeek()

        if offset > 0:
            cycle = (offset - 1) * 7 + day
            cycle -= first_day
            if first_day > day:
                cycle += 7
            self.mDay = cycle + 1
        elif offset < 0:
            days_in_month = utils.daysInMonth(self.mMonth, self.mYear)
            first_day += days_in_month - 1
            first_day %= 7

            cycle = (-offset - 1) * 7 - day
            cycle += first_day
            if day > first_day:
                cycle += 7
            self.mDay = days_in_month - cycle

        if not allow_invalid:
            self.normalise()
        else:
            self.changed()
Example #3
0
 def isMonthDay(self, day):
     if day > 0:
         return self.mDay == day
     elif day < 0:
         return self.mDay - 1 - utils.daysInMonth(self.mMonth, self.mYear) == day
     else:
         return False
Example #4
0
    def setDayOfWeekInMonth(self, offset, day, allow_invalid=False):
        # Set to first day in month
        self.mDay = 1

        # Determine first weekday in month
        first_day = self.getDayOfWeek()

        if offset > 0:
            cycle = (offset - 1) * 7 + day
            cycle -= first_day
            if first_day > day:
                cycle += 7
            self.mDay = cycle + 1
        elif offset < 0:
            days_in_month = utils.daysInMonth(self.mMonth, self.mYear)
            first_day += days_in_month - 1
            first_day %= 7

            cycle = (-offset - 1) * 7 - day
            cycle += first_day
            if day > first_day:
                cycle += 7
            self.mDay = days_in_month - cycle

        if not allow_invalid:
            self.normalise()
        else:
            self.changed()
Example #5
0
    def invalid(self):
        """
        Are any of the current fields invalid.
        """

        # Right now we only care about invalid days of the month (e.g. February 30th). In the
        # future we may also want to look for invalid times during a DST transition.

        if self.mDay <= 0 or self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
            return True

        return False
Example #6
0
    def invalid(self):
        """
        Are any of the current fields invalid.
        """

        # Right now we only care about invalid days of the month (e.g. February 30th). In the
        # future we may also want to look for invalid times during a DST transition.

        if self.mDay <= 0 or self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
            return True

        return False
Example #7
0
    def recur(self, freq, interval, allow_invalid=False):
        # Add appropriate interval
        normalize = True
        if freq == definitions.eRecurrence_SECONDLY:
            self.mSeconds += interval
        elif freq == definitions.eRecurrence_MINUTELY:
            self.mMinutes += interval
        elif freq == definitions.eRecurrence_HOURLY:
            self.mHours += interval
        elif freq == definitions.eRecurrence_DAILY:
            self.mDay += interval
        elif freq == definitions.eRecurrence_WEEKLY:
            self.mDay += 7 * interval
        elif freq == definitions.eRecurrence_MONTHLY:
            # Iterate until a valid day in the next month is found.
            # This can happen if adding one month to e.g. 31 January.
            # That is an undefined operation - does it mean 28/29 February
            # or 1/2 May, or 31 March or what? We choose to find the next month with
            # the same day number as the current one.
            self.mMonth += interval

            # Normalise month
            normalised_month = ((self.mMonth - 1) % 12) + 1
            adjustment_year = (self.mMonth - 1) / 12
            if (normalised_month - 1) < 0:
                normalised_month += 12
                adjustment_year -= 1
            self.mMonth = normalised_month
            self.mYear += adjustment_year

            if not allow_invalid:
                self.normalise()
                while self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
                    self.mMonth += interval
                    self.normalise()
            normalize = False
        elif freq == definitions.eRecurrence_YEARLY:
            self.mYear += interval
            if allow_invalid:
                normalize = False

        if normalize:
            # Normalise to standard date-time ranges
            self.normalise()
        else:
            self.changed()
Example #8
0
    def recur(self, freq, interval, allow_invalid=False):
        # Add appropriate interval
        normalize = True
        if freq == definitions.eRecurrence_SECONDLY:
            self.mSeconds += interval
        elif freq == definitions.eRecurrence_MINUTELY:
            self.mMinutes += interval
        elif freq == definitions.eRecurrence_HOURLY:
            self.mHours += interval
        elif freq == definitions.eRecurrence_DAILY:
            self.mDay += interval
        elif freq == definitions.eRecurrence_WEEKLY:
            self.mDay += 7 * interval
        elif freq == definitions.eRecurrence_MONTHLY:
            # Iterate until a valid day in the next month is found.
            # This can happen if adding one month to e.g. 31 January.
            # That is an undefined operation - does it mean 28/29 February
            # or 1/2 May, or 31 March or what? We choose to find the next month with
            # the same day number as the current one.
            self.mMonth += interval

            # Normalise month
            normalised_month = ((self.mMonth - 1) % 12) + 1
            adjustment_year = (self.mMonth - 1) / 12
            if (normalised_month - 1) < 0:
                normalised_month += 12
                adjustment_year -= 1
            self.mMonth = normalised_month
            self.mYear += adjustment_year

            if not allow_invalid:
                self.normalise()
                while self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
                    self.mMonth += interval
                    self.normalise()
            normalize = False
        elif freq == definitions.eRecurrence_YEARLY:
            self.mYear += interval
            if allow_invalid:
                normalize = False

        if normalize:
            # Normalise to standard date-time ranges
            self.normalise()
        else:
            self.changed()
Example #9
0
    def setMonthDay(self, day, allow_invalid=False):
        # 1 .. 31 offset from start, or
        # -1 .. -31 offset from end

        if day > 0:
            # Offset current date to 1st of current month
            self.mDay = 1

            # Increment day
            self.mDay += day - 1

        elif day < 0:
            # Offset current date to last of month
            self.mDay = utils.daysInMonth(self.mMonth, self.mYear)

            # Decrement day
            self.mDay += day + 1

        if not allow_invalid:
            # Normalise to get proper year/month/day values
            self.normalise()
        else:
            self.changed()
Example #10
0
    def setMonthDay(self, day, allow_invalid=False):
        # 1 .. 31 offset from start, or
        # -1 .. -31 offset from end

        if day > 0:
            # Offset current date to 1st of current month
            self.mDay = 1

            # Increment day
            self.mDay += day - 1

        elif day < 0:
            # Offset current date to last of month
            self.mDay = utils.daysInMonth(self.mMonth, self.mYear)

            # Decrement day
            self.mDay += day + 1

        if not allow_invalid:
            # Normalise to get proper year/month/day values
            self.normalise()
        else:
            self.changed()
Example #11
0
    def vtimezone(self, vtz, zonerule, start, end, offsetfrom, offsetto, instanceCount):
        """
        Generate a VTIMEZONE sub-component for this Rule.

        @param vtz: VTIMEZONE to add to
        @type vtz: L{VTimezone}
        @param zonerule: the Zone rule line being used
        @type zonerule: L{ZoneRule}
        @param start: the start time for the first instance
        @type start: L{DateTime}
        @param end: the start time for the last instance
        @type end: L{DateTime}
        @param offsetfrom: the UTC offset-from
        @type offsetfrom: C{int}
        @param offsetto: the UTC offset-to
        @type offsetto: C{int}
        @param instanceCount: the number of instances in the set
        @type instanceCount: C{int}
        """

        # Determine type of component based on offset
        dstoffset = self.getOffset()
        if dstoffset == 0:
            comp = Standard(parent=vtz)
        else:
            comp = Daylight(parent=vtz)

        # Do offsets
        tzoffsetfrom = UTCOffsetValue(offsetfrom)
        tzoffsetto = UTCOffsetValue(offsetto)

        comp.addProperty(Property(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
        comp.addProperty(Property(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))

        # Do TZNAME
        if zonerule.format.find("%") != -1:
            tzname = zonerule.format % (self.letter if self.letter != "-" else "",)
        else:
            tzname = zonerule.format
        comp.addProperty(Property(definitions.cICalProperty_TZNAME, tzname))

        # Do DTSTART
        comp.addProperty(Property(definitions.cICalProperty_DTSTART, start))

        # Now determine the recurrences (use RDATE if only one year or
        # number of instances is one)
        if self.toYear != "only" and instanceCount != 1:
            rrule = Recurrence()
            rrule.setFreq(definitions.eRecurrence_YEARLY)
            rrule.setByMonth((Rule.MONTH_NAME_TO_POS[self.inMonth],))
            if self.onDay in Rule.LASTDAY_NAME_TO_RDAY:

                # Need to check whether day has changed due to time shifting
                dayOfWeek = start.getDayOfWeek()
                indicatedDay = Rule.LASTDAY_NAME_TO_DAY[self.onDay]

                if dayOfWeek == indicatedDay:
                    rrule.setByDay(((-1, Rule.LASTDAY_NAME_TO_RDAY[self.onDay]),))
                elif dayOfWeek < indicatedDay or dayOfWeek == 6 and indicatedDay == 0:
                    # This is OK as we have moved back a day and thus no month transition
                    # could have occurred
                    fakeOffset = daysInMonth(start.getMonth(), start.getYear()) - 6
                    offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, fakeOffset)
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday),))
                else:
                    # This is bad news as we have moved forward a day possibly into the next month
                    # What we do is switch to using a BYYEARDAY rule with offset from the end of the year
                    rrule.setByMonth(())
                    daysBackStartOfMonth = (
                        365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0     # Does not account for leap year
                    )
                    rrule.setByYearDay([-(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[self.inMonth]] + i) for i in range(7)])
                    rrule.setByDay(
                        ((0, divmod(Rule.LASTDAY_NAME_TO_DAY[self.onDay] + 1, 7)[1]),),
                    )

            elif self.onDay.find(">=") != -1:
                indicatedDay, dayoffset = self.onDay.split(">=")

                # Need to check whether day has changed due to time shifting
                dayOfWeek = start.getDayOfWeek()
                indicatedDay = Rule.DAY_NAME_TO_DAY[indicatedDay]

                if dayOfWeek == indicatedDay:
                    offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, int(dayoffset))
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday),))
                elif dayoffset == 1 and divmod(dayoffset - indicatedDay, 7)[1] == 6:
                    # This is bad news as we have moved backward a day possibly into the next month
                    # What we do is switch to using a BYYEARDAY rule with offset from the end of the year
                    rrule.setByMonth(())
                    daysBackStartOfMonth = (
                        365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0     # Does not account for leap year
                    )
                    rrule.setByYearDay([-(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[self.inMonth]] + i) for i in range(7)])
                    rrule.setByDay(
                        ((0, divmod(indicatedDay + 1, 7)[1]),),
                    )
                else:
                    # This is OK as we have moved forward a day and thus no month transition
                    # could have occurred
                    offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, int(dayoffset))
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday),))
            else:
                try:
                    int(self.onDay)
                except:
                    assert False, "onDay value is not recognized: %s" % (self.onDay,)

            # Add any UNTIL
            if zonerule.getUntilDate().dt.getYear() < 9999 or self.endYear() < 9999:
                until = end.duplicate()
                until.offsetSeconds(-offsetfrom)
                until.setTimezoneUTC(True)
                rrule.setUseUntil(True)
                rrule.setUntil(until)

            comp.addProperty(Property(definitions.cICalProperty_RRULE, rrule))
        else:
            comp.addProperty(Property(definitions.cICalProperty_RDATE, start))

        comp.finalise()
        vtz.addComponent(comp)
Example #12
0
    def getOnDayDetails(self, start, indicatedDay, indicatedOffset):
        """
        Get RRULE BYxxx part items from the Rule data.

        @param start: start date-time for the recurrence set
        @type start: L{DateTime}
        @param indicatedDay: the day that the Rule indicates for recurrence
        @type indicatedDay: C{int}
        @param indicatedOffset: the offset that the Rule indicates for recurrence
        @type indicatedOffset: C{int}
        """

        month = start.getMonth()
        year = start.getYear()
        dayOfWeek = start.getDayOfWeek()

        # Need to check whether day has changed due to time shifting
        # e.g. if "u" mode is specified, the actual localtime may be
        # shifted to the previous day if the offset is negative
        if indicatedDay != dayOfWeek:
            difference = dayOfWeek - indicatedDay
            if difference in (1, -6,):
                indicatedOffset += 1

                # Adjust the month down too if needed
                if start.getDay() == 1:
                    month -= 1
                    if month < 1:
                        month = 12
            elif difference in (-1, 6,):
                assert indicatedOffset != 1, "Bad RRULE adjustment"
                indicatedOffset -= 1
            else:
                assert False, "Unknown RRULE adjustment"

        try:
            # Form the appropriate RRULE bits
            day = Rule.DAY_NAME_TO_RDAY[dayOfWeek]
            offset = indicatedOffset
            bymday = None
            if offset == 1:
                offset = 1
            elif offset == 8:
                offset = 2
            elif offset == 15:
                offset = 3
            elif offset == 22:
                offset = 4
            else:
                days_in_month = daysInMonth(month, year)
                if days_in_month - offset == 6:
                    offset = -1
                elif days_in_month - offset == 13:
                    offset = -2
                elif days_in_month - offset == 20:
                    offset = -3
                else:
                    bymday = [offset + i for i in range(7) if (offset + i) <= days_in_month]
                    offset = 0
        except:
            assert False, "onDay value is not recognized: %s" % (self.onDay,)

        return offset, day, bymday
Example #13
0
    def normalise(self):
        # Normalise seconds
        normalised_secs = self.mSeconds % 60
        adjustment_mins = self.mSeconds / 60
        if normalised_secs < 0:
            normalised_secs += 60
            adjustment_mins -= 1
        self.mSeconds = normalised_secs
        self.mMinutes += adjustment_mins

        # Normalise minutes
        normalised_mins = self.mMinutes % 60
        adjustment_hours = self.mMinutes / 60
        if normalised_mins < 0:
            normalised_mins += 60
            adjustment_hours -= 1
        self.mMinutes = normalised_mins
        self.mHours += adjustment_hours

        # Normalise hours
        normalised_hours = self.mHours % 24
        adjustment_days = self.mHours / 24
        if normalised_hours < 0:
            normalised_hours += 24
            adjustment_days -= 1
        self.mHours = normalised_hours
        self.mDay += adjustment_days

        # Wipe the time if date only
        if self.mDateOnly:
            self.mSeconds = self.mMinutes = self.mHours = 0

        # Adjust the month first, since the day adjustment is month dependent

        # Normalise month
        normalised_month = ((self.mMonth - 1) % 12) + 1
        adjustment_year = (self.mMonth - 1) / 12
        if (normalised_month - 1) < 0:
            normalised_month += 12
            adjustment_year -= 1
        self.mMonth = normalised_month
        self.mYear += adjustment_year

        # Now do days
        if self.mDay > 0:
            while self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
                self.mDay -= utils.daysInMonth(self.mMonth, self.mYear)
                self.mMonth += 1
                if self.mMonth > 12:
                    self.mMonth = 1
                    self.mYear += 1
        else:
            while self.mDay <= 0:
                self.mMonth -= 1
                if self.mMonth < 1:
                    self.mMonth = 12
                    self.mYear -= 1
                self.mDay += utils.daysInMonth(self.mMonth, self.mYear)

        # Always invalidate posix time cache
        self.changed()
Example #14
0
    def normalise(self):
        # Normalise seconds
        normalised_secs = self.mSeconds % 60
        adjustment_mins = self.mSeconds / 60
        if normalised_secs < 0:
            normalised_secs += 60
            adjustment_mins -= 1
        self.mSeconds = normalised_secs
        self.mMinutes += adjustment_mins

        # Normalise minutes
        normalised_mins = self.mMinutes % 60
        adjustment_hours = self.mMinutes / 60
        if normalised_mins < 0:
            normalised_mins += 60
            adjustment_hours -= 1
        self.mMinutes = normalised_mins
        self.mHours += adjustment_hours

        # Normalise hours
        normalised_hours = self.mHours % 24
        adjustment_days = self.mHours / 24
        if normalised_hours < 0:
            normalised_hours += 24
            adjustment_days -= 1
        self.mHours = normalised_hours
        self.mDay += adjustment_days

        # Wipe the time if date only
        if self.mDateOnly:
            self.mSeconds = self.mMinutes = self.mHours = 0

        # Adjust the month first, since the day adjustment is month dependent

        # Normalise month
        normalised_month = ((self.mMonth - 1) % 12) + 1
        adjustment_year = (self.mMonth - 1) / 12
        if (normalised_month - 1) < 0:
            normalised_month += 12
            adjustment_year -= 1
        self.mMonth = normalised_month
        self.mYear += adjustment_year

        # Now do days
        if self.mDay > 0:
            while self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
                self.mDay -= utils.daysInMonth(self.mMonth, self.mYear)
                self.mMonth += 1
                if self.mMonth > 12:
                    self.mMonth = 1
                    self.mYear += 1
        else:
            while self.mDay <= 0:
                self.mMonth -= 1
                if self.mMonth < 1:
                    self.mMonth = 12
                    self.mYear -= 1
                self.mDay += utils.daysInMonth(self.mMonth, self.mYear)

        # Always invalidate posix time cache
        self.changed()
Example #15
0
    def vtimezone(self, vtz, zonerule, start, end, offsetfrom, offsetto, instanceCount):
        """
        Generate a VTIMEZONE sub-component for this Rule.

        @param vtz: VTIMEZONE to add to
        @type vtz: L{VTimezone}
        @param zonerule: the Zone rule line being used
        @type zonerule: L{ZoneRule}
        @param start: the start time for the first instance
        @type start: L{DateTime}
        @param end: the start time for the last instance
        @type end: L{DateTime}
        @param offsetfrom: the UTC offset-from
        @type offsetfrom: C{int}
        @param offsetto: the UTC offset-to
        @type offsetto: C{int}
        @param instanceCount: the number of instances in the set
        @type instanceCount: C{int}
        """

        # Determine type of component based on offset
        dstoffset = self.getOffset()
        if dstoffset == 0:
            comp = Standard(parent=vtz)
        else:
            comp = Daylight(parent=vtz)

        # Do offsets
        tzoffsetfrom = UTCOffsetValue(offsetfrom)
        tzoffsetto = UTCOffsetValue(offsetto)

        comp.addProperty(Property(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
        comp.addProperty(Property(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))

        # Do TZNAME
        if zonerule.format.find("%") != -1:
            tzname = zonerule.format % (self.letter if self.letter != "-" else "",)
        else:
            tzname = zonerule.format
        comp.addProperty(Property(definitions.cICalProperty_TZNAME, tzname))

        # Do DTSTART
        comp.addProperty(Property(definitions.cICalProperty_DTSTART, start))

        # Now determine the recurrences (use RDATE if only one year or
        # number of instances is one)
        if self.toYear != "only" and instanceCount != 1:
            rrule = Recurrence()
            rrule.setFreq(definitions.eRecurrence_YEARLY)
            rrule.setByMonth((Rule.MONTH_NAME_TO_POS[self.inMonth],))
            if self.onDay in Rule.LASTDAY_NAME_TO_RDAY:

                # Need to check whether day has changed due to time shifting
                dayOfWeek = start.getDayOfWeek()
                indicatedDay = Rule.LASTDAY_NAME_TO_DAY[self.onDay]

                if dayOfWeek == indicatedDay:
                    rrule.setByDay(((-1, Rule.LASTDAY_NAME_TO_RDAY[self.onDay]),))
                elif dayOfWeek < indicatedDay or dayOfWeek == 6 and indicatedDay == 0:
                    # This is OK as we have moved back a day and thus no month transition
                    # could have occurred
                    fakeOffset = daysInMonth(start.getMonth(), start.getYear()) - 6
                    offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, fakeOffset)
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday),))
                else:
                    # This is bad news as we have moved forward a day possibly into the next month
                    # What we do is switch to using a BYYEARDAY rule with offset from the end of the year
                    rrule.setByMonth(())
                    daysBackStartOfMonth = (
                        365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0     # Does not account for leap year
                    )
                    rrule.setByYearDay([-(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[self.inMonth]] + i) for i in range(7)])
                    rrule.setByDay(
                        ((0, divmod(Rule.LASTDAY_NAME_TO_DAY[self.onDay] + 1, 7)[1]),),
                    )

            elif self.onDay.find(">=") != -1:
                indicatedDay, dayoffset = self.onDay.split(">=")

                # Need to check whether day has changed due to time shifting
                dayOfWeek = start.getDayOfWeek()
                indicatedDay = Rule.DAY_NAME_TO_DAY[indicatedDay]

                if dayOfWeek == indicatedDay:
                    offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, int(dayoffset))
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday),))
                elif dayoffset == 1 and divmod(dayoffset - indicatedDay, 7)[1] == 6:
                    # This is bad news as we have moved backward a day possibly into the next month
                    # What we do is switch to using a BYYEARDAY rule with offset from the end of the year
                    rrule.setByMonth(())
                    daysBackStartOfMonth = (
                        365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0     # Does not account for leap year
                    )
                    rrule.setByYearDay([-(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[self.inMonth]] + i) for i in range(7)])
                    rrule.setByDay(
                        ((0, divmod(indicatedDay + 1, 7)[1]),),
                    )
                else:
                    # This is OK as we have moved forward a day and thus no month transition
                    # could have occurred
                    offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, int(dayoffset))
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday),))
            else:
                try:
                    _ignore_day = int(self.onDay)
                except:
                    assert False, "onDay value is not recognized: %s" % (self.onDay,)

            # Add any UNTIL
            if zonerule.getUntilDate().dt.getYear() < 9999 or self.endYear() < 9999:
                until = end.duplicate()
                until.offsetSeconds(-offsetfrom)
                until.setTimezoneUTC(True)
                rrule.setUseUntil(True)
                rrule.setUntil(until)

            comp.addProperty(Property(definitions.cICalProperty_RRULE, rrule))
        else:
            comp.addProperty(Property(definitions.cICalProperty_RDATE, start))

        comp.finalise()
        vtz.addComponent(comp)
Example #16
0
    def getOnDayDetails(self, start, indicatedDay, indicatedOffset):
        """
        Get RRULE BYxxx part items from the Rule data.

        @param start: start date-time for the recurrence set
        @type start: L{DateTime}
        @param indicatedDay: the day that the Rule indicates for recurrence
        @type indicatedDay: C{int}
        @param indicatedOffset: the offset that the Rule indicates for recurrence
        @type indicatedOffset: C{int}
        """

        month = start.getMonth()
        year = start.getYear()
        dayOfWeek = start.getDayOfWeek()

        # Need to check whether day has changed due to time shifting
        # e.g. if "u" mode is specified, the actual localtime may be
        # shifted to the previous day if the offset is negative
        if indicatedDay != dayOfWeek:
            difference = dayOfWeek - indicatedDay
            if difference in (1, -6,):
                indicatedOffset += 1

                # Adjust the month down too if needed
                if start.getDay() == 1:
                    month -= 1
                    if month < 1:
                        month = 12
            elif difference in (-1, 6,):
                assert indicatedOffset != 1, "Bad RRULE adjustment"
                indicatedOffset -= 1
            else:
                assert False, "Unknown RRULE adjustment"

        try:
            # Form the appropriate RRULE bits
            day = Rule.DAY_NAME_TO_RDAY[dayOfWeek]
            offset = indicatedOffset
            bymday = None
            if offset == 1:
                offset = 1
            elif offset == 8:
                offset = 2
            elif offset == 15:
                offset = 3
            elif offset == 22:
                offset = 4
            else:
                days_in_month = daysInMonth(month, year)
                if days_in_month - offset == 6:
                    offset = -1
                elif days_in_month - offset == 13:
                    offset = -2
                elif days_in_month - offset == 20:
                    offset = -3
                else:
                    bymday = [offset + i for i in range(7) if (offset + i) <= days_in_month]
                    offset = 0
        except:
            assert False, "onDay value is not recognized: %s" % (self.onDay,)

        return offset, day, bymday