[CalendarServer-changes] [15129] PyCalendar/branches/rscale/src/pycalendar

source_changes at macosforge.org source_changes at macosforge.org
Thu Sep 17 10:55:04 PDT 2015


Revision: 15129
          http://trac.calendarserver.org//changeset/15129
Author:   cdaboo at apple.com
Date:     2015-09-17 10:55:04 -0700 (Thu, 17 Sep 2015)
Log Message:
-----------
Sync-up with latest RFC (default skip behavior). Fix various skip issues.

Modified Paths:
--------------
    PyCalendar/branches/rscale/src/pycalendar/datetime.py
    PyCalendar/branches/rscale/src/pycalendar/exceptions.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/definitions.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/exceptions.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/icudatetime.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/recuriter.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/recurrence.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/rscale_examples.json
    PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_icudatetime.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recuriter.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recurrence.py
    PyCalendar/branches/rscale/src/pycalendar/icalendar/xmldefinitions.py
    PyCalendar/branches/rscale/src/pycalendar/property.py
    PyCalendar/branches/rscale/src/pycalendar/tests/test_datetime.py

Modified: PyCalendar/branches/rscale/src/pycalendar/datetime.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/datetime.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/datetime.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -293,6 +293,7 @@
         # the new date is invalid
         if self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
             self.mDay = utils.daysInMonth(self.mMonth, self.mYear)
+        self.changed()
 
 
     def getMonth(self):
@@ -321,6 +322,7 @@
         # the new date is invalid
         if self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
             self.mDay = utils.daysInMonth(self.mMonth, self.mYear)
+        self.changed()
 
 
     def getLeapMonth(self):
@@ -1161,7 +1163,7 @@
         return False
 
 
-    def invalidSkip(self, skip):
+    def invalidSkip(self, skip, monthly=False):
         """
         If this is an invalid value skip backward or forward or not at all.
 
@@ -1169,10 +1171,10 @@
         @type skip: L{int}
         """
 
-        if self.invalid():
-            if skip == definitions.eRecurrence_SKIP_YES:
+        if self.invalid() and not monthly:
+            if skip == definitions.eRecurrence_SKIP_OMIT:
                 # Leave it as invalid
-                pass
+                return False
             elif skip == definitions.eRecurrence_SKIP_BACKWARD:
                 if self.mDay <= 0:
                     self.mDay = 1
@@ -1185,6 +1187,7 @@
                 else:
                     self.mDay = utils.daysInMonth(self.mMonth, self.mYear)
                     self.offsetDay(1)
+        return True
 
 
     def normalise(self):

Modified: PyCalendar/branches/rscale/src/pycalendar/exceptions.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/exceptions.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/exceptions.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -16,9 +16,10 @@
 
 class ErrorBase(Exception):
 
-    def __init__(self, reason, data=""):
+    def __init__(self, reason, data="", cause=None):
         self.mReason = reason
         self.mData = data
+        self.mCause = cause
 
 
 

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/definitions.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/definitions.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/definitions.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -277,11 +277,11 @@
 cICalValue_RECUR_WEEKDAY_FR = "FR"
 cICalValue_RECUR_WEEKDAY_SA = "SA"
 
-eRecurrence_SKIP_YES = 0
+eRecurrence_SKIP_OMIT = 0
 eRecurrence_SKIP_BACKWARD = 1
 eRecurrence_SKIP_FORWARD = 2
 
-cICalValue_RECUR_SKIP_YES = "YES"
+cICalValue_RECUR_SKIP_OMIT = "OMIT"
 cICalValue_RECUR_SKIP_BACKWARD = "BACKWARD"
 cICalValue_RECUR_SKIP_FORWARD = "FORWARD"
 

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/exceptions.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/exceptions.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/exceptions.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -18,3 +18,13 @@
 
 class TooManyInstancesError(ErrorBase):
     pass
+
+
+
+class InvalidRscaleError(ErrorBase):
+    pass
+
+
+
+class RscaleNotAllowedError(ErrorBase):
+    pass

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/icudatetime.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/icudatetime.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/icudatetime.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -15,7 +15,8 @@
 ##
 
 from cffi import FFI
-from __builtin__ import classmethod
+from collections import namedtuple
+
 from pycalendar.datetime import DateTime
 from pycalendar.icalendar import definitions
 
@@ -142,6 +143,8 @@
          int32_t              amount,
          UErrorCode*          status);
 
+    void ucal_clear(UCalendar*  cal);
+
     enum UCalendarLimitType {
       /** Minimum value */
       UCAL_MINIMUM,
@@ -178,14 +181,34 @@
     RSCALE_GREGORIAN = "gregorian"
     RSCALE_HEBREW = "hebrew"
 
-    RSCALE_CALCODE = {
+    # Cache known valid RSCALE values. Not sure how we can automatically
+    # extract these from ICU, so for now they are hard-coded. The dict
+    # values are text prefixes used when generating text representations
+    # of the date.
+    VALID_RSCALES = {
         "gregorian": "",
-        "chinese": "C",
+        "japanese": "J",
+        "buddhist": "BT",
+        "roc": "RC",
+        "persian": "P",
         "islamic-civil": "I",
+        "islamic": "I2",
         "hebrew": "H",
+        "chinese": "C",
+        "indian": "IN",
+        "coptic": "CT",
         "ethiopic": "E",
+        "ethiopic-amete-alem": "E2",
+        "iso8601": "I8",
+        "dangi": "D",
+        "islamic-umalqura": "I3",
+        "islamic-tbla": "I4",
+        "islamic-rgsa": "I5",
     }
 
+    # Used to cache day of month, month, and year day limits for each calscale
+    RSCALE_LIMITS = {}
+
     def __init__(self, rscale, ucal):
         """
         Initialize using an ICU C{ucal} object and the name of the calendar scale.
@@ -214,6 +237,9 @@
     def __del__(self):
         """
         Always close the ICU C{ucal} object.
+
+        FIXME: for PyPy we need to explicitly close all L{ICUDateTime} objects since garbage
+        collection is lazy.
         """
         ICU.ucal_close(self.ucal)
         self.ucal = None
@@ -241,6 +267,10 @@
         return hash(self.getPosixTime())
 
 
+    def __eq__(self, comp):
+        return self.getPosixTime() == comp.getPosixTime()
+
+
     @classmethod
     def fromDateTime(cls, dt, rscale):
         """
@@ -277,9 +307,70 @@
 
 
     @classmethod
+    def allRSCALEs(cls):
+        """
+        Return the L{list} of known RSCALEs.
+
+        @return: all valid RSCALEs
+        @rtype: L{list}
+        """
+
+        return cls.VALID_RSCALES.keys()
+
+
+    @classmethod
+    def validRSCALE(cls, rscale):
+        """
+        Test to see if the specified calendar scale is valid.
+        We use a cache to maintain known values.
+
+        @param rscale: calendar scale to use
+        @type rscale: L{str}
+
+        @return: L{True} if valid, L{False} if not
+        @rtype: L{bool}
+        """
+
+        return rscale.lower() in cls.VALID_RSCALES
+
+
+    @classmethod
+    def limitsRSCALE(cls, rscale):
+        """
+        Get the day, month, yearday limits for the specified RSCALE to use in bounds
+        checking allowed values.
+
+        @param rscale: calendar scale to use
+        @type rscale: L{str}
+
+        @return: a L{dict} containing keys "monthday", "month" and "yearday"
+        @rtype: L{dict}
+        """
+
+        if rscale not in cls.RSCALE_LIMITS:
+            if rscale is None:
+                # Gregorian limits
+                result = {"monthday": 31, "month": 12, "weekno": 53, "yearday": 366}
+            else:
+                # Fetch limits from ICU
+                result = {}
+                ucal = cls._newUcal(rscale)
+                error = ffi.new("UErrorCode *", 0)
+                result["monthday"] = ICU.ucal_getLimit(ucal, ICU.UCAL_DAY_OF_MONTH, ICU.UCAL_MAXIMUM, error)
+                result["month"] = ICU.ucal_getLimit(ucal, ICU.UCAL_MONTH, ICU.UCAL_MAXIMUM, error) + 1
+                result["weekno"] = ICU.ucal_getLimit(ucal, ICU.UCAL_WEEK_OF_YEAR, ICU.UCAL_MAXIMUM, error)
+                result["yearday"] = ICU.ucal_getLimit(ucal, ICU.UCAL_DAY_OF_YEAR, ICU.UCAL_MAXIMUM, error)
+                ICU.ucal_close(ucal)
+            cls.RSCALE_LIMITS[rscale] = result
+
+        return cls.RSCALE_LIMITS[rscale]
+
+
+    @classmethod
     def _newUcal(cls, rscale):
         """
-        Create an ICU C{ucal} object for the specified calendar scale.
+        Create an ICU C{ucal} object for the specified calendar scale. There needs to be a matching call
+        to ICU.ucal_close to avoid leaking memory.
 
         @param rscale: calendar scale to use
         @type rscale: L{str}
@@ -292,6 +383,7 @@
         ucal = ICU.ucal_open(ffi.NULL, -1, ffi.new("char[]", calsystem), ICU.UCAL_DEFAULT, error)
         if error[0] != ICU.U_ZERO_ERROR:
             raise ValueError("Unable to create ICU calendar for rscale '{}', code: {}".format(rscale, error))
+        ICU.ucal_clear(ucal)
         return ucal
 
 
@@ -330,6 +422,22 @@
         return ICUDateTime(rscale, ucal)
 
 
+    MONTH2ICU = {
+        1: ICU.UCAL_JANUARY,
+        2: ICU.UCAL_FEBRUARY,
+        3: ICU.UCAL_MARCH,
+        4: ICU.UCAL_APRIL,
+        5: ICU.UCAL_MAY,
+        6: ICU.UCAL_JUNE,
+        7: ICU.UCAL_JULY,
+        8: ICU.UCAL_AUGUST,
+        9: ICU.UCAL_SEPTEMBER,
+        10: ICU.UCAL_OCTOBER,
+        11: ICU.UCAL_NOVEMBER,
+        12: ICU.UCAL_DECEMBER,
+        13: ICU.UCAL_UNDECIMBER,
+    }
+
     @classmethod
     def _numericMonthToICU(cls, month):
         """
@@ -341,23 +449,25 @@
         @return: the ICU constant
         @rtype: L{ICU.UCalendarMonths}
         """
-        return {
-            1: ICU.UCAL_JANUARY,
-            2: ICU.UCAL_FEBRUARY,
-            3: ICU.UCAL_MARCH,
-            4: ICU.UCAL_APRIL,
-            5: ICU.UCAL_MAY,
-            6: ICU.UCAL_JUNE,
-            7: ICU.UCAL_JULY,
-            8: ICU.UCAL_AUGUST,
-            9: ICU.UCAL_SEPTEMBER,
-            10: ICU.UCAL_OCTOBER,
-            11: ICU.UCAL_NOVEMBER,
-            12: ICU.UCAL_DECEMBER,
-            13: ICU.UCAL_UNDECIMBER,
-        }[month]
+        return cls.MONTH2ICU[month]
 
 
+    ICU2MONTH = {
+        ICU.UCAL_JANUARY: 1,
+        ICU.UCAL_FEBRUARY: 2,
+        ICU.UCAL_MARCH: 3,
+        ICU.UCAL_APRIL: 4,
+        ICU.UCAL_MAY: 5,
+        ICU.UCAL_JUNE: 6,
+        ICU.UCAL_JULY: 7,
+        ICU.UCAL_AUGUST: 8,
+        ICU.UCAL_SEPTEMBER: 9,
+        ICU.UCAL_OCTOBER: 10,
+        ICU.UCAL_NOVEMBER: 11,
+        ICU.UCAL_DECEMBER: 12,
+        ICU.UCAL_UNDECIMBER: 13,
+    }
+
     @classmethod
     def _icuToNumericMonth(cls, month):
         """
@@ -369,23 +479,25 @@
         @return: the month
         @rtype: L{int}
         """
-        return {
-            ICU.UCAL_JANUARY: 1,
-            ICU.UCAL_FEBRUARY: 2,
-            ICU.UCAL_MARCH: 3,
-            ICU.UCAL_APRIL: 4,
-            ICU.UCAL_MAY: 5,
-            ICU.UCAL_JUNE: 6,
-            ICU.UCAL_JULY: 7,
-            ICU.UCAL_AUGUST: 8,
-            ICU.UCAL_SEPTEMBER: 9,
-            ICU.UCAL_OCTOBER: 10,
-            ICU.UCAL_NOVEMBER: 11,
-            ICU.UCAL_DECEMBER: 12,
-            ICU.UCAL_UNDECIMBER: 13,
-        }[month]
+        return cls.ICU2MONTH[month]
 
 
+    TOICU_HEBREW_MONTH = {
+        (1, False): (1, False),
+        (2, False): (2, False),
+        (3, False): (3, False),
+        (4, False): (4, False),
+        (5, False): (5, False),
+        (5, True): (6, False),
+        (6, False): (7, False),
+        (7, False): (8, False),
+        (8, False): (9, False),
+        (9, False): (10, False),
+        (10, False): (11, False),
+        (11, False): (12, False),
+        (12, False): (13, False),
+    }
+
     @classmethod
     def _adjustToICULeapMonth(cls, rscale, month, isleapmonth):
         """
@@ -406,18 +518,31 @@
         """
 
         if rscale.lower() == cls.RSCALE_HEBREW:
-            if month == 5 and isleapmonth:
-                month = 6
-                isleapmonth = None
-            elif month >= 6:
-                month += 1
-        return (month, isleapmonth,)
+            return cls.TOICU_HEBREW_MONTH[(month, isleapmonth,)]
+        else:
+            return (month, isleapmonth,)
 
 
+    FROMICU_HEBREW_MONTH = {
+        (1, False): (1, False),
+        (2, False): (2, False),
+        (3, False): (3, False),
+        (4, False): (4, False),
+        (5, False): (5, False),
+        (6, False): (5, True),
+        (7, False): (6, False),
+        (8, False): (7, False),
+        (9, False): (8, False),
+        (10, False): (9, False),
+        (11, False): (10, False),
+        (12, False): (11, False),
+        (13, False): (12, False),
+    }
+
     @classmethod
     def _adjustFromICULeapMonth(cls, rscale, month, isleapmonth):
         """
-        For the Hebrew calendar, ISU uses a count of 13 months rather than 12 months
+        For the Hebrew calendar, ICU uses a count of 13 months rather than 12 months
         plus an "isleapmonth" indicator. So when converting to/from ICU we need to make
         that adjustment as we always use 12 months + isleapmonth. This method converts
         to our internal representation from what ICU uses.
@@ -434,12 +559,9 @@
         """
 
         if rscale.lower() == cls.RSCALE_HEBREW:
-            isleapmonth = False
-            if month == 6:
-                isleapmonth = True
-            elif month >= 6:
-                month -= 1
-        return (month, isleapmonth,)
+            return cls.FROMICU_HEBREW_MONTH[(month, isleapmonth,)]
+        else:
+            return (month, isleapmonth,)
 
 
     @classmethod
@@ -492,12 +614,13 @@
         @return: the date components
         @rtype: L{tuple} of (L{int}, L{int}, L{int}, L{bool})
         """
-        year = self.getYear()
-        month = self.getMonth()
-        day = self.getDay()
-        isleapmonth = self.getLeapMonth()
 
+        error = ffi.new("UErrorCode *", 0)
+        year = ICU.ucal_get(self.ucal, ICU.UCAL_EXTENDED_YEAR, error)
+        month = self._icuToNumericMonth(ICU.ucal_get(self.ucal, ICU.UCAL_MONTH, error))
+        isleapmonth = ICU.ucal_get(self.ucal, ICU.UCAL_IS_LEAP_MONTH, error) != 0
         month, isleapmonth = self._adjustFromICULeapMonth(self.rscale, month, isleapmonth)
+        day = ICU.ucal_get(self.ucal, ICU.UCAL_DAY_OF_MONTH, error)
 
         return (year, month, day, isleapmonth,)
 
@@ -525,7 +648,6 @@
         self.setYear(year)
         self.setMonth(month, isleapmonth)
         self.setDay(day)
-
         self.testInvalid(year, month, day, isleapmonth)
 
 
@@ -553,13 +675,17 @@
 
     def getMonth(self):
         error = ffi.new("UErrorCode *", 0)
-        return self._icuToNumericMonth(ICU.ucal_get(self.ucal, ICU.UCAL_MONTH, error))
+        month = self._icuToNumericMonth(ICU.ucal_get(self.ucal, ICU.UCAL_MONTH, error))
+        isleapmonth = ICU.ucal_get(self.ucal, ICU.UCAL_IS_LEAP_MONTH, error) != 0
+        month, isleapmonth = self._adjustFromICULeapMonth(self.rscale, month, isleapmonth)
+        return month
 
 
     def setMonth(self, month, isleapmonth=False):
+        adjusted_month, adjusted_isleapmonth = self._adjustToICULeapMonth(self.rscale, month, isleapmonth)
         old_year, _ignore_old_month, old_day, _ignore_old_isleapmonth = self.getDateComponents()
-        ICU.ucal_set(self.ucal, ICU.UCAL_MONTH, self._numericMonthToICU(month))
-        ICU.ucal_set(self.ucal, ICU.UCAL_IS_LEAP_MONTH, isleapmonth)
+        ICU.ucal_set(self.ucal, ICU.UCAL_MONTH, self._numericMonthToICU(adjusted_month))
+        ICU.ucal_set(self.ucal, ICU.UCAL_IS_LEAP_MONTH, adjusted_isleapmonth)
         self.testInvalid(old_year, month, old_day, isleapmonth)
 
 
@@ -576,7 +702,10 @@
 
     def getLeapMonth(self):
         error = ffi.new("UErrorCode *", 0)
-        return ICU.ucal_get(self.ucal, ICU.UCAL_IS_LEAP_MONTH, error) != 0
+        month = self._icuToNumericMonth(ICU.ucal_get(self.ucal, ICU.UCAL_MONTH, error))
+        isleapmonth = ICU.ucal_get(self.ucal, ICU.UCAL_IS_LEAP_MONTH, error) != 0
+        month, isleapmonth = self._adjustFromICULeapMonth(self.rscale, month, isleapmonth)
+        return isleapmonth
 
 
     def getDay(self):
@@ -861,10 +990,11 @@
 
     # When doing recurrence iteration we sometimes need to preserve an invalid value for
     # either day or month (though month is never invalid for Gregorian calendars it can
-    # be for non-Gregorian). For this class we simply set the stored attributes to their
-    # invalid values.
+    # be for non-Gregorian).
+    YYMMDDLL = namedtuple("YYMMDDLL", ("year", "month", "day", "isleapmonth"))
+
     def setInvalid(self, year, month, day, isleapmonth=False):
-        self.mInvalid = (year, month, day, isleapmonth,)
+        self.mInvalid = self.YYMMDDLL(year, month, day, isleapmonth,)
 
 
     def testInvalid(self, year, month, day, isleapmonth=False):
@@ -894,39 +1024,67 @@
         return self.mInvalid is not None
 
 
-    def invalidSkip(self, skip):
+    def invalidSkip(self, skip, monthly=False):
         """
-        If this is an invalid value skip backward or forward or not at all.
+        If this is an invalid value skip backward or forward or not at all. We need to take into account which
+        component (day or month) is invalid to match with the request skip behavior (skip can be done on the
+        month - if L{monthly} is L{True} - or on the day - if L{monthly} is L{False}.
 
-        @param skip: the skip mode (yes, backward, forward)
+        Note that for a monthly skip, the resulting L{ICUDateTime} may have an invalid day component.
+
+        @param skip: the skip mode (omit, backward, forward)
         @type skip: L{int}
+        @param monthly: skip by month if L{True}, otherwise by day
+        @type monthly: L{bool}
+
+        @return: L{True} if a skip was done, L{False} if not
+        @rtype: L{bool}
         """
 
-        if self.mInvalid:
-            if skip == definitions.eRecurrence_SKIP_YES:
-                # Leave it as invalid
-                pass
-            else:
-                # Need to determine which component (day or month/leap) is invalid,
-                # and react accordingly
-                _ignore_y, m, d, l = self.getDateComponents()
-                if (m, l) != (self.mInvalid[1], self.mInvalid[3]):
-                    # Month/leap is invalid
-                    if skip == definitions.eRecurrence_SKIP_BACKWARD:
-                        # Defaults to skip backward
-                        pass
-                    elif skip == definitions.eRecurrence_SKIP_FORWARD:
-                        self.offsetDay(1)
+        if self.mInvalid is not None:
+            # Need to determine which component (day or month/leap) is invalid,
+            # and react accordingly
+            y, _ignore_m, _ignore_d, _ignore_l = self.getDateComponents()
 
-                elif d != self.mInvalid[2]:
+            # Create a new date with the same year and month/leap, but day set to 1 - that will
+            # determine whether the month is invalid
+            ucal_test = self.duplicate()
+            ucal_test.setYYMMDD(self.mInvalid.year, self.mInvalid.month, 1, self.mInvalid.isleapmonth)
+            _ignore_y, test_m, _ignore_d, test_l = ucal_test.getDateComponents()
+            invalid_month = test_m != self.mInvalid.month or test_l != self.mInvalid.isleapmonth
+
+            # Only skip when the relevant component is being tested
+            if monthly == invalid_month:
+
+                if skip == definitions.eRecurrence_SKIP_OMIT:
+                    # Leave it as invalid
+                    return False
+                else:
+
                     if skip == definitions.eRecurrence_SKIP_BACKWARD:
-                        if self.mInvalid[2] < 1:
+                        if monthly:
+                            # Bump to the month before the bogus leap month
+                            self.setYYMMDD(y, self.mInvalid.month, self.mInvalid.day, False)
+                        else:
+                            day = self.mInvalid.day
+                            self.setYYMMDD(y, self.mInvalid.month, 1, self.mInvalid.isleapmonth)
+                            if day > 0:
+                                self.offsetMonth(1)
                             self.offsetDay(-1)
+                            self.clearInvalid()
+
                     elif skip == definitions.eRecurrence_SKIP_FORWARD:
-                        if self.mInvalid[2] > 0:
-                            self.offsetDay(1)
+                        if monthly:
+                            # Bump to the month after the bogus leap month
+                            self.setYYMMDD(y, self.mInvalid.month + 1, self.mInvalid.day, False)
+                        else:
+                            day = self.mInvalid.day
+                            self.setYYMMDD(y, self.mInvalid.month, 1, self.mInvalid.isleapmonth)
+                            if day > 0:
+                                self.offsetMonth(1)
+                            self.clearInvalid()
 
-                self.clearInvalid()
+        return True
 
 
     def normalise(self):
@@ -971,7 +1129,7 @@
         @return: the ISO-8601 text
         @rtype L{str}
         """
-        calcode = self.RSCALE_CALCODE.get(self.rscale.lower(), "{}:".format(self.rscale))
+        calcode = self.VALID_RSCALES.get(self.rscale.lower(), "{}:".format(self.rscale))
         if calcode:
             calcode = "{{{}}}".format(calcode)
         year, month, day, isleapmonth = self.getDateComponents()
@@ -982,14 +1140,14 @@
 
 
 if __name__ == '__main__':
-    newyear = ICUDateTime.fromDateComponents("chinese", 4651, 1, 1, False)
+    newyear = ICUDateTime.fromDateComponents("chinese", 4653, 1, 30, False)
     print("From: {} to {}".format(
         newyear.getText(),
         newyear.convertTo("gregorian").getText(),
     ))
 
-    for i in range(0):
-        newyear.offsetDay(1)
+    for i in range(3):
+        newyear.offsetYear(1)
         print("From: {} to {}".format(
             newyear.getText(),
             newyear.convertTo("gregorian").getText(),

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/recuriter.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/recuriter.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/recuriter.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -23,7 +23,7 @@
     An iterator that iterates a simple recurrence pattern.
     """
 
-    def __init__(self, start, freq, interval, rscale=None, skip=definitions.eRecurrence_SKIP_YES, allow_invalid=False):
+    def __init__(self, start, freq, interval, rscale=None, skip=definitions.eRecurrence_SKIP_OMIT, allow_invalid=False):
         """
         @param start: the start date-time
         @type start: L{DateTime} or L{ICUDateTime}
@@ -105,18 +105,16 @@
             if dt.getDay() != self.start.getDay():
                 if self.allow_invalid:
                     dt.setInvalid(dt.getYear(), dt.getMonth(), self.start.getDay(), dt.getLeapMonth())
-                elif self.skip == definitions.eRecurrence_SKIP_YES:
+                elif self.skip == definitions.eRecurrence_SKIP_OMIT:
                     # Iterate until we have a valid month
                     while dt.getDay() != self.start.getDay():
                         self.step += self.interval
                         dt = self.start.duplicate()
                         dt.offsetMonth(self.step)
-                elif self.skip == definitions.eRecurrence_SKIP_BACKWARD:
-                    # Both ICU and PyCalendar skip back by default
-                    pass
-                elif self.skip == definitions.eRecurrence_SKIP_FORWARD:
-                    # Go one day forward
-                    dt.offsetDay(1)
+                else:
+                    dt.setInvalid(dt.getYear(), dt.getMonth(), self.start.getDay(), dt.getLeapMonth())
+                    dt.invalidSkip(self.skip, True)
+                    dt.invalidSkip(self.skip, False)
 
         elif self.freq == definitions.eRecurrence_YEARLY:
             dt.offsetYear(self.step)
@@ -126,18 +124,16 @@
             if dt.getDay() != self.start.getDay() or dt.getMonth() != self.start.getMonth() or dt.getLeapMonth() != self.start.getLeapMonth():
                 if self.allow_invalid:
                     dt.setInvalid(dt.getYear(), self.start.getMonth(), self.start.getDay(), self.start.getLeapMonth())
-                elif self.skip == definitions.eRecurrence_SKIP_YES:
+                elif self.skip == definitions.eRecurrence_SKIP_OMIT:
                     # Iterate until we have a valid date-time
                     while dt.getDay() != self.start.getDay() or dt.getMonth() != self.start.getMonth() or dt.getLeapMonth() != self.start.getLeapMonth():
                         self.step += self.interval
                         dt = self.start.duplicate()
                         dt.offsetYear(self.step)
-                elif self.skip == definitions.eRecurrence_SKIP_BACKWARD:
-                    # Both ICU and PyCalendar skip back by default
-                    pass
-                elif self.skip == definitions.eRecurrence_SKIP_FORWARD:
-                    # Go one day forward
-                    dt.offsetDay(1)
+                else:
+                    dt.setInvalid(dt.getYear(), self.start.getMonth(), self.start.getDay(), self.start.getLeapMonth())
+                    dt.invalidSkip(self.skip, True)
+                    dt.invalidSkip(self.skip, False)
 
         self.step += self.interval
 
@@ -145,7 +141,9 @@
 
 
 if __name__ == '__main__':
-    icudt = ICUDateTime.fromDateComponents("gregorian", 2014, 1, 31)
-    iter = RecurrenceIterator(icudt, definitions.eRecurrence_MONTHLY, 1, definitions.eRecurrence_SKIP_BACKWARD)
-    for i in range(12):
-        print(iter.next().getText())
+    icudt = ICUDateTime.fromDateComponents("gregorian", 2013, 1, 1)
+    iter = RecurrenceIterator(icudt, definitions.eRecurrence_DAILY, 1)
+    for i in range(1200):
+        gregorian = iter.next()
+        nongregorian = gregorian.convertTo("hebrew")
+        print("{}\t{}".format(gregorian.getText(), nongregorian.getText()))

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/recurrence.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/recurrence.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/recurrence.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -17,12 +17,14 @@
 from pycalendar import xmlutils
 from pycalendar.datetime import DateTime
 from pycalendar.icalendar import definitions, xmldefinitions
-from pycalendar.icalendar.exceptions import TooManyInstancesError
+from pycalendar.icalendar.exceptions import TooManyInstancesError, InvalidRscaleError
+from pycalendar.icalendar.icudatetime import ICUDateTime
+from pycalendar.icalendar.recuriter import RecurrenceIterator
 from pycalendar.period import Period
 from pycalendar.valueutils import ValueMixin
+
 import cStringIO as StringIO
 import xml.etree.cElementTree as XML
-from pycalendar.icalendar.recuriter import RecurrenceIterator
 
 def WeekDayNumCompare_compare(w1, w2):
 
@@ -101,7 +103,7 @@
     cWeekdayRecurMap = dict([(v, k) for k, v in cWeekdayMap.items()])
 
     cSkipMap = {
-        definitions.cICalValue_RECUR_SKIP_YES       : definitions.eRecurrence_SKIP_YES,
+        definitions.cICalValue_RECUR_SKIP_OMIT       : definitions.eRecurrence_SKIP_OMIT,
         definitions.cICalValue_RECUR_SKIP_BACKWARD  : definitions.eRecurrence_SKIP_BACKWARD,
         definitions.cICalValue_RECUR_SKIP_FORWARD   : definitions.eRecurrence_SKIP_FORWARD,
     }
@@ -109,7 +111,7 @@
     cSkipInverseMap = dict([(v, k) for k, v in cSkipMap.items()])
 
     cSkipToXMLMap = {
-        definitions.eRecurrence_SKIP_YES: xmldefinitions.recur_skip_yes,
+        definitions.eRecurrence_SKIP_OMIT: xmldefinitions.recur_skip_omit,
         definitions.eRecurrence_SKIP_BACKWARD: xmldefinitions.recur_skip_backward,
         definitions.eRecurrence_SKIP_FORWARD: xmldefinitions.recur_skip_forward,
     }
@@ -312,6 +314,8 @@
 
 
     def setRscale(self, rscale):
+        if not ICUDateTime.validRSCALE(rscale):
+            raise InvalidRscaleError(rscale)
         self._setAndclearIfChanged("mRscale", rscale)
 
 
@@ -371,10 +375,7 @@
         """
         The default skip value depends on whether RSCALE is used or not
         """
-        if self.mSkip is None:
-            return definitions.eRecurrence_SKIP_YES if self.mRscale is None else definitions.eRecurrence_SKIP_BACKWARD
-        else:
-            return self.mSkip
+        return definitions.eRecurrence_SKIP_OMIT if self.mSkip is None else self.mSkip
 
 
     def setSkip(self, skip):
@@ -517,25 +518,25 @@
                 if self.mByMonthDay is not None:
                     raise ValueError("Recurrence: Only one BYMONTHDAY allowed")
                 self.mByMonthDay = []
-                self.parseList(tvalue, self.mByMonthDay, 1, 31, True, errmsg="Recurrence: Invalid BYMONTHDAY value")
+                self.parseList(tvalue, self.mByMonthDay, 1, ICUDateTime.limitsRSCALE(self.mRscale)["monthday"], True, errmsg="Recurrence: Invalid BYMONTHDAY value")
 
             elif index == definitions.eRecurrence_BYYEARDAY:
                 if self.mByYearDay is not None:
                     raise ValueError("Recurrence: Only one BYYEARDAY allowed")
                 self.mByYearDay = []
-                self.parseList(tvalue, self.mByYearDay, 1, 366, True, errmsg="Recurrence: Invalid BYYEARDAY value")
+                self.parseList(tvalue, self.mByYearDay, 1, ICUDateTime.limitsRSCALE(self.mRscale)["yearday"], True, errmsg="Recurrence: Invalid BYYEARDAY value")
 
             elif index == definitions.eRecurrence_BYWEEKNO:
                 if self.mByWeekNo is not None:
                     raise ValueError("Recurrence: Only one BYWEEKNO allowed")
                 self.mByWeekNo = []
-                self.parseList(tvalue, self.mByWeekNo, 1, 53, True, errmsg="Recurrence: Invalid BYWEEKNO value")
+                self.parseList(tvalue, self.mByWeekNo, 1, ICUDateTime.limitsRSCALE(self.mRscale)["weekno"], True, errmsg="Recurrence: Invalid BYWEEKNO value")
 
             elif index == definitions.eRecurrence_BYMONTH:
                 if self.mByMonth is not None:
                     raise ValueError("Recurrence: Only one BYMONTH allowed")
                 self.mByMonth = []
-                self.parseMonthNumList(tvalue, self.mByMonth, 1, 12, errmsg="Recurrence: Invalid BYMONTH value")
+                self.parseMonthNumList(tvalue, self.mByMonth, 1, ICUDateTime.limitsRSCALE(self.mRscale)["month"], errmsg="Recurrence: Invalid BYMONTH value")
 
             elif index == definitions.eRecurrence_BYSETPOS:
                 if self.mBySetPos is not None:
@@ -551,6 +552,8 @@
 
             elif index == definitions.eRecurrence_RSCALE:
                 self.mRscale = tvalue.upper()
+                if not ICUDateTime.validRSCALE(self.mRscale):
+                    raise InvalidRscaleError(self.mRscale)
 
             elif index == definitions.eRecurrence_SKIP:
                 # Get the SKIP value
@@ -1074,25 +1077,25 @@
         while True:
             start_iter = riter.next()
 
+            # Exit if next item is after until (it is OK if it is the same as
+            # UNTIL as UNTIL is inclusive)
+            if self.mUseUntil and start_iter > float_until:
+                return True
+
             # Exit if after period we want
             if range.isDateAfterPeriod(start_iter):
                 return False
-            elif self.mUseUntil:
-                # Exit if next item is after until (it is OK if it is the same as
-                # UNTIL as UNTIL is inclusive)
-                if start_iter > float_until:
-                    return True
 
             # Add current one to list
             results.append(start_iter)
+
+            # Check maximum limit
             if maxInstances and len(results) > maxInstances:
                 raise TooManyInstancesError("Too many instances")
 
-            # Check limits
-            if self.mUseCount:
-                # Exit if max count reached
-                if len(results) >= self.mCount:
-                    return True
+            # Check count limit and exit if max count reached
+            if self.mUseCount and len(results) >= self.mCount:
+                return True
 
 
     def complexExpand(self, start, range, results, float_offset, maxInstances=None):
@@ -1135,17 +1138,9 @@
             elif self.mFreq == definitions.eRecurrence_YEARLY:
                 self.generateYearlySet(start_iter, set_items)
 
-            # Remove invalid items before BYSETPOS
-            def _invalidMap(dt):
-                dt.invalidSkip(self.effectiveSkip())
-                return dt
-            set_items = map(lambda x: _invalidMap(x), set_items)
-            set_items = filter(lambda x: not x.invalid(), set_items)
+            # Always de-duplicate and sort the set as BYxxx rules may not be sorted
+            set_items = sorted(set(set_items), key=lambda x: x.getPosixTime())
 
-            # Always sort the set as BYxxx rules may not be sorted
-            # set_items.sort(cmp=DateTime.sort)
-            set_items.sort(key=lambda x: x.getPosixTime())
-
             if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
                 set_items[:] = self.bySetPosLimit(set_items)
 
@@ -1164,27 +1159,25 @@
                 if iter < start:
                     continue
 
+                # Exit if next item is after until (its OK if its the same
+                # as UNTIL as UNTIL is inclusive)
+                if self.mUseUntil and iter > float_until:
+                    return True
+
                 # Exit if after period we want
                 if range.isDateAfterPeriod(iter):
                     return False
 
-                # Exit if beyond the UNTIL limit
-                elif self.mUseUntil:
-                    # Exit if next item is after until (its OK if its the same
-                    # as UNTIL as UNTIL is inclusive)
-                    if iter > float_until:
-                        return True
-
                 # Add current one to list
                 results.append(iter)
+
+                # Check maximum limit
                 if maxInstances and len(results) > maxInstances:
                     raise TooManyInstancesError("Too many instances")
 
-                # Check limits
-                if self.mUseCount:
-                    # Exit if max count reached
-                    if len(results) >= self.mCount:
-                        return True
+                # Check count limit and exit if max count reached
+                if self.mUseCount and len(results) >= self.mCount:
+                    return True
 
 
     def clear(self):
@@ -1225,6 +1218,10 @@
         self.clear()
 
 
+    def applySkip(self, items, monthly):
+        items[:] = filter(lambda x: x.invalidSkip(self.effectiveSkip(), monthly), items)
+
+
     def generateYearlySet(self, start, items):
         # All possible BYxxx are valid, though some combinations are not
 
@@ -1233,6 +1230,7 @@
 
         if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
             items[:] = self.byMonthExpand(items)
+        self.applySkip(items, True)
 
         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
             items[:] = self.byWeekNoExpand(items)
@@ -1259,6 +1257,7 @@
                 items[:] = self.byDayExpandMonthly(items)
             else:
                 items[:] = self.byDayExpandYearly(items)
+        self.applySkip(items, False)
 
         if (self.mByHours is not None) and (len(self.mByHours) != 0):
             items[:] = self.byHourExpand(items)
@@ -1281,6 +1280,7 @@
             items[:] = self.byMonthLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, True)
 
         # No BYWEEKNO
 
@@ -1301,6 +1301,7 @@
                 items[:] = self.byDayLimit(items)
             else:
                 items[:] = self.byDayExpandMonthly(items)
+        self.applySkip(items, False)
 
         if ((self.mByHours is not None) and (len(self.mByHours) != 0)):
             items[:] = self.byHourExpand(items)
@@ -1323,6 +1324,7 @@
             items[:] = self.byMonthLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, True)
 
         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
             items[:] = self.byWeekNoLimit(items)
@@ -1335,6 +1337,7 @@
 
         if (self.mByDay is not None) and (len(self.mByDay) != 0):
             items[:] = self.byDayExpandWeekly(items)
+        self.applySkip(items, False)
 
         if (self.mByHours is not None) and (len(self.mByHours) != 0):
             items[:] = self.byHourExpand(items)
@@ -1357,6 +1360,7 @@
             items[:] = self.byMonthLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, True)
 
         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
             items[:] = self.byWeekNoLimit(items)
@@ -1374,6 +1378,7 @@
             items[:] = self.byDayLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, False)
 
         if (self.mByHours is not None) and (len(self.mByHours) != 0):
             items[:] = self.byHourExpand(items)
@@ -1396,6 +1401,7 @@
             items[:] = self.byMonthLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, True)
 
         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
             items[:] = self.byWeekNoLimit(items)
@@ -1413,6 +1419,7 @@
             items[:] = self.byDayLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, False)
 
         if (self.mByHours is not None) and (len(self.mByHours) != 0):
             items[:] = self.byHourLimit(items)
@@ -1437,6 +1444,7 @@
             items[:] = self.byMonthLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, True)
 
         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
             items[:] = self.byWeekNoLimit(items)
@@ -1454,6 +1462,7 @@
             items[:] = self.byDayLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, False)
 
         if (self.mByHours is not None) and (len(self.mByHours) != 0):
             items[:] = self.byHourLimit(items)
@@ -1480,6 +1489,7 @@
             items[:] = self.byMonthLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, True)
 
         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
             items[:] = self.byWeekNoLimit(items)
@@ -1497,6 +1507,7 @@
             items[:] = self.byDayLimit(items)
             if (len(items) == 0):
                 return
+        self.applySkip(items, False)
 
         if (self.mByHours is not None) and (len(self.mByHours) != 0):
             items[:] = self.byHourLimit(items)
@@ -1692,7 +1703,7 @@
 
     def byMonthLimit(self, dates):
         # Keep each date that matches a BYMONTH
-        return filter(lambda date: date.getMonth() in self.mByMonth, dates)
+        return filter(lambda date: (date.getMonth(), date.getLeapMonth(),) in self.mByMonth, dates)
 
 
     def byWeekNoLimit(self, dates):

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/rscale_examples.json
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/rscale_examples.json	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/rscale_examples.json	2015-09-17 17:55:04 UTC (rev 15129)
@@ -1,7 +1,10 @@
 [
 	{
-		"title": "MonthlyRscaleStartInLeapYearSkipYes - start: {C}46501230",
-		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;SKIP=YES"],
+		"title": "MonthlyRscaleStartInLeapYearSkipOmit - start: {C}46501230",
+		"rule": [
+			"RSCALE=CHINESE;FREQ=MONTHLY;SKIP=OMIT",
+            "RSCALE=CHINESE;FREQ=MONTHLY"
+		],
 		"start": "20140130",
 		"end": "20180101",
 		"results": [
@@ -92,10 +95,7 @@
 	},
 	{
 		"title": "MonthlyRscaleStartInLeapYearSkipBackwardDefault - start: {C}46501230",
-		"rule": [
-            "RSCALE=CHINESE;FREQ=MONTHLY;SKIP=BACKWARD",
-            "RSCALE=CHINESE;FREQ=MONTHLY"
-		],
+		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;SKIP=BACKWARD"],
 		"start": "20140130",
 		"end": "20180101",
 		"results": [
@@ -151,8 +151,11 @@
 		]
 	},
 	{
-		"title": "YearlyLeapDaySkipYes",
-		"rule": ["RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=YES;COUNT=5"],
+		"title": "YearlyLeapDaySkipOmit",
+		"rule": [
+			"RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=OMIT;COUNT=5",
+            "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=5"
+		],
 		"start": "20160229",
 		"end": "21000101",
 		"results": [
@@ -178,10 +181,7 @@
 	},
 	{
 		"title": "YearlyLeapDaySkipBackwardDefault",
-		"rule": [
-            "RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=BACKWARD;COUNT=5",
-            "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=5"
-		],
+		"rule": ["RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=BACKWARD;COUNT=5"],
 		"start": "20160229",
 		"end": "21000101",
 		"results": [
@@ -193,8 +193,8 @@
 		]
 	},
 	{
-		"title": "ChineseMonthlyByMonthDay30SkipYes",
-		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=30;SKIP=YES"],
+		"title": "ChineseMonthlyByMonthDay30SkipOmit",
+		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=30;SKIP=OMIT"],
 		"start": "20140130T120000",
 		"end": "20150101T000000",
 		"results": [
@@ -248,8 +248,8 @@
 		]
 	},
 	{
-		"title": "ChineseMonthlyByMonthDayMinus30SkipYes",
-		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=-30;SKIP=YES"],
+		"title": "ChineseMonthlyByMonthDayMinus30SkipOmit",
+		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=-30;SKIP=OMIT"],
 		"start": "20140130T120000",
 		"end": "20150101T000000",
 		"results": [
@@ -300,5 +300,316 @@
             "20141122T120000",
             "20141222T120000"
 		]
+	},
+	{
+		"title": "Ethiopic last day of year",
+		"rule": ["RSCALE=ETHIOPIC;FREQ=YEARLY;BYMONTH=13;BYMONTHDAY=-1;COUNT=6"],
+		"start": "20140910",
+		"end": "20200101",
+		"results": ["20140910","20150911","20160910","20170910","20180910","20190911"]
+	},
+
+
+	{
+		"title": "Chinese New Year",
+		"rule": ["RSCALE=CHINESE;FREQ=YEARLY;UNTIL=20180101"],
+		"start": "20130210",
+		"end": "20180101",
+		"results": ["20130210","20140131","20150219","20160208","20170128"]
+	},
+
+
+	{
+		"title": "Chinese monthly",
+		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;COUNT=4"],
+		"start": "20140920",
+		"end": "20150101",
+		"results": ["20140920","20141020","20141119","20141218"]
+	},
+
+
+	{
+		"title": "Islamic monthly",
+		"rule": ["RSCALE=ISLAMIC-CIVIL;FREQ=MONTHLY;COUNT=4"],
+		"start": "20131025",
+		"end": "20150101",
+		"results": ["20131025","20131124","20131224","20140122"]
+	},
+
+
+	{
+		"title": "Ramadan",
+		"rule": ["RSCALE=ISLAMIC-CIVIL;FREQ=YEARLY;BYMONTH=9;COUNT=5"],
+		"start": "20130709",
+		"end": "20180101",
+		"results": ["20130709","20140629","20150618","20160607","20170527"]
+	},
+
+
+	{
+		"title": "Buddha birthday",
+		"rule": ["RSCALE=DANGI;FREQ=DAILY;BYMONTHDAY=8;BYMONTH=4;UNTIL=20160101"],
+		"start": "20131025",
+		"end": "20160101",
+		"results": ["20140506","20150525"]
+	},
+
+
+	{
+		"title": "No instances in Chinese leap month",
+		"rule": ["RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9;COUNT=3"],
+		"start": "20131025",
+		"end": "20170101",
+		"results": ["20141003","20151022","20161010"]
+	},
+
+
+	{
+		"title": "Chinese 9L leap month skip - daily",
+		"rule": ["RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2"],
+		"start": "20141102",
+		"end": "21500101",
+		"results": ["20141102","21091102"]
+	},
+
+
+	{
+		"title": "Chinese 9L leap month skip - monthly",
+		"rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2"],
+		"start": "20141102",
+		"end": "21500101",
+		"results": ["20141102","21091102"]
+	},
+
+
+	{
+		"title": "Chinese 9L leap month skip - yearly",
+		"rule": ["RSCALE=CHINESE;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2"],
+		"start": "20141102",
+		"end": "21500101",
+		"results": ["20141102","21091102"]
+	},
+
+
+	{
+		"title": "Chinese 4L leap month skip - daily",
+		"rule": ["FREQ=DAILY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT"],
+		"start": "20131025",
+		"end": "21000101",
+		"results": ["20200601","20580531","20690530","20770531","20880530","20960531"]
+	},
+
+
+	{
+		"title": "Chinese 4L leap month skip - monthly",
+		"rule": ["FREQ=MONTHLY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT"],
+		"start": "20131025",
+		"end": "21000101",
+		"results": ["20200601","20580531","20690530","20770531","20880530","20960531"]
+	},
+
+
+	{
+		"title": "Chinese 4L leap month skip - yearly",
+		"rule": ["FREQ=YEARLY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT"],
+		"start": "20131025",
+		"end": "21000101",
+		"results": ["20200601","20580531","20690530","20770531","20880530","20960531"]
+	},
+
+
+	{
+		"title": "Chinese 9L leap month skip back - daily",
+		"rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3"],
+		"start": "20131025",
+		"end": "20170101",
+		"results": ["20141102"]
+	},
+
+
+	{
+		"title": "Chinese 9L leap month skip back - monthly",
+		"rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3"],
+		"start": "20131025",
+		"end": "20170101",
+		"results": ["20141102"]
+	},
+
+
+	{
+		"title": "Chinese 9L leap month skip back - yearly",
+		"rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3"],
+		"start": "20131025",
+		"end": "20170101",
+		"results": ["20141102","20151022","20161010"]
+	},
+
+
+	{
+		"title": "Chinese 9,9L leap month skip back - daily",
+		"rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4"],
+		"start": "20131025",
+		"end": "20170101",
+		"results": ["20141003","20141102","20151022","20161010"]
+	},
+
+	{
+		"title": "Chinese 9,9L leap month skip back - monthly",
+		"rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4"],
+		"start": "20131025",
+		"end": "20170101",
+		"results": ["20141003","20141102","20151022","20161010"]
+	},
+
+	{
+		"title": "Chinese 9,9L leap month skip back - yearly",
+		"rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4"],
+		"start": "20131025",
+		"end": "20170101",
+		"results": ["20141003","20141102","20151022","20161010"]
+	},
+
+
+	{
+		"title": "Hebrew yearly skip",
+		"rule": ["RSCALE=HEBREW;FREQ=YEARLY;COUNT=4"],
+		"start": "20140205",
+		"end": "20230101",
+		"results": ["20140205","20160214","20190210","20220206"]
+	},
+
+
+	{
+		"title": "Hebrew yearly skip forward",
+		"rule": ["RSCALE=HEBREW;FREQ=YEARLY;SKIP=FORWARD;COUNT=4"],
+		"start": "20140205",
+		"end": "20180101",
+		"results": ["20140205","20150224","20160214","20170303"]
+	},
+
+
+	{
+		"title": "Hebrew yearly 5L skip forward",
+		"rule": ["RSCALE=HEBREW;FREQ=YEARLY;BYMONTH=5L;BYMONTHDAY=8;SKIP=FORWARD;COUNT=5"],
+		"start": "20140208",
+		"end": "20190101",
+		"results": ["20140208","20150227","20160217","20170306","20180223"]
+	},
+
+
+	{
+		"title": "Gregorian monthly skip back",
+		"rule": ["RSCALE=GREGORIAN;FREQ=MONTHLY;SKIP=BACKWARD;COUNT=4"],
+		"start": "20140131",
+		"end": "20150101",
+		"results": ["20140131","20140228","20140331","20140430"]
+	},
+
+
+	{
+		"title": "Gregorian monthly skip forward",
+		"rule": ["RSCALE=GREGORIAN;FREQ=MONTHLY;SKIP=FORWARD;COUNT=4"],
+		"start": "20140131",
+		"end": "20150101",
+		"results": ["20140131","20140301","20140331","20140501"]
+	},
+
+
+	{
+		"title": "Gregorian leap day skip forward",
+		"rule": ["RSCALE=GREGORIAN;FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=28,29;SKIP=FORWARD;COUNT=5"],
+		"start": "20150201",
+		"end": "20180101",
+		"results": ["20150228","20150301","20160228","20160229","20170228"]
+	},
+
+
+	{
+		"title": "Gregorian monthly interval skip forward",
+		"rule": ["RSCALE=GREGORIAN;FREQ=MONTHLY;INTERVAL=3;SKIP=FORWARD;COUNT=4"],
+		"start": "20140131",
+		"end": "20150101",
+		"results": ["20140131","20140501","20140731","20141031"]
+	},
+
+
+	{
+		"title": "Gregorian month day skip forward",
+		"rule": ["FREQ=MONTHLY;RSCALE=GREGORIAN;SKIP=FORWARD;COUNT=12;BYMONTHDAY=31"],
+		"start": "20150131",
+		"end": "20160101",
+		"results": ["20150131","20150301","20150331","20150501","20150531","20150701","20150731","20150831","20151001","20151031","20151201","20151231"]
+	},
+
+
+	{
+		"title": "Gregorian last month day skip forward",
+		"rule": ["FREQ=MONTHLY;RSCALE=GREGORIAN;SKIP=FORWARD;COUNT=12;BYMONTHDAY=-1"],
+		"start": "20150131",
+		"end": "20160101",
+		"results": ["20150131","20150228","20150331","20150430","20150531","20150630","20150731","20150831","20150930","20151031","20151130","20151231"]
+	},
+
+
+	{
+		"title": "Hebrew leap month day skip forward",
+		"rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=FORWARD;COUNT=5;BYMONTHDAY=30;BYMONTH=5L"],
+		"start": "20140302",
+		"end": "20190101",
+		"results": ["20140302","20150321","20160310","20170328","20180317"]
+	},
+
+
+	{
+		"title": "Hebrew last leap month day skip forward",
+		"rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=FORWARD;COUNT=5;BYMONTHDAY=-1;BYMONTH=5L"],
+		"start": "20140302",
+		"end": "20190101",
+		"results": ["20140302","20150320","20160310","20170327","20180316"]
+	},
+
+
+	{
+		"title": "Hebrew leap month day skip back",
+		"rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=BACKWARD;COUNT=5;BYMONTHDAY=30;BYMONTH=5L"],
+		"start": "20140302",
+		"end": "20190101",
+		"results": ["20140302","20150219","20160310","20170226","20180215"]
+	},
+
+
+	{
+		"title": "Hebrew last leap month day skip forward",
+		"rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=BACKWARD;COUNT=5;BYMONTHDAY=-1;BYMONTH=5L"],
+		"start": "20140302",
+		"end": "20190101",
+		"results": ["20140302","20150219","20160310","20170226","20180215"]
+	},
+
+
+	{
+		"title": "Chinese leap month day skip forward - daily",
+		"rule": ["FREQ=DAILY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L"],
+		"start": "20131025",
+		"end": "20160101",
+		"results": ["20141102"]
+	},
+
+
+	{
+		"title": "Chinese leap month day skip forward - monthly",
+		"rule": ["FREQ=MONTHLY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L"],
+		"start": "20131025",
+		"end": "20160101",
+		"results": ["20141102"]
+	},
+
+
+	{
+		"title": "Chinese leap month day skip forward - yearly",
+		"rule": ["FREQ=YEARLY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L"],
+		"start": "20131025",
+		"end": "20160101",
+		"results": ["20131112", "20141102","20151121"]
 	}
 ]

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_icudatetime.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_icudatetime.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_icudatetime.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -17,6 +17,7 @@
 
 import unittest
 from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
 from pycalendar.icalendar.icudatetime import ICUDateTime
 
 class TestICUDateTime(unittest.TestCase):
@@ -24,6 +25,37 @@
     Test L{ICUDateTime}
     """
 
+    def testValidRscale(self):
+
+        data_rscale = (
+            ("gregorian", True,),
+            ("chinese", True,),
+            ("foo", False,),
+            ("gregorian", True,),
+            ("chinese", True,),
+            ("foo", False,),
+        )
+
+        for rscale, result in data_rscale:
+            valid = ICUDateTime.validRSCALE(rscale)
+            self.assertEqual(valid, result, "Failed on: %s" % (rscale,))
+
+
+    def testLimitsRscale(self):
+
+        data_rscale = (
+            (None, {"monthday": 31, "month": 12, "weekno": 53, "yearday": 366},),
+            ("gregorian", {"monthday": 31, "month": 12, "weekno": 53, "yearday": 366},),
+            ("chinese", {"monthday": 30, "month": 12, "weekno": 55, "yearday": 385},),
+            ("ethiopic", {"monthday": 30, "month": 13, "weekno": 53, "yearday": 366},),
+            ("hebrew", {"monthday": 30, "month": 13, "weekno": 56, "yearday": 385},),
+        )
+
+        for rscale, result in data_rscale:
+            limit = ICUDateTime.limitsRSCALE(rscale)
+            self.assertEqual(limit, result, "Failed on: %s: %s" % (rscale, limit))
+
+
     def testRoundtripDateText(self):
 
         data_date = (
@@ -91,17 +123,23 @@
     def testSetMonth(self):
 
         data_date = (
-            ("gregorian", 2012, 1, 2, False, 2, "20120202", False,),
-            ("gregorian", 2012, 1, 29, False, 2, "20120229", False,),
-            ("gregorian", 2012, 1, 31, False, 2, "20120302", True,),
-            ("gregorian", 2012, 2, 29, False, 3, "20120329", False,),
+            ("gregorian", 2012, 1, 2, False, 2, False, "20120202", False,),
+            ("gregorian", 2012, 1, 29, False, 2, False, "20120229", False,),
+            ("gregorian", 2012, 1, 31, False, 2, False, "20120302", True,),
+            ("gregorian", 2012, 2, 29, False, 3, False, "20120329", False,),
+            ("chinese", 4651, 1, 1, False, 9, False, "{C}46510901", False,),
+            ("chinese", 4651, 1, 1, False, 9, True, "{C}465109L01", False,),
+            ("chinese", 4651, 1, 1, False, 10, False, "{C}46511001", False,),
+            ("hebrew", 5774, 1, 1, False, 5, False, "{H}57740501", False,),
+            ("hebrew", 5774, 1, 1, False, 5, True, "{H}577405L01", False,),
+            ("hebrew", 5774, 1, 1, False, 6, False, "{H}57740601", False,),
         )
 
-        for rscale, y, m, d, l, month, result, result_invalid in data_date:
+        for rscale, y, m, d, l, month, leapmonth, result, result_invalid in data_date:
             dt = ICUDateTime.fromDateComponents(rscale, y, m, d, l)
-            dt.setMonth(month)
-            self.assertEqual(dt.getText(), result, "Failed on: {} {} {}".format(month, result, result_invalid,))
-            self.assertEqual(dt.invalid(), result_invalid, "Failed invalid on: {} {} {}".format(month, result, result_invalid,))
+            dt.setMonth(month, leapmonth)
+            self.assertEqual(dt.getText(), result, "Failed on: {} {} {} {}".format(month, leapmonth, result, result_invalid,))
+            self.assertEqual(dt.invalid(), result_invalid, "Failed invalid on: {} {} {} {}".format(month, leapmonth, result, result_invalid,))
 
 
     def testOffsetMonth(self):
@@ -223,3 +261,40 @@
             dt = ICUDateTime.fromDateComponents(rscale, y, m, d, l)
             dt.setDayOfWeekInYear(offset, day)
             self.assertEqual(dt.getText(), result, "Failed on: {} vs {}".format(result, dt.getText(),))
+
+
+    def testSkip(self):
+
+        data_date = (
+            ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_BACKWARD, False, "20150228",),
+            ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_FORWARD, False, "20150301",),
+            ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_OMIT, False, None,),
+            ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_BACKWARD, True, None,),
+            ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_FORWARD, True, None,),
+            ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_OMIT, True, None,),
+            ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_BACKWARD, False, None,),
+            ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_FORWARD, False, None,),
+            ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_OMIT, False, None,),
+            ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_BACKWARD, True, "{C}46520902",),
+            ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_FORWARD, True, "{C}46521002",),
+            ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_OMIT, True, None,),
+        )
+
+        ctr = 0
+        for rscale, y, m, d, l, skip, skip_monthly, result in data_date:
+            ctr += 1
+            dt = DateTime.getToday()
+            udt = ICUDateTime.fromDateTime(dt, rscale)
+            udt.setYYMMDD(y, m, d, l)
+            self.assertEqual(udt.invalid(), True)
+            udt.invalidSkip(skip, skip_monthly)
+            test_value = udt.getText() if not udt.invalid() else None
+            self.assertEqual(test_value, result, msg="Failed: #{} {} vs {}".format(ctr, test_value, result))
+
+
+    def testDuplicates(self):
+
+        dt1 = ICUDateTime.fromDateComponents("gregorian", 2011, 1, 2, False)
+        dt2 = ICUDateTime.fromDateComponents("gregorian", 2011, 1, 2, False)
+        dt3 = ICUDateTime.fromDateComponents("gregorian", 2011, 1, 3, False)
+        self.assertEqual(len(set((dt1, dt2, dt3,))), 2)

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recuriter.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recuriter.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recuriter.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -23,9 +23,9 @@
 
 class MonthlySkips(object):
 
-    def testMonthlySkipYes(self):
+    def testMonthlySkipOmit(self):
 
-        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_MONTHLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_YES)
+        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_MONTHLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_OMIT)
         results = [riter.next().getText() for _ in range(12)]
         self.assertEqual(
             results,
@@ -136,9 +136,9 @@
 
 class YearlySkipsOnLeapDay(object):
 
-    def testYearlySkipYes(self):
+    def testYearlySkipOmit(self):
 
-        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_YEARLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_YES)
+        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_YEARLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_OMIT)
         results = [riter.next().getText() for _ in range(5)]
         self.assertEqual(
             results,
@@ -203,10 +203,10 @@
 
 class TestMonthlyChineseICU(unittest.TestCase):
 
-    def testMonthlyStartInLeapYearSkipYes(self):
+    def testMonthlyStartInLeapYearSkipOmit(self):
         dt = ICUDateTime.fromDateComponents("chinese", 4650, 12, 30)
 
-        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale=None, skip=definitions.eRecurrence_SKIP_YES)
+        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale=None, skip=definitions.eRecurrence_SKIP_OMIT)
         results = []
         while True:
             result = riter.next()
@@ -384,10 +384,10 @@
         )
 
 
-    def testMonthlyRscaleStartInLeapYearSkipYes(self):
+    def testMonthlyRscaleStartInLeapYearSkipOmit(self):
         dt = ICUDateTime.fromDateComponents("chinese", 4650, 12, 30).toDateTime()
 
-        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale="chinese", skip=definitions.eRecurrence_SKIP_YES)
+        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale="chinese", skip=definitions.eRecurrence_SKIP_OMIT)
         results = []
         while True:
             result = riter.next()

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recurrence.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recurrence.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recurrence.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -56,7 +56,7 @@
 
         # RSCALE
         "RSCALE=CHINESE;FREQ=DAILY",
-        "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=YES",
+        "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=OMIT",
         "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=BACKWARD",
         "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=FORWARD",
         "RSCALE=CHINESE;FREQ=YEARLY;BYMONTH=5,6,6L,7",
@@ -89,6 +89,7 @@
             "FREQ=MONTHLY;BYHOUR=54",
             "FREQ=MONTHLY;SKIP=YES",
             "RSCALE=CHINESE;FREQ=MONTHLY;SKIP=NO",
+            "RSCALE=CHINESE;FREQ=MONTHLY;SKIP=YES",
         )
 
         for item in items:
@@ -207,7 +208,7 @@
 
             rules = [i["rule"]]
             if "RSCALE" not in rules[0]:
-                rules.append("RSCALE=GREGORIAN;{};SKIP=YES".format(rules[0]))
+                rules.append("RSCALE=GREGORIAN;{};SKIP=OMIT".format(rules[0]))
             for rule in rules:
                 recur = Recurrence()
                 recur.parse(rule)
@@ -221,10 +222,43 @@
                 self.assertEqual(
                     items,
                     results,
-                    msg="Failed rule: #{} {}".format(ctr + 1, rule)
+                    msg="Failed rule: #{ctr} {rule}: {items}".format(ctr=ctr + 1, rule=rule, items=items)
                 )
 
 
+    def testLimitWithUntilAndCount(self):
+
+        recur = Recurrence()
+        recur.parse("FREQ=WEEKLY;UNTIL=20130110")
+
+        start = DateTime(2013, 1, 1, 0, 0, 0)
+        end = DateTime(2013, 1, 9, 0, 0, 0)
+        range = Period(start, end)
+        items = []
+        limited = recur.expand(start, range, items)
+        self.assertFalse(limited)
+
+        recur = Recurrence()
+        recur.parse("FREQ=WEEKLY;UNTIL=20130110")
+
+        start = DateTime(2013, 1, 1, 0, 0, 0)
+        end = DateTime(2013, 1, 11, 0, 0, 0)
+        range = Period(start, end)
+        items = []
+        limited = recur.expand(start, range, items)
+        self.assertFalse(limited)
+
+        recur = Recurrence()
+        recur.parse("FREQ=WEEKLY;UNTIL=20130110")
+
+        start = DateTime(2013, 1, 1, 0, 0, 0)
+        end = DateTime(2013, 1, 6, 0, 0, 0)
+        range = Period(start, end)
+        items = []
+        limited = recur.expand(start, range, items)
+        self.assertTrue(limited)
+
+
     def testClearOnChange(self):
 
         recur = Recurrence()
@@ -257,7 +291,6 @@
             examples = json.loads(f.read())
 
         for ctr, i in enumerate(examples):
-
             for rule in i["rule"]:
                 recur = Recurrence()
                 recur.parse(rule)
@@ -271,5 +304,5 @@
                 self.assertEqual(
                     items,
                     results,
-                    msg="Failed rule: #{} {}".format(ctr + 1, rule)
+                    msg="Failed rule: #{ctr} {rule}: {items}".format(ctr=ctr + 1, rule=rule, items=items)
                 )

Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/xmldefinitions.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/icalendar/xmldefinitions.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/icalendar/xmldefinitions.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -38,7 +38,7 @@
 recur_interval = "interval"
 
 recur_skip = "skip"
-recur_skip_yes = "yes"
+recur_skip_omit = "omit"
 recur_skip_backward = "backward"
 recur_skip_forward = "forward"
 

Modified: PyCalendar/branches/rscale/src/pycalendar/property.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/property.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/property.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -301,8 +301,8 @@
 
             return prop
 
-        except Exception:
-            raise InvalidProperty("Invalid property", data)
+        except Exception as e:
+            raise InvalidProperty("Invalid property", data, cause=e)
 
 
     def parseTextParameters(self, txt, data):

Modified: PyCalendar/branches/rscale/src/pycalendar/tests/test_datetime.py
===================================================================
--- PyCalendar/branches/rscale/src/pycalendar/tests/test_datetime.py	2015-09-14 16:50:02 UTC (rev 15128)
+++ PyCalendar/branches/rscale/src/pycalendar/tests/test_datetime.py	2015-09-17 17:55:04 UTC (rev 15129)
@@ -157,6 +157,74 @@
             self.assertEqual(str(dt), result)
 
 
+    def testCacheChangeOnAdjustment(self):
+
+        # UTC first
+        dt = DateTime(2012, 1, 1, 12, 0, 0, Timezone(tzid="utc"))
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.setYear(2013)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.offsetYear(1)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.setMonth(2)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.offsetMonth(1)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.setDay(2)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.offsetDay(1)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.setHours(2)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.offsetHours(1)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.setMinutes(2)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.offsetMinutes(1)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.setSeconds(2)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+        dt.offsetSeconds(1)
+        self.assertFalse(dt.mPosixTimeCached)
+        dt.getPosixTime()
+        self.assertTrue(dt.mPosixTimeCached)
+
+
     def testCachePreserveOnAdjustment(self):
 
         # UTC first
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150917/47cae19e/attachment-0001.html>


More information about the calendarserver-changes mailing list