<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[15129] PyCalendar/branches/rscale/src/pycalendar</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/15129">15129</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2015-09-17 10:55:04 -0700 (Thu, 17 Sep 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Sync-up with latest RFC (default skip behavior). Fix various skip issues.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#PyCalendarbranchesrscalesrcpycalendardatetimepy">PyCalendar/branches/rscale/src/pycalendar/datetime.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendarexceptionspy">PyCalendar/branches/rscale/src/pycalendar/exceptions.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendardefinitionspy">PyCalendar/branches/rscale/src/pycalendar/icalendar/definitions.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendarexceptionspy">PyCalendar/branches/rscale/src/pycalendar/icalendar/exceptions.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendaricudatetimepy">PyCalendar/branches/rscale/src/pycalendar/icalendar/icudatetime.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendarrecuriterpy">PyCalendar/branches/rscale/src/pycalendar/icalendar/recuriter.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendarrecurrencepy">PyCalendar/branches/rscale/src/pycalendar/icalendar/recurrence.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendartestsrscale_examplesjson">PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/rscale_examples.json</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendarteststest_icudatetimepy">PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_icudatetime.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendarteststest_recuriterpy">PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recuriter.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendarteststest_recurrencepy">PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recurrence.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendaricalendarxmldefinitionspy">PyCalendar/branches/rscale/src/pycalendar/icalendar/xmldefinitions.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendarpropertypy">PyCalendar/branches/rscale/src/pycalendar/property.py</a></li>
<li><a href="#PyCalendarbranchesrscalesrcpycalendarteststest_datetimepy">PyCalendar/branches/rscale/src/pycalendar/tests/test_datetime.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="PyCalendarbranchesrscalesrcpycalendardatetimepy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/datetime.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -293,6 +293,7 @@
</span><span class="cx">         # the new date is invalid
</span><span class="cx">         if self.mDay &gt; utils.daysInMonth(self.mMonth, self.mYear):
</span><span class="cx">             self.mDay = utils.daysInMonth(self.mMonth, self.mYear)
</span><ins>+        self.changed()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getMonth(self):
</span><span class="lines">@@ -321,6 +322,7 @@
</span><span class="cx">         # the new date is invalid
</span><span class="cx">         if self.mDay &gt; utils.daysInMonth(self.mMonth, self.mYear):
</span><span class="cx">             self.mDay = utils.daysInMonth(self.mMonth, self.mYear)
</span><ins>+        self.changed()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getLeapMonth(self):
</span><span class="lines">@@ -1161,7 +1163,7 @@
</span><span class="cx">         return False
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def invalidSkip(self, skip):
</del><ins>+    def invalidSkip(self, skip, monthly=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         If this is an invalid value skip backward or forward or not at all.
</span><span class="cx"> 
</span><span class="lines">@@ -1169,10 +1171,10 @@
</span><span class="cx">         @type skip: L{int}
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        if self.invalid():
-            if skip == definitions.eRecurrence_SKIP_YES:
</del><ins>+        if self.invalid() and not monthly:
+            if skip == definitions.eRecurrence_SKIP_OMIT:
</ins><span class="cx">                 # Leave it as invalid
</span><del>-                pass
</del><ins>+                return False
</ins><span class="cx">             elif skip == definitions.eRecurrence_SKIP_BACKWARD:
</span><span class="cx">                 if self.mDay &lt;= 0:
</span><span class="cx">                     self.mDay = 1
</span><span class="lines">@@ -1185,6 +1187,7 @@
</span><span class="cx">                 else:
</span><span class="cx">                     self.mDay = utils.daysInMonth(self.mMonth, self.mYear)
</span><span class="cx">                     self.offsetDay(1)
</span><ins>+        return True
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def normalise(self):
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendarexceptionspy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/exceptions.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -16,9 +16,10 @@
</span><span class="cx"> 
</span><span class="cx"> class ErrorBase(Exception):
</span><span class="cx"> 
</span><del>-    def __init__(self, reason, data=&quot;&quot;):
</del><ins>+    def __init__(self, reason, data=&quot;&quot;, cause=None):
</ins><span class="cx">         self.mReason = reason
</span><span class="cx">         self.mData = data
</span><ins>+        self.mCause = cause
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendardefinitionspy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/definitions.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -277,11 +277,11 @@
</span><span class="cx"> cICalValue_RECUR_WEEKDAY_FR = &quot;FR&quot;
</span><span class="cx"> cICalValue_RECUR_WEEKDAY_SA = &quot;SA&quot;
</span><span class="cx"> 
</span><del>-eRecurrence_SKIP_YES = 0
</del><ins>+eRecurrence_SKIP_OMIT = 0
</ins><span class="cx"> eRecurrence_SKIP_BACKWARD = 1
</span><span class="cx"> eRecurrence_SKIP_FORWARD = 2
</span><span class="cx"> 
</span><del>-cICalValue_RECUR_SKIP_YES = &quot;YES&quot;
</del><ins>+cICalValue_RECUR_SKIP_OMIT = &quot;OMIT&quot;
</ins><span class="cx"> cICalValue_RECUR_SKIP_BACKWARD = &quot;BACKWARD&quot;
</span><span class="cx"> cICalValue_RECUR_SKIP_FORWARD = &quot;FORWARD&quot;
</span><span class="cx"> 
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendarexceptionspy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/exceptions.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -18,3 +18,13 @@
</span><span class="cx"> 
</span><span class="cx"> class TooManyInstancesError(ErrorBase):
</span><span class="cx">     pass
</span><ins>+
+
+
+class InvalidRscaleError(ErrorBase):
+    pass
+
+
+
+class RscaleNotAllowedError(ErrorBase):
+    pass
</ins></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendaricudatetimepy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/icudatetime.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -15,7 +15,8 @@
</span><span class="cx"> ##
</span><span class="cx"> 
</span><span class="cx"> from cffi import FFI
</span><del>-from __builtin__ import classmethod
</del><ins>+from collections import namedtuple
+
</ins><span class="cx"> from pycalendar.datetime import DateTime
</span><span class="cx"> from pycalendar.icalendar import definitions
</span><span class="cx"> 
</span><span class="lines">@@ -142,6 +143,8 @@
</span><span class="cx">          int32_t              amount,
</span><span class="cx">          UErrorCode*          status);
</span><span class="cx"> 
</span><ins>+    void ucal_clear(UCalendar*  cal);
+
</ins><span class="cx">     enum UCalendarLimitType {
</span><span class="cx">       /** Minimum value */
</span><span class="cx">       UCAL_MINIMUM,
</span><span class="lines">@@ -178,14 +181,34 @@
</span><span class="cx">     RSCALE_GREGORIAN = &quot;gregorian&quot;
</span><span class="cx">     RSCALE_HEBREW = &quot;hebrew&quot;
</span><span class="cx"> 
</span><del>-    RSCALE_CALCODE = {
</del><ins>+    # 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 = {
</ins><span class="cx">         &quot;gregorian&quot;: &quot;&quot;,
</span><del>-        &quot;chinese&quot;: &quot;C&quot;,
</del><ins>+        &quot;japanese&quot;: &quot;J&quot;,
+        &quot;buddhist&quot;: &quot;BT&quot;,
+        &quot;roc&quot;: &quot;RC&quot;,
+        &quot;persian&quot;: &quot;P&quot;,
</ins><span class="cx">         &quot;islamic-civil&quot;: &quot;I&quot;,
</span><ins>+        &quot;islamic&quot;: &quot;I2&quot;,
</ins><span class="cx">         &quot;hebrew&quot;: &quot;H&quot;,
</span><ins>+        &quot;chinese&quot;: &quot;C&quot;,
+        &quot;indian&quot;: &quot;IN&quot;,
+        &quot;coptic&quot;: &quot;CT&quot;,
</ins><span class="cx">         &quot;ethiopic&quot;: &quot;E&quot;,
</span><ins>+        &quot;ethiopic-amete-alem&quot;: &quot;E2&quot;,
+        &quot;iso8601&quot;: &quot;I8&quot;,
+        &quot;dangi&quot;: &quot;D&quot;,
+        &quot;islamic-umalqura&quot;: &quot;I3&quot;,
+        &quot;islamic-tbla&quot;: &quot;I4&quot;,
+        &quot;islamic-rgsa&quot;: &quot;I5&quot;,
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    # Used to cache day of month, month, and year day limits for each calscale
+    RSCALE_LIMITS = {}
+
</ins><span class="cx">     def __init__(self, rscale, ucal):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Initialize using an ICU C{ucal} object and the name of the calendar scale.
</span><span class="lines">@@ -214,6 +237,9 @@
</span><span class="cx">     def __del__(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Always close the ICU C{ucal} object.
</span><ins>+
+        FIXME: for PyPy we need to explicitly close all L{ICUDateTime} objects since garbage
+        collection is lazy.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         ICU.ucal_close(self.ucal)
</span><span class="cx">         self.ucal = None
</span><span class="lines">@@ -241,6 +267,10 @@
</span><span class="cx">         return hash(self.getPosixTime())
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def __eq__(self, comp):
+        return self.getPosixTime() == comp.getPosixTime()
+
+
</ins><span class="cx">     @classmethod
</span><span class="cx">     def fromDateTime(cls, dt, rscale):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -277,9 +307,70 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classmethod
</span><ins>+    def allRSCALEs(cls):
+        &quot;&quot;&quot;
+        Return the L{list} of known RSCALEs.
+
+        @return: all valid RSCALEs
+        @rtype: L{list}
+        &quot;&quot;&quot;
+
+        return cls.VALID_RSCALES.keys()
+
+
+    @classmethod
+    def validRSCALE(cls, rscale):
+        &quot;&quot;&quot;
+        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}
+        &quot;&quot;&quot;
+
+        return rscale.lower() in cls.VALID_RSCALES
+
+
+    @classmethod
+    def limitsRSCALE(cls, rscale):
+        &quot;&quot;&quot;
+        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 &quot;monthday&quot;, &quot;month&quot; and &quot;yearday&quot;
+        @rtype: L{dict}
+        &quot;&quot;&quot;
+
+        if rscale not in cls.RSCALE_LIMITS:
+            if rscale is None:
+                # Gregorian limits
+                result = {&quot;monthday&quot;: 31, &quot;month&quot;: 12, &quot;weekno&quot;: 53, &quot;yearday&quot;: 366}
+            else:
+                # Fetch limits from ICU
+                result = {}
+                ucal = cls._newUcal(rscale)
+                error = ffi.new(&quot;UErrorCode *&quot;, 0)
+                result[&quot;monthday&quot;] = ICU.ucal_getLimit(ucal, ICU.UCAL_DAY_OF_MONTH, ICU.UCAL_MAXIMUM, error)
+                result[&quot;month&quot;] = ICU.ucal_getLimit(ucal, ICU.UCAL_MONTH, ICU.UCAL_MAXIMUM, error) + 1
+                result[&quot;weekno&quot;] = ICU.ucal_getLimit(ucal, ICU.UCAL_WEEK_OF_YEAR, ICU.UCAL_MAXIMUM, error)
+                result[&quot;yearday&quot;] = 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
</ins><span class="cx">     def _newUcal(cls, rscale):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Create an ICU C{ucal} object for the specified calendar scale.
</del><ins>+        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.
</ins><span class="cx"> 
</span><span class="cx">         @param rscale: calendar scale to use
</span><span class="cx">         @type rscale: L{str}
</span><span class="lines">@@ -292,6 +383,7 @@
</span><span class="cx">         ucal = ICU.ucal_open(ffi.NULL, -1, ffi.new(&quot;char[]&quot;, calsystem), ICU.UCAL_DEFAULT, error)
</span><span class="cx">         if error[0] != ICU.U_ZERO_ERROR:
</span><span class="cx">             raise ValueError(&quot;Unable to create ICU calendar for rscale '{}', code: {}&quot;.format(rscale, error))
</span><ins>+        ICU.ucal_clear(ucal)
</ins><span class="cx">         return ucal
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -330,6 +422,22 @@
</span><span class="cx">         return ICUDateTime(rscale, ucal)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    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,
+    }
+
</ins><span class="cx">     @classmethod
</span><span class="cx">     def _numericMonthToICU(cls, month):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -341,23 +449,25 @@
</span><span class="cx">         @return: the ICU constant
</span><span class="cx">         @rtype: L{ICU.UCalendarMonths}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        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]
</del><ins>+        return cls.MONTH2ICU[month]
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    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,
+    }
+
</ins><span class="cx">     @classmethod
</span><span class="cx">     def _icuToNumericMonth(cls, month):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -369,23 +479,25 @@
</span><span class="cx">         @return: the month
</span><span class="cx">         @rtype: L{int}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        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]
</del><ins>+        return cls.ICU2MONTH[month]
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    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),
+    }
+
</ins><span class="cx">     @classmethod
</span><span class="cx">     def _adjustToICULeapMonth(cls, rscale, month, isleapmonth):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -406,18 +518,31 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         if rscale.lower() == cls.RSCALE_HEBREW:
</span><del>-            if month == 5 and isleapmonth:
-                month = 6
-                isleapmonth = None
-            elif month &gt;= 6:
-                month += 1
-        return (month, isleapmonth,)
</del><ins>+            return cls.TOICU_HEBREW_MONTH[(month, isleapmonth,)]
+        else:
+            return (month, isleapmonth,)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    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),
+    }
+
</ins><span class="cx">     @classmethod
</span><span class="cx">     def _adjustFromICULeapMonth(cls, rscale, month, isleapmonth):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        For the Hebrew calendar, ISU uses a count of 13 months rather than 12 months
</del><ins>+        For the Hebrew calendar, ICU uses a count of 13 months rather than 12 months
</ins><span class="cx">         plus an &quot;isleapmonth&quot; indicator. So when converting to/from ICU we need to make
</span><span class="cx">         that adjustment as we always use 12 months + isleapmonth. This method converts
</span><span class="cx">         to our internal representation from what ICU uses.
</span><span class="lines">@@ -434,12 +559,9 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         if rscale.lower() == cls.RSCALE_HEBREW:
</span><del>-            isleapmonth = False
-            if month == 6:
-                isleapmonth = True
-            elif month &gt;= 6:
-                month -= 1
-        return (month, isleapmonth,)
</del><ins>+            return cls.FROMICU_HEBREW_MONTH[(month, isleapmonth,)]
+        else:
+            return (month, isleapmonth,)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @classmethod
</span><span class="lines">@@ -492,12 +614,13 @@
</span><span class="cx">         @return: the date components
</span><span class="cx">         @rtype: L{tuple} of (L{int}, L{int}, L{int}, L{bool})
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        year = self.getYear()
-        month = self.getMonth()
-        day = self.getDay()
-        isleapmonth = self.getLeapMonth()
</del><span class="cx"> 
</span><ins>+        error = ffi.new(&quot;UErrorCode *&quot;, 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
</ins><span class="cx">         month, isleapmonth = self._adjustFromICULeapMonth(self.rscale, month, isleapmonth)
</span><ins>+        day = ICU.ucal_get(self.ucal, ICU.UCAL_DAY_OF_MONTH, error)
</ins><span class="cx"> 
</span><span class="cx">         return (year, month, day, isleapmonth,)
</span><span class="cx"> 
</span><span class="lines">@@ -525,7 +648,6 @@
</span><span class="cx">         self.setYear(year)
</span><span class="cx">         self.setMonth(month, isleapmonth)
</span><span class="cx">         self.setDay(day)
</span><del>-
</del><span class="cx">         self.testInvalid(year, month, day, isleapmonth)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -553,13 +675,17 @@
</span><span class="cx"> 
</span><span class="cx">     def getMonth(self):
</span><span class="cx">         error = ffi.new(&quot;UErrorCode *&quot;, 0)
</span><del>-        return self._icuToNumericMonth(ICU.ucal_get(self.ucal, ICU.UCAL_MONTH, error))
</del><ins>+        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
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def setMonth(self, month, isleapmonth=False):
</span><ins>+        adjusted_month, adjusted_isleapmonth = self._adjustToICULeapMonth(self.rscale, month, isleapmonth)
</ins><span class="cx">         old_year, _ignore_old_month, old_day, _ignore_old_isleapmonth = self.getDateComponents()
</span><del>-        ICU.ucal_set(self.ucal, ICU.UCAL_MONTH, self._numericMonthToICU(month))
-        ICU.ucal_set(self.ucal, ICU.UCAL_IS_LEAP_MONTH, isleapmonth)
</del><ins>+        ICU.ucal_set(self.ucal, ICU.UCAL_MONTH, self._numericMonthToICU(adjusted_month))
+        ICU.ucal_set(self.ucal, ICU.UCAL_IS_LEAP_MONTH, adjusted_isleapmonth)
</ins><span class="cx">         self.testInvalid(old_year, month, old_day, isleapmonth)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -576,7 +702,10 @@
</span><span class="cx"> 
</span><span class="cx">     def getLeapMonth(self):
</span><span class="cx">         error = ffi.new(&quot;UErrorCode *&quot;, 0)
</span><del>-        return ICU.ucal_get(self.ucal, ICU.UCAL_IS_LEAP_MONTH, error) != 0
</del><ins>+        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
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getDay(self):
</span><span class="lines">@@ -861,10 +990,11 @@
</span><span class="cx"> 
</span><span class="cx">     # When doing recurrence iteration we sometimes need to preserve an invalid value for
</span><span class="cx">     # either day or month (though month is never invalid for Gregorian calendars it can
</span><del>-    # be for non-Gregorian). For this class we simply set the stored attributes to their
-    # invalid values.
</del><ins>+    # be for non-Gregorian).
+    YYMMDDLL = namedtuple(&quot;YYMMDDLL&quot;, (&quot;year&quot;, &quot;month&quot;, &quot;day&quot;, &quot;isleapmonth&quot;))
+
</ins><span class="cx">     def setInvalid(self, year, month, day, isleapmonth=False):
</span><del>-        self.mInvalid = (year, month, day, isleapmonth,)
</del><ins>+        self.mInvalid = self.YYMMDDLL(year, month, day, isleapmonth,)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def testInvalid(self, year, month, day, isleapmonth=False):
</span><span class="lines">@@ -894,39 +1024,67 @@
</span><span class="cx">         return self.mInvalid is not None
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def invalidSkip(self, skip):
</del><ins>+    def invalidSkip(self, skip, monthly=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        If this is an invalid value skip backward or forward or not at all.
</del><ins>+        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}.
</ins><span class="cx"> 
</span><del>-        @param skip: the skip mode (yes, backward, forward)
</del><ins>+        Note that for a monthly skip, the resulting L{ICUDateTime} may have an invalid day component.
+
+        @param skip: the skip mode (omit, backward, forward)
</ins><span class="cx">         @type skip: L{int}
</span><ins>+        @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}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-        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)
</del><ins>+        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()
</ins><span class="cx"> 
</span><del>-                elif d != self.mInvalid[2]:
</del><ins>+            # 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:
+
</ins><span class="cx">                     if skip == definitions.eRecurrence_SKIP_BACKWARD:
</span><del>-                        if self.mInvalid[2] &lt; 1:
</del><ins>+                        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 &gt; 0:
+                                self.offsetMonth(1)
</ins><span class="cx">                             self.offsetDay(-1)
</span><ins>+                            self.clearInvalid()
+
</ins><span class="cx">                     elif skip == definitions.eRecurrence_SKIP_FORWARD:
</span><del>-                        if self.mInvalid[2] &gt; 0:
-                            self.offsetDay(1)
</del><ins>+                        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 &gt; 0:
+                                self.offsetMonth(1)
+                            self.clearInvalid()
</ins><span class="cx"> 
</span><del>-                self.clearInvalid()
</del><ins>+        return True
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def normalise(self):
</span><span class="lines">@@ -971,7 +1129,7 @@
</span><span class="cx">         @return: the ISO-8601 text
</span><span class="cx">         @rtype L{str}
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        calcode = self.RSCALE_CALCODE.get(self.rscale.lower(), &quot;{}:&quot;.format(self.rscale))
</del><ins>+        calcode = self.VALID_RSCALES.get(self.rscale.lower(), &quot;{}:&quot;.format(self.rscale))
</ins><span class="cx">         if calcode:
</span><span class="cx">             calcode = &quot;{{{}}}&quot;.format(calcode)
</span><span class="cx">         year, month, day, isleapmonth = self.getDateComponents()
</span><span class="lines">@@ -982,14 +1140,14 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> if __name__ == '__main__':
</span><del>-    newyear = ICUDateTime.fromDateComponents(&quot;chinese&quot;, 4651, 1, 1, False)
</del><ins>+    newyear = ICUDateTime.fromDateComponents(&quot;chinese&quot;, 4653, 1, 30, False)
</ins><span class="cx">     print(&quot;From: {} to {}&quot;.format(
</span><span class="cx">         newyear.getText(),
</span><span class="cx">         newyear.convertTo(&quot;gregorian&quot;).getText(),
</span><span class="cx">     ))
</span><span class="cx"> 
</span><del>-    for i in range(0):
-        newyear.offsetDay(1)
</del><ins>+    for i in range(3):
+        newyear.offsetYear(1)
</ins><span class="cx">         print(&quot;From: {} to {}&quot;.format(
</span><span class="cx">             newyear.getText(),
</span><span class="cx">             newyear.convertTo(&quot;gregorian&quot;).getText(),
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendarrecuriterpy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/recuriter.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -23,7 +23,7 @@
</span><span class="cx">     An iterator that iterates a simple recurrence pattern.
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def __init__(self, start, freq, interval, rscale=None, skip=definitions.eRecurrence_SKIP_YES, allow_invalid=False):
</del><ins>+    def __init__(self, start, freq, interval, rscale=None, skip=definitions.eRecurrence_SKIP_OMIT, allow_invalid=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @param start: the start date-time
</span><span class="cx">         @type start: L{DateTime} or L{ICUDateTime}
</span><span class="lines">@@ -105,18 +105,16 @@
</span><span class="cx">             if dt.getDay() != self.start.getDay():
</span><span class="cx">                 if self.allow_invalid:
</span><span class="cx">                     dt.setInvalid(dt.getYear(), dt.getMonth(), self.start.getDay(), dt.getLeapMonth())
</span><del>-                elif self.skip == definitions.eRecurrence_SKIP_YES:
</del><ins>+                elif self.skip == definitions.eRecurrence_SKIP_OMIT:
</ins><span class="cx">                     # Iterate until we have a valid month
</span><span class="cx">                     while dt.getDay() != self.start.getDay():
</span><span class="cx">                         self.step += self.interval
</span><span class="cx">                         dt = self.start.duplicate()
</span><span class="cx">                         dt.offsetMonth(self.step)
</span><del>-                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)
</del><ins>+                else:
+                    dt.setInvalid(dt.getYear(), dt.getMonth(), self.start.getDay(), dt.getLeapMonth())
+                    dt.invalidSkip(self.skip, True)
+                    dt.invalidSkip(self.skip, False)
</ins><span class="cx"> 
</span><span class="cx">         elif self.freq == definitions.eRecurrence_YEARLY:
</span><span class="cx">             dt.offsetYear(self.step)
</span><span class="lines">@@ -126,18 +124,16 @@
</span><span class="cx">             if dt.getDay() != self.start.getDay() or dt.getMonth() != self.start.getMonth() or dt.getLeapMonth() != self.start.getLeapMonth():
</span><span class="cx">                 if self.allow_invalid:
</span><span class="cx">                     dt.setInvalid(dt.getYear(), self.start.getMonth(), self.start.getDay(), self.start.getLeapMonth())
</span><del>-                elif self.skip == definitions.eRecurrence_SKIP_YES:
</del><ins>+                elif self.skip == definitions.eRecurrence_SKIP_OMIT:
</ins><span class="cx">                     # Iterate until we have a valid date-time
</span><span class="cx">                     while dt.getDay() != self.start.getDay() or dt.getMonth() != self.start.getMonth() or dt.getLeapMonth() != self.start.getLeapMonth():
</span><span class="cx">                         self.step += self.interval
</span><span class="cx">                         dt = self.start.duplicate()
</span><span class="cx">                         dt.offsetYear(self.step)
</span><del>-                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)
</del><ins>+                else:
+                    dt.setInvalid(dt.getYear(), self.start.getMonth(), self.start.getDay(), self.start.getLeapMonth())
+                    dt.invalidSkip(self.skip, True)
+                    dt.invalidSkip(self.skip, False)
</ins><span class="cx"> 
</span><span class="cx">         self.step += self.interval
</span><span class="cx"> 
</span><span class="lines">@@ -145,7 +141,9 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> if __name__ == '__main__':
</span><del>-    icudt = ICUDateTime.fromDateComponents(&quot;gregorian&quot;, 2014, 1, 31)
-    iter = RecurrenceIterator(icudt, definitions.eRecurrence_MONTHLY, 1, definitions.eRecurrence_SKIP_BACKWARD)
-    for i in range(12):
-        print(iter.next().getText())
</del><ins>+    icudt = ICUDateTime.fromDateComponents(&quot;gregorian&quot;, 2013, 1, 1)
+    iter = RecurrenceIterator(icudt, definitions.eRecurrence_DAILY, 1)
+    for i in range(1200):
+        gregorian = iter.next()
+        nongregorian = gregorian.convertTo(&quot;hebrew&quot;)
+        print(&quot;{}\t{}&quot;.format(gregorian.getText(), nongregorian.getText()))
</ins></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendarrecurrencepy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/recurrence.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -17,12 +17,14 @@
</span><span class="cx"> from pycalendar import xmlutils
</span><span class="cx"> from pycalendar.datetime import DateTime
</span><span class="cx"> from pycalendar.icalendar import definitions, xmldefinitions
</span><del>-from pycalendar.icalendar.exceptions import TooManyInstancesError
</del><ins>+from pycalendar.icalendar.exceptions import TooManyInstancesError, InvalidRscaleError
+from pycalendar.icalendar.icudatetime import ICUDateTime
+from pycalendar.icalendar.recuriter import RecurrenceIterator
</ins><span class="cx"> from pycalendar.period import Period
</span><span class="cx"> from pycalendar.valueutils import ValueMixin
</span><ins>+
</ins><span class="cx"> import cStringIO as StringIO
</span><span class="cx"> import xml.etree.cElementTree as XML
</span><del>-from pycalendar.icalendar.recuriter import RecurrenceIterator
</del><span class="cx"> 
</span><span class="cx"> def WeekDayNumCompare_compare(w1, w2):
</span><span class="cx"> 
</span><span class="lines">@@ -101,7 +103,7 @@
</span><span class="cx">     cWeekdayRecurMap = dict([(v, k) for k, v in cWeekdayMap.items()])
</span><span class="cx"> 
</span><span class="cx">     cSkipMap = {
</span><del>-        definitions.cICalValue_RECUR_SKIP_YES       : definitions.eRecurrence_SKIP_YES,
</del><ins>+        definitions.cICalValue_RECUR_SKIP_OMIT       : definitions.eRecurrence_SKIP_OMIT,
</ins><span class="cx">         definitions.cICalValue_RECUR_SKIP_BACKWARD  : definitions.eRecurrence_SKIP_BACKWARD,
</span><span class="cx">         definitions.cICalValue_RECUR_SKIP_FORWARD   : definitions.eRecurrence_SKIP_FORWARD,
</span><span class="cx">     }
</span><span class="lines">@@ -109,7 +111,7 @@
</span><span class="cx">     cSkipInverseMap = dict([(v, k) for k, v in cSkipMap.items()])
</span><span class="cx"> 
</span><span class="cx">     cSkipToXMLMap = {
</span><del>-        definitions.eRecurrence_SKIP_YES: xmldefinitions.recur_skip_yes,
</del><ins>+        definitions.eRecurrence_SKIP_OMIT: xmldefinitions.recur_skip_omit,
</ins><span class="cx">         definitions.eRecurrence_SKIP_BACKWARD: xmldefinitions.recur_skip_backward,
</span><span class="cx">         definitions.eRecurrence_SKIP_FORWARD: xmldefinitions.recur_skip_forward,
</span><span class="cx">     }
</span><span class="lines">@@ -312,6 +314,8 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def setRscale(self, rscale):
</span><ins>+        if not ICUDateTime.validRSCALE(rscale):
+            raise InvalidRscaleError(rscale)
</ins><span class="cx">         self._setAndclearIfChanged(&quot;mRscale&quot;, rscale)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -371,10 +375,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         The default skip value depends on whether RSCALE is used or not
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        if self.mSkip is None:
-            return definitions.eRecurrence_SKIP_YES if self.mRscale is None else definitions.eRecurrence_SKIP_BACKWARD
-        else:
-            return self.mSkip
</del><ins>+        return definitions.eRecurrence_SKIP_OMIT if self.mSkip is None else self.mSkip
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def setSkip(self, skip):
</span><span class="lines">@@ -517,25 +518,25 @@
</span><span class="cx">                 if self.mByMonthDay is not None:
</span><span class="cx">                     raise ValueError(&quot;Recurrence: Only one BYMONTHDAY allowed&quot;)
</span><span class="cx">                 self.mByMonthDay = []
</span><del>-                self.parseList(tvalue, self.mByMonthDay, 1, 31, True, errmsg=&quot;Recurrence: Invalid BYMONTHDAY value&quot;)
</del><ins>+                self.parseList(tvalue, self.mByMonthDay, 1, ICUDateTime.limitsRSCALE(self.mRscale)[&quot;monthday&quot;], True, errmsg=&quot;Recurrence: Invalid BYMONTHDAY value&quot;)
</ins><span class="cx"> 
</span><span class="cx">             elif index == definitions.eRecurrence_BYYEARDAY:
</span><span class="cx">                 if self.mByYearDay is not None:
</span><span class="cx">                     raise ValueError(&quot;Recurrence: Only one BYYEARDAY allowed&quot;)
</span><span class="cx">                 self.mByYearDay = []
</span><del>-                self.parseList(tvalue, self.mByYearDay, 1, 366, True, errmsg=&quot;Recurrence: Invalid BYYEARDAY value&quot;)
</del><ins>+                self.parseList(tvalue, self.mByYearDay, 1, ICUDateTime.limitsRSCALE(self.mRscale)[&quot;yearday&quot;], True, errmsg=&quot;Recurrence: Invalid BYYEARDAY value&quot;)
</ins><span class="cx"> 
</span><span class="cx">             elif index == definitions.eRecurrence_BYWEEKNO:
</span><span class="cx">                 if self.mByWeekNo is not None:
</span><span class="cx">                     raise ValueError(&quot;Recurrence: Only one BYWEEKNO allowed&quot;)
</span><span class="cx">                 self.mByWeekNo = []
</span><del>-                self.parseList(tvalue, self.mByWeekNo, 1, 53, True, errmsg=&quot;Recurrence: Invalid BYWEEKNO value&quot;)
</del><ins>+                self.parseList(tvalue, self.mByWeekNo, 1, ICUDateTime.limitsRSCALE(self.mRscale)[&quot;weekno&quot;], True, errmsg=&quot;Recurrence: Invalid BYWEEKNO value&quot;)
</ins><span class="cx"> 
</span><span class="cx">             elif index == definitions.eRecurrence_BYMONTH:
</span><span class="cx">                 if self.mByMonth is not None:
</span><span class="cx">                     raise ValueError(&quot;Recurrence: Only one BYMONTH allowed&quot;)
</span><span class="cx">                 self.mByMonth = []
</span><del>-                self.parseMonthNumList(tvalue, self.mByMonth, 1, 12, errmsg=&quot;Recurrence: Invalid BYMONTH value&quot;)
</del><ins>+                self.parseMonthNumList(tvalue, self.mByMonth, 1, ICUDateTime.limitsRSCALE(self.mRscale)[&quot;month&quot;], errmsg=&quot;Recurrence: Invalid BYMONTH value&quot;)
</ins><span class="cx"> 
</span><span class="cx">             elif index == definitions.eRecurrence_BYSETPOS:
</span><span class="cx">                 if self.mBySetPos is not None:
</span><span class="lines">@@ -551,6 +552,8 @@
</span><span class="cx"> 
</span><span class="cx">             elif index == definitions.eRecurrence_RSCALE:
</span><span class="cx">                 self.mRscale = tvalue.upper()
</span><ins>+                if not ICUDateTime.validRSCALE(self.mRscale):
+                    raise InvalidRscaleError(self.mRscale)
</ins><span class="cx"> 
</span><span class="cx">             elif index == definitions.eRecurrence_SKIP:
</span><span class="cx">                 # Get the SKIP value
</span><span class="lines">@@ -1074,25 +1077,25 @@
</span><span class="cx">         while True:
</span><span class="cx">             start_iter = riter.next()
</span><span class="cx"> 
</span><ins>+            # 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 &gt; float_until:
+                return True
+
</ins><span class="cx">             # Exit if after period we want
</span><span class="cx">             if range.isDateAfterPeriod(start_iter):
</span><span class="cx">                 return False
</span><del>-            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 &gt; float_until:
-                    return True
</del><span class="cx"> 
</span><span class="cx">             # Add current one to list
</span><span class="cx">             results.append(start_iter)
</span><ins>+
+            # Check maximum limit
</ins><span class="cx">             if maxInstances and len(results) &gt; maxInstances:
</span><span class="cx">                 raise TooManyInstancesError(&quot;Too many instances&quot;)
</span><span class="cx"> 
</span><del>-            # Check limits
-            if self.mUseCount:
-                # Exit if max count reached
-                if len(results) &gt;= self.mCount:
-                    return True
</del><ins>+            # Check count limit and exit if max count reached
+            if self.mUseCount and len(results) &gt;= self.mCount:
+                return True
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def complexExpand(self, start, range, results, float_offset, maxInstances=None):
</span><span class="lines">@@ -1135,17 +1138,9 @@
</span><span class="cx">             elif self.mFreq == definitions.eRecurrence_YEARLY:
</span><span class="cx">                 self.generateYearlySet(start_iter, set_items)
</span><span class="cx"> 
</span><del>-            # 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)
</del><ins>+            # 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())
</ins><span class="cx"> 
</span><del>-            # 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())
-
</del><span class="cx">             if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
</span><span class="cx">                 set_items[:] = self.bySetPosLimit(set_items)
</span><span class="cx"> 
</span><span class="lines">@@ -1164,27 +1159,25 @@
</span><span class="cx">                 if iter &lt; start:
</span><span class="cx">                     continue
</span><span class="cx"> 
</span><ins>+                # Exit if next item is after until (its OK if its the same
+                # as UNTIL as UNTIL is inclusive)
+                if self.mUseUntil and iter &gt; float_until:
+                    return True
+
</ins><span class="cx">                 # Exit if after period we want
</span><span class="cx">                 if range.isDateAfterPeriod(iter):
</span><span class="cx">                     return False
</span><span class="cx"> 
</span><del>-                # 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 &gt; float_until:
-                        return True
-
</del><span class="cx">                 # Add current one to list
</span><span class="cx">                 results.append(iter)
</span><ins>+
+                # Check maximum limit
</ins><span class="cx">                 if maxInstances and len(results) &gt; maxInstances:
</span><span class="cx">                     raise TooManyInstancesError(&quot;Too many instances&quot;)
</span><span class="cx"> 
</span><del>-                # Check limits
-                if self.mUseCount:
-                    # Exit if max count reached
-                    if len(results) &gt;= self.mCount:
-                        return True
</del><ins>+                # Check count limit and exit if max count reached
+                if self.mUseCount and len(results) &gt;= self.mCount:
+                    return True
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def clear(self):
</span><span class="lines">@@ -1225,6 +1218,10 @@
</span><span class="cx">         self.clear()
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def applySkip(self, items, monthly):
+        items[:] = filter(lambda x: x.invalidSkip(self.effectiveSkip(), monthly), items)
+
+
</ins><span class="cx">     def generateYearlySet(self, start, items):
</span><span class="cx">         # All possible BYxxx are valid, though some combinations are not
</span><span class="cx"> 
</span><span class="lines">@@ -1233,6 +1230,7 @@
</span><span class="cx"> 
</span><span class="cx">         if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
</span><span class="cx">             items[:] = self.byMonthExpand(items)
</span><ins>+        self.applySkip(items, True)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
</span><span class="cx">             items[:] = self.byWeekNoExpand(items)
</span><span class="lines">@@ -1259,6 +1257,7 @@
</span><span class="cx">                 items[:] = self.byDayExpandMonthly(items)
</span><span class="cx">             else:
</span><span class="cx">                 items[:] = self.byDayExpandYearly(items)
</span><ins>+        self.applySkip(items, False)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByHours is not None) and (len(self.mByHours) != 0):
</span><span class="cx">             items[:] = self.byHourExpand(items)
</span><span class="lines">@@ -1281,6 +1280,7 @@
</span><span class="cx">             items[:] = self.byMonthLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, True)
</ins><span class="cx"> 
</span><span class="cx">         # No BYWEEKNO
</span><span class="cx"> 
</span><span class="lines">@@ -1301,6 +1301,7 @@
</span><span class="cx">                 items[:] = self.byDayLimit(items)
</span><span class="cx">             else:
</span><span class="cx">                 items[:] = self.byDayExpandMonthly(items)
</span><ins>+        self.applySkip(items, False)
</ins><span class="cx"> 
</span><span class="cx">         if ((self.mByHours is not None) and (len(self.mByHours) != 0)):
</span><span class="cx">             items[:] = self.byHourExpand(items)
</span><span class="lines">@@ -1323,6 +1324,7 @@
</span><span class="cx">             items[:] = self.byMonthLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, True)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
</span><span class="cx">             items[:] = self.byWeekNoLimit(items)
</span><span class="lines">@@ -1335,6 +1337,7 @@
</span><span class="cx"> 
</span><span class="cx">         if (self.mByDay is not None) and (len(self.mByDay) != 0):
</span><span class="cx">             items[:] = self.byDayExpandWeekly(items)
</span><ins>+        self.applySkip(items, False)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByHours is not None) and (len(self.mByHours) != 0):
</span><span class="cx">             items[:] = self.byHourExpand(items)
</span><span class="lines">@@ -1357,6 +1360,7 @@
</span><span class="cx">             items[:] = self.byMonthLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, True)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
</span><span class="cx">             items[:] = self.byWeekNoLimit(items)
</span><span class="lines">@@ -1374,6 +1378,7 @@
</span><span class="cx">             items[:] = self.byDayLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, False)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByHours is not None) and (len(self.mByHours) != 0):
</span><span class="cx">             items[:] = self.byHourExpand(items)
</span><span class="lines">@@ -1396,6 +1401,7 @@
</span><span class="cx">             items[:] = self.byMonthLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, True)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
</span><span class="cx">             items[:] = self.byWeekNoLimit(items)
</span><span class="lines">@@ -1413,6 +1419,7 @@
</span><span class="cx">             items[:] = self.byDayLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, False)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByHours is not None) and (len(self.mByHours) != 0):
</span><span class="cx">             items[:] = self.byHourLimit(items)
</span><span class="lines">@@ -1437,6 +1444,7 @@
</span><span class="cx">             items[:] = self.byMonthLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, True)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
</span><span class="cx">             items[:] = self.byWeekNoLimit(items)
</span><span class="lines">@@ -1454,6 +1462,7 @@
</span><span class="cx">             items[:] = self.byDayLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, False)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByHours is not None) and (len(self.mByHours) != 0):
</span><span class="cx">             items[:] = self.byHourLimit(items)
</span><span class="lines">@@ -1480,6 +1489,7 @@
</span><span class="cx">             items[:] = self.byMonthLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, True)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
</span><span class="cx">             items[:] = self.byWeekNoLimit(items)
</span><span class="lines">@@ -1497,6 +1507,7 @@
</span><span class="cx">             items[:] = self.byDayLimit(items)
</span><span class="cx">             if (len(items) == 0):
</span><span class="cx">                 return
</span><ins>+        self.applySkip(items, False)
</ins><span class="cx"> 
</span><span class="cx">         if (self.mByHours is not None) and (len(self.mByHours) != 0):
</span><span class="cx">             items[:] = self.byHourLimit(items)
</span><span class="lines">@@ -1692,7 +1703,7 @@
</span><span class="cx"> 
</span><span class="cx">     def byMonthLimit(self, dates):
</span><span class="cx">         # Keep each date that matches a BYMONTH
</span><del>-        return filter(lambda date: date.getMonth() in self.mByMonth, dates)
</del><ins>+        return filter(lambda date: (date.getMonth(), date.getLeapMonth(),) in self.mByMonth, dates)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def byWeekNoLimit(self, dates):
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendartestsrscale_examplesjson"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/rscale_examples.json (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1,7 +1,10 @@
</span><span class="cx"> [
</span><span class="cx">         {
</span><del>-                &quot;title&quot;: &quot;MonthlyRscaleStartInLeapYearSkipYes - start: {C}46501230&quot;,
-                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;SKIP=YES&quot;],
</del><ins>+                &quot;title&quot;: &quot;MonthlyRscaleStartInLeapYearSkipOmit - start: {C}46501230&quot;,
+                &quot;rule&quot;: [
+                        &quot;RSCALE=CHINESE;FREQ=MONTHLY;SKIP=OMIT&quot;,
+            &quot;RSCALE=CHINESE;FREQ=MONTHLY&quot;
+                ],
</ins><span class="cx">                 &quot;start&quot;: &quot;20140130&quot;,
</span><span class="cx">                 &quot;end&quot;: &quot;20180101&quot;,
</span><span class="cx">                 &quot;results&quot;: [
</span><span class="lines">@@ -92,10 +95,7 @@
</span><span class="cx">         },
</span><span class="cx">         {
</span><span class="cx">                 &quot;title&quot;: &quot;MonthlyRscaleStartInLeapYearSkipBackwardDefault - start: {C}46501230&quot;,
</span><del>-                &quot;rule&quot;: [
-            &quot;RSCALE=CHINESE;FREQ=MONTHLY;SKIP=BACKWARD&quot;,
-            &quot;RSCALE=CHINESE;FREQ=MONTHLY&quot;
-                ],
</del><ins>+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;SKIP=BACKWARD&quot;],
</ins><span class="cx">                 &quot;start&quot;: &quot;20140130&quot;,
</span><span class="cx">                 &quot;end&quot;: &quot;20180101&quot;,
</span><span class="cx">                 &quot;results&quot;: [
</span><span class="lines">@@ -151,8 +151,11 @@
</span><span class="cx">                 ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><del>-                &quot;title&quot;: &quot;YearlyLeapDaySkipYes&quot;,
-                &quot;rule&quot;: [&quot;RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=YES;COUNT=5&quot;],
</del><ins>+                &quot;title&quot;: &quot;YearlyLeapDaySkipOmit&quot;,
+                &quot;rule&quot;: [
+                        &quot;RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=OMIT;COUNT=5&quot;,
+            &quot;RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=5&quot;
+                ],
</ins><span class="cx">                 &quot;start&quot;: &quot;20160229&quot;,
</span><span class="cx">                 &quot;end&quot;: &quot;21000101&quot;,
</span><span class="cx">                 &quot;results&quot;: [
</span><span class="lines">@@ -178,10 +181,7 @@
</span><span class="cx">         },
</span><span class="cx">         {
</span><span class="cx">                 &quot;title&quot;: &quot;YearlyLeapDaySkipBackwardDefault&quot;,
</span><del>-                &quot;rule&quot;: [
-            &quot;RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=BACKWARD;COUNT=5&quot;,
-            &quot;RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=5&quot;
-                ],
</del><ins>+                &quot;rule&quot;: [&quot;RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=BACKWARD;COUNT=5&quot;],
</ins><span class="cx">                 &quot;start&quot;: &quot;20160229&quot;,
</span><span class="cx">                 &quot;end&quot;: &quot;21000101&quot;,
</span><span class="cx">                 &quot;results&quot;: [
</span><span class="lines">@@ -193,8 +193,8 @@
</span><span class="cx">                 ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><del>-                &quot;title&quot;: &quot;ChineseMonthlyByMonthDay30SkipYes&quot;,
-                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=30;SKIP=YES&quot;],
</del><ins>+                &quot;title&quot;: &quot;ChineseMonthlyByMonthDay30SkipOmit&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=30;SKIP=OMIT&quot;],
</ins><span class="cx">                 &quot;start&quot;: &quot;20140130T120000&quot;,
</span><span class="cx">                 &quot;end&quot;: &quot;20150101T000000&quot;,
</span><span class="cx">                 &quot;results&quot;: [
</span><span class="lines">@@ -248,8 +248,8 @@
</span><span class="cx">                 ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><del>-                &quot;title&quot;: &quot;ChineseMonthlyByMonthDayMinus30SkipYes&quot;,
-                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=-30;SKIP=YES&quot;],
</del><ins>+                &quot;title&quot;: &quot;ChineseMonthlyByMonthDayMinus30SkipOmit&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=-30;SKIP=OMIT&quot;],
</ins><span class="cx">                 &quot;start&quot;: &quot;20140130T120000&quot;,
</span><span class="cx">                 &quot;end&quot;: &quot;20150101T000000&quot;,
</span><span class="cx">                 &quot;results&quot;: [
</span><span class="lines">@@ -300,5 +300,316 @@
</span><span class="cx">             &quot;20141122T120000&quot;,
</span><span class="cx">             &quot;20141222T120000&quot;
</span><span class="cx">                 ]
</span><ins>+        },
+        {
+                &quot;title&quot;: &quot;Ethiopic last day of year&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=ETHIOPIC;FREQ=YEARLY;BYMONTH=13;BYMONTHDAY=-1;COUNT=6&quot;],
+                &quot;start&quot;: &quot;20140910&quot;,
+                &quot;end&quot;: &quot;20200101&quot;,
+                &quot;results&quot;: [&quot;20140910&quot;,&quot;20150911&quot;,&quot;20160910&quot;,&quot;20170910&quot;,&quot;20180910&quot;,&quot;20190911&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese New Year&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=YEARLY;UNTIL=20180101&quot;],
+                &quot;start&quot;: &quot;20130210&quot;,
+                &quot;end&quot;: &quot;20180101&quot;,
+                &quot;results&quot;: [&quot;20130210&quot;,&quot;20140131&quot;,&quot;20150219&quot;,&quot;20160208&quot;,&quot;20170128&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese monthly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20140920&quot;,
+                &quot;end&quot;: &quot;20150101&quot;,
+                &quot;results&quot;: [&quot;20140920&quot;,&quot;20141020&quot;,&quot;20141119&quot;,&quot;20141218&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Islamic monthly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=ISLAMIC-CIVIL;FREQ=MONTHLY;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20150101&quot;,
+                &quot;results&quot;: [&quot;20131025&quot;,&quot;20131124&quot;,&quot;20131224&quot;,&quot;20140122&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Ramadan&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=ISLAMIC-CIVIL;FREQ=YEARLY;BYMONTH=9;COUNT=5&quot;],
+                &quot;start&quot;: &quot;20130709&quot;,
+                &quot;end&quot;: &quot;20180101&quot;,
+                &quot;results&quot;: [&quot;20130709&quot;,&quot;20140629&quot;,&quot;20150618&quot;,&quot;20160607&quot;,&quot;20170527&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Buddha birthday&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=DANGI;FREQ=DAILY;BYMONTHDAY=8;BYMONTH=4;UNTIL=20160101&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20160101&quot;,
+                &quot;results&quot;: [&quot;20140506&quot;,&quot;20150525&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;No instances in Chinese leap month&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9;COUNT=3&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20170101&quot;,
+                &quot;results&quot;: [&quot;20141003&quot;,&quot;20151022&quot;,&quot;20161010&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 9L leap month skip - daily&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2&quot;],
+                &quot;start&quot;: &quot;20141102&quot;,
+                &quot;end&quot;: &quot;21500101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;,&quot;21091102&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 9L leap month skip - monthly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2&quot;],
+                &quot;start&quot;: &quot;20141102&quot;,
+                &quot;end&quot;: &quot;21500101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;,&quot;21091102&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 9L leap month skip - yearly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2&quot;],
+                &quot;start&quot;: &quot;20141102&quot;,
+                &quot;end&quot;: &quot;21500101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;,&quot;21091102&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 4L leap month skip - daily&quot;,
+                &quot;rule&quot;: [&quot;FREQ=DAILY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;21000101&quot;,
+                &quot;results&quot;: [&quot;20200601&quot;,&quot;20580531&quot;,&quot;20690530&quot;,&quot;20770531&quot;,&quot;20880530&quot;,&quot;20960531&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 4L leap month skip - monthly&quot;,
+                &quot;rule&quot;: [&quot;FREQ=MONTHLY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;21000101&quot;,
+                &quot;results&quot;: [&quot;20200601&quot;,&quot;20580531&quot;,&quot;20690530&quot;,&quot;20770531&quot;,&quot;20880530&quot;,&quot;20960531&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 4L leap month skip - yearly&quot;,
+                &quot;rule&quot;: [&quot;FREQ=YEARLY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;21000101&quot;,
+                &quot;results&quot;: [&quot;20200601&quot;,&quot;20580531&quot;,&quot;20690530&quot;,&quot;20770531&quot;,&quot;20880530&quot;,&quot;20960531&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 9L leap month skip back - daily&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;SKIP=BACKWARD;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20170101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 9L leap month skip back - monthly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;SKIP=BACKWARD;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20170101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 9L leap month skip back - yearly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;SKIP=BACKWARD;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20170101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;,&quot;20151022&quot;,&quot;20161010&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese 9,9L leap month skip back - daily&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;SKIP=BACKWARD;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20170101&quot;,
+                &quot;results&quot;: [&quot;20141003&quot;,&quot;20141102&quot;,&quot;20151022&quot;,&quot;20161010&quot;]
+        },
+
+        {
+                &quot;title&quot;: &quot;Chinese 9,9L leap month skip back - monthly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;SKIP=BACKWARD;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20170101&quot;,
+                &quot;results&quot;: [&quot;20141003&quot;,&quot;20141102&quot;,&quot;20151022&quot;,&quot;20161010&quot;]
+        },
+
+        {
+                &quot;title&quot;: &quot;Chinese 9,9L leap month skip back - yearly&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=CHINESE;SKIP=BACKWARD;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20170101&quot;,
+                &quot;results&quot;: [&quot;20141003&quot;,&quot;20141102&quot;,&quot;20151022&quot;,&quot;20161010&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Hebrew yearly skip&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=HEBREW;FREQ=YEARLY;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20140205&quot;,
+                &quot;end&quot;: &quot;20230101&quot;,
+                &quot;results&quot;: [&quot;20140205&quot;,&quot;20160214&quot;,&quot;20190210&quot;,&quot;20220206&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Hebrew yearly skip forward&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=HEBREW;FREQ=YEARLY;SKIP=FORWARD;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20140205&quot;,
+                &quot;end&quot;: &quot;20180101&quot;,
+                &quot;results&quot;: [&quot;20140205&quot;,&quot;20150224&quot;,&quot;20160214&quot;,&quot;20170303&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Hebrew yearly 5L skip forward&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=HEBREW;FREQ=YEARLY;BYMONTH=5L;BYMONTHDAY=8;SKIP=FORWARD;COUNT=5&quot;],
+                &quot;start&quot;: &quot;20140208&quot;,
+                &quot;end&quot;: &quot;20190101&quot;,
+                &quot;results&quot;: [&quot;20140208&quot;,&quot;20150227&quot;,&quot;20160217&quot;,&quot;20170306&quot;,&quot;20180223&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Gregorian monthly skip back&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=GREGORIAN;FREQ=MONTHLY;SKIP=BACKWARD;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20140131&quot;,
+                &quot;end&quot;: &quot;20150101&quot;,
+                &quot;results&quot;: [&quot;20140131&quot;,&quot;20140228&quot;,&quot;20140331&quot;,&quot;20140430&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Gregorian monthly skip forward&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=GREGORIAN;FREQ=MONTHLY;SKIP=FORWARD;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20140131&quot;,
+                &quot;end&quot;: &quot;20150101&quot;,
+                &quot;results&quot;: [&quot;20140131&quot;,&quot;20140301&quot;,&quot;20140331&quot;,&quot;20140501&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Gregorian leap day skip forward&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=GREGORIAN;FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=28,29;SKIP=FORWARD;COUNT=5&quot;],
+                &quot;start&quot;: &quot;20150201&quot;,
+                &quot;end&quot;: &quot;20180101&quot;,
+                &quot;results&quot;: [&quot;20150228&quot;,&quot;20150301&quot;,&quot;20160228&quot;,&quot;20160229&quot;,&quot;20170228&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Gregorian monthly interval skip forward&quot;,
+                &quot;rule&quot;: [&quot;RSCALE=GREGORIAN;FREQ=MONTHLY;INTERVAL=3;SKIP=FORWARD;COUNT=4&quot;],
+                &quot;start&quot;: &quot;20140131&quot;,
+                &quot;end&quot;: &quot;20150101&quot;,
+                &quot;results&quot;: [&quot;20140131&quot;,&quot;20140501&quot;,&quot;20140731&quot;,&quot;20141031&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Gregorian month day skip forward&quot;,
+                &quot;rule&quot;: [&quot;FREQ=MONTHLY;RSCALE=GREGORIAN;SKIP=FORWARD;COUNT=12;BYMONTHDAY=31&quot;],
+                &quot;start&quot;: &quot;20150131&quot;,
+                &quot;end&quot;: &quot;20160101&quot;,
+                &quot;results&quot;: [&quot;20150131&quot;,&quot;20150301&quot;,&quot;20150331&quot;,&quot;20150501&quot;,&quot;20150531&quot;,&quot;20150701&quot;,&quot;20150731&quot;,&quot;20150831&quot;,&quot;20151001&quot;,&quot;20151031&quot;,&quot;20151201&quot;,&quot;20151231&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Gregorian last month day skip forward&quot;,
+                &quot;rule&quot;: [&quot;FREQ=MONTHLY;RSCALE=GREGORIAN;SKIP=FORWARD;COUNT=12;BYMONTHDAY=-1&quot;],
+                &quot;start&quot;: &quot;20150131&quot;,
+                &quot;end&quot;: &quot;20160101&quot;,
+                &quot;results&quot;: [&quot;20150131&quot;,&quot;20150228&quot;,&quot;20150331&quot;,&quot;20150430&quot;,&quot;20150531&quot;,&quot;20150630&quot;,&quot;20150731&quot;,&quot;20150831&quot;,&quot;20150930&quot;,&quot;20151031&quot;,&quot;20151130&quot;,&quot;20151231&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Hebrew leap month day skip forward&quot;,
+                &quot;rule&quot;: [&quot;FREQ=YEARLY;RSCALE=HEBREW;SKIP=FORWARD;COUNT=5;BYMONTHDAY=30;BYMONTH=5L&quot;],
+                &quot;start&quot;: &quot;20140302&quot;,
+                &quot;end&quot;: &quot;20190101&quot;,
+                &quot;results&quot;: [&quot;20140302&quot;,&quot;20150321&quot;,&quot;20160310&quot;,&quot;20170328&quot;,&quot;20180317&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Hebrew last leap month day skip forward&quot;,
+                &quot;rule&quot;: [&quot;FREQ=YEARLY;RSCALE=HEBREW;SKIP=FORWARD;COUNT=5;BYMONTHDAY=-1;BYMONTH=5L&quot;],
+                &quot;start&quot;: &quot;20140302&quot;,
+                &quot;end&quot;: &quot;20190101&quot;,
+                &quot;results&quot;: [&quot;20140302&quot;,&quot;20150320&quot;,&quot;20160310&quot;,&quot;20170327&quot;,&quot;20180316&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Hebrew leap month day skip back&quot;,
+                &quot;rule&quot;: [&quot;FREQ=YEARLY;RSCALE=HEBREW;SKIP=BACKWARD;COUNT=5;BYMONTHDAY=30;BYMONTH=5L&quot;],
+                &quot;start&quot;: &quot;20140302&quot;,
+                &quot;end&quot;: &quot;20190101&quot;,
+                &quot;results&quot;: [&quot;20140302&quot;,&quot;20150219&quot;,&quot;20160310&quot;,&quot;20170226&quot;,&quot;20180215&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Hebrew last leap month day skip forward&quot;,
+                &quot;rule&quot;: [&quot;FREQ=YEARLY;RSCALE=HEBREW;SKIP=BACKWARD;COUNT=5;BYMONTHDAY=-1;BYMONTH=5L&quot;],
+                &quot;start&quot;: &quot;20140302&quot;,
+                &quot;end&quot;: &quot;20190101&quot;,
+                &quot;results&quot;: [&quot;20140302&quot;,&quot;20150219&quot;,&quot;20160310&quot;,&quot;20170226&quot;,&quot;20180215&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese leap month day skip forward - daily&quot;,
+                &quot;rule&quot;: [&quot;FREQ=DAILY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20160101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese leap month day skip forward - monthly&quot;,
+                &quot;rule&quot;: [&quot;FREQ=MONTHLY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20160101&quot;,
+                &quot;results&quot;: [&quot;20141102&quot;]
+        },
+
+
+        {
+                &quot;title&quot;: &quot;Chinese leap month day skip forward - yearly&quot;,
+                &quot;rule&quot;: [&quot;FREQ=YEARLY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L&quot;],
+                &quot;start&quot;: &quot;20131025&quot;,
+                &quot;end&quot;: &quot;20160101&quot;,
+                &quot;results&quot;: [&quot;20131112&quot;, &quot;20141102&quot;,&quot;20151121&quot;]
</ins><span class="cx">         }
</span><span class="cx"> ]
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendarteststest_icudatetimepy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_icudatetime.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -17,6 +17,7 @@
</span><span class="cx"> 
</span><span class="cx"> import unittest
</span><span class="cx"> from pycalendar.datetime import DateTime
</span><ins>+from pycalendar.icalendar import definitions
</ins><span class="cx"> from pycalendar.icalendar.icudatetime import ICUDateTime
</span><span class="cx"> 
</span><span class="cx"> class TestICUDateTime(unittest.TestCase):
</span><span class="lines">@@ -24,6 +25,37 @@
</span><span class="cx">     Test L{ICUDateTime}
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><ins>+    def testValidRscale(self):
+
+        data_rscale = (
+            (&quot;gregorian&quot;, True,),
+            (&quot;chinese&quot;, True,),
+            (&quot;foo&quot;, False,),
+            (&quot;gregorian&quot;, True,),
+            (&quot;chinese&quot;, True,),
+            (&quot;foo&quot;, False,),
+        )
+
+        for rscale, result in data_rscale:
+            valid = ICUDateTime.validRSCALE(rscale)
+            self.assertEqual(valid, result, &quot;Failed on: %s&quot; % (rscale,))
+
+
+    def testLimitsRscale(self):
+
+        data_rscale = (
+            (None, {&quot;monthday&quot;: 31, &quot;month&quot;: 12, &quot;weekno&quot;: 53, &quot;yearday&quot;: 366},),
+            (&quot;gregorian&quot;, {&quot;monthday&quot;: 31, &quot;month&quot;: 12, &quot;weekno&quot;: 53, &quot;yearday&quot;: 366},),
+            (&quot;chinese&quot;, {&quot;monthday&quot;: 30, &quot;month&quot;: 12, &quot;weekno&quot;: 55, &quot;yearday&quot;: 385},),
+            (&quot;ethiopic&quot;, {&quot;monthday&quot;: 30, &quot;month&quot;: 13, &quot;weekno&quot;: 53, &quot;yearday&quot;: 366},),
+            (&quot;hebrew&quot;, {&quot;monthday&quot;: 30, &quot;month&quot;: 13, &quot;weekno&quot;: 56, &quot;yearday&quot;: 385},),
+        )
+
+        for rscale, result in data_rscale:
+            limit = ICUDateTime.limitsRSCALE(rscale)
+            self.assertEqual(limit, result, &quot;Failed on: %s: %s&quot; % (rscale, limit))
+
+
</ins><span class="cx">     def testRoundtripDateText(self):
</span><span class="cx"> 
</span><span class="cx">         data_date = (
</span><span class="lines">@@ -91,17 +123,23 @@
</span><span class="cx">     def testSetMonth(self):
</span><span class="cx"> 
</span><span class="cx">         data_date = (
</span><del>-            (&quot;gregorian&quot;, 2012, 1, 2, False, 2, &quot;20120202&quot;, False,),
-            (&quot;gregorian&quot;, 2012, 1, 29, False, 2, &quot;20120229&quot;, False,),
-            (&quot;gregorian&quot;, 2012, 1, 31, False, 2, &quot;20120302&quot;, True,),
-            (&quot;gregorian&quot;, 2012, 2, 29, False, 3, &quot;20120329&quot;, False,),
</del><ins>+            (&quot;gregorian&quot;, 2012, 1, 2, False, 2, False, &quot;20120202&quot;, False,),
+            (&quot;gregorian&quot;, 2012, 1, 29, False, 2, False, &quot;20120229&quot;, False,),
+            (&quot;gregorian&quot;, 2012, 1, 31, False, 2, False, &quot;20120302&quot;, True,),
+            (&quot;gregorian&quot;, 2012, 2, 29, False, 3, False, &quot;20120329&quot;, False,),
+            (&quot;chinese&quot;, 4651, 1, 1, False, 9, False, &quot;{C}46510901&quot;, False,),
+            (&quot;chinese&quot;, 4651, 1, 1, False, 9, True, &quot;{C}465109L01&quot;, False,),
+            (&quot;chinese&quot;, 4651, 1, 1, False, 10, False, &quot;{C}46511001&quot;, False,),
+            (&quot;hebrew&quot;, 5774, 1, 1, False, 5, False, &quot;{H}57740501&quot;, False,),
+            (&quot;hebrew&quot;, 5774, 1, 1, False, 5, True, &quot;{H}577405L01&quot;, False,),
+            (&quot;hebrew&quot;, 5774, 1, 1, False, 6, False, &quot;{H}57740601&quot;, False,),
</ins><span class="cx">         )
</span><span class="cx"> 
</span><del>-        for rscale, y, m, d, l, month, result, result_invalid in data_date:
</del><ins>+        for rscale, y, m, d, l, month, leapmonth, result, result_invalid in data_date:
</ins><span class="cx">             dt = ICUDateTime.fromDateComponents(rscale, y, m, d, l)
</span><del>-            dt.setMonth(month)
-            self.assertEqual(dt.getText(), result, &quot;Failed on: {} {} {}&quot;.format(month, result, result_invalid,))
-            self.assertEqual(dt.invalid(), result_invalid, &quot;Failed invalid on: {} {} {}&quot;.format(month, result, result_invalid,))
</del><ins>+            dt.setMonth(month, leapmonth)
+            self.assertEqual(dt.getText(), result, &quot;Failed on: {} {} {} {}&quot;.format(month, leapmonth, result, result_invalid,))
+            self.assertEqual(dt.invalid(), result_invalid, &quot;Failed invalid on: {} {} {} {}&quot;.format(month, leapmonth, result, result_invalid,))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def testOffsetMonth(self):
</span><span class="lines">@@ -223,3 +261,40 @@
</span><span class="cx">             dt = ICUDateTime.fromDateComponents(rscale, y, m, d, l)
</span><span class="cx">             dt.setDayOfWeekInYear(offset, day)
</span><span class="cx">             self.assertEqual(dt.getText(), result, &quot;Failed on: {} vs {}&quot;.format(result, dt.getText(),))
</span><ins>+
+
+    def testSkip(self):
+
+        data_date = (
+            (&quot;gregorian&quot;, 2015, 2, 29, False, definitions.eRecurrence_SKIP_BACKWARD, False, &quot;20150228&quot;,),
+            (&quot;gregorian&quot;, 2015, 2, 29, False, definitions.eRecurrence_SKIP_FORWARD, False, &quot;20150301&quot;,),
+            (&quot;gregorian&quot;, 2015, 2, 29, False, definitions.eRecurrence_SKIP_OMIT, False, None,),
+            (&quot;gregorian&quot;, 2015, 2, 29, False, definitions.eRecurrence_SKIP_BACKWARD, True, None,),
+            (&quot;gregorian&quot;, 2015, 2, 29, False, definitions.eRecurrence_SKIP_FORWARD, True, None,),
+            (&quot;gregorian&quot;, 2015, 2, 29, False, definitions.eRecurrence_SKIP_OMIT, True, None,),
+            (&quot;chinese&quot;, 4652, 9, 2, True, definitions.eRecurrence_SKIP_BACKWARD, False, None,),
+            (&quot;chinese&quot;, 4652, 9, 2, True, definitions.eRecurrence_SKIP_FORWARD, False, None,),
+            (&quot;chinese&quot;, 4652, 9, 2, True, definitions.eRecurrence_SKIP_OMIT, False, None,),
+            (&quot;chinese&quot;, 4652, 9, 2, True, definitions.eRecurrence_SKIP_BACKWARD, True, &quot;{C}46520902&quot;,),
+            (&quot;chinese&quot;, 4652, 9, 2, True, definitions.eRecurrence_SKIP_FORWARD, True, &quot;{C}46521002&quot;,),
+            (&quot;chinese&quot;, 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=&quot;Failed: #{} {} vs {}&quot;.format(ctr, test_value, result))
+
+
+    def testDuplicates(self):
+
+        dt1 = ICUDateTime.fromDateComponents(&quot;gregorian&quot;, 2011, 1, 2, False)
+        dt2 = ICUDateTime.fromDateComponents(&quot;gregorian&quot;, 2011, 1, 2, False)
+        dt3 = ICUDateTime.fromDateComponents(&quot;gregorian&quot;, 2011, 1, 3, False)
+        self.assertEqual(len(set((dt1, dt2, dt3,))), 2)
</ins></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendarteststest_recuriterpy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recuriter.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -23,9 +23,9 @@
</span><span class="cx"> 
</span><span class="cx"> class MonthlySkips(object):
</span><span class="cx"> 
</span><del>-    def testMonthlySkipYes(self):
</del><ins>+    def testMonthlySkipOmit(self):
</ins><span class="cx"> 
</span><del>-        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_MONTHLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_YES)
</del><ins>+        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_MONTHLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_OMIT)
</ins><span class="cx">         results = [riter.next().getText() for _ in range(12)]
</span><span class="cx">         self.assertEqual(
</span><span class="cx">             results,
</span><span class="lines">@@ -136,9 +136,9 @@
</span><span class="cx"> 
</span><span class="cx"> class YearlySkipsOnLeapDay(object):
</span><span class="cx"> 
</span><del>-    def testYearlySkipYes(self):
</del><ins>+    def testYearlySkipOmit(self):
</ins><span class="cx"> 
</span><del>-        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_YEARLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_YES)
</del><ins>+        riter = RecurrenceIterator(self.dt, definitions.eRecurrence_YEARLY, 1, rscale=self.rscale, skip=definitions.eRecurrence_SKIP_OMIT)
</ins><span class="cx">         results = [riter.next().getText() for _ in range(5)]
</span><span class="cx">         self.assertEqual(
</span><span class="cx">             results,
</span><span class="lines">@@ -203,10 +203,10 @@
</span><span class="cx"> 
</span><span class="cx"> class TestMonthlyChineseICU(unittest.TestCase):
</span><span class="cx"> 
</span><del>-    def testMonthlyStartInLeapYearSkipYes(self):
</del><ins>+    def testMonthlyStartInLeapYearSkipOmit(self):
</ins><span class="cx">         dt = ICUDateTime.fromDateComponents(&quot;chinese&quot;, 4650, 12, 30)
</span><span class="cx"> 
</span><del>-        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale=None, skip=definitions.eRecurrence_SKIP_YES)
</del><ins>+        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale=None, skip=definitions.eRecurrence_SKIP_OMIT)
</ins><span class="cx">         results = []
</span><span class="cx">         while True:
</span><span class="cx">             result = riter.next()
</span><span class="lines">@@ -384,10 +384,10 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def testMonthlyRscaleStartInLeapYearSkipYes(self):
</del><ins>+    def testMonthlyRscaleStartInLeapYearSkipOmit(self):
</ins><span class="cx">         dt = ICUDateTime.fromDateComponents(&quot;chinese&quot;, 4650, 12, 30).toDateTime()
</span><span class="cx"> 
</span><del>-        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale=&quot;chinese&quot;, skip=definitions.eRecurrence_SKIP_YES)
</del><ins>+        riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale=&quot;chinese&quot;, skip=definitions.eRecurrence_SKIP_OMIT)
</ins><span class="cx">         results = []
</span><span class="cx">         while True:
</span><span class="cx">             result = riter.next()
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendarteststest_recurrencepy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/tests/test_recurrence.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -56,7 +56,7 @@
</span><span class="cx"> 
</span><span class="cx">         # RSCALE
</span><span class="cx">         &quot;RSCALE=CHINESE;FREQ=DAILY&quot;,
</span><del>-        &quot;RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=YES&quot;,
</del><ins>+        &quot;RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=OMIT&quot;,
</ins><span class="cx">         &quot;RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=BACKWARD&quot;,
</span><span class="cx">         &quot;RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=FORWARD&quot;,
</span><span class="cx">         &quot;RSCALE=CHINESE;FREQ=YEARLY;BYMONTH=5,6,6L,7&quot;,
</span><span class="lines">@@ -89,6 +89,7 @@
</span><span class="cx">             &quot;FREQ=MONTHLY;BYHOUR=54&quot;,
</span><span class="cx">             &quot;FREQ=MONTHLY;SKIP=YES&quot;,
</span><span class="cx">             &quot;RSCALE=CHINESE;FREQ=MONTHLY;SKIP=NO&quot;,
</span><ins>+            &quot;RSCALE=CHINESE;FREQ=MONTHLY;SKIP=YES&quot;,
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         for item in items:
</span><span class="lines">@@ -207,7 +208,7 @@
</span><span class="cx"> 
</span><span class="cx">             rules = [i[&quot;rule&quot;]]
</span><span class="cx">             if &quot;RSCALE&quot; not in rules[0]:
</span><del>-                rules.append(&quot;RSCALE=GREGORIAN;{};SKIP=YES&quot;.format(rules[0]))
</del><ins>+                rules.append(&quot;RSCALE=GREGORIAN;{};SKIP=OMIT&quot;.format(rules[0]))
</ins><span class="cx">             for rule in rules:
</span><span class="cx">                 recur = Recurrence()
</span><span class="cx">                 recur.parse(rule)
</span><span class="lines">@@ -221,10 +222,43 @@
</span><span class="cx">                 self.assertEqual(
</span><span class="cx">                     items,
</span><span class="cx">                     results,
</span><del>-                    msg=&quot;Failed rule: #{} {}&quot;.format(ctr + 1, rule)
</del><ins>+                    msg=&quot;Failed rule: #{ctr} {rule}: {items}&quot;.format(ctr=ctr + 1, rule=rule, items=items)
</ins><span class="cx">                 )
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def testLimitWithUntilAndCount(self):
+
+        recur = Recurrence()
+        recur.parse(&quot;FREQ=WEEKLY;UNTIL=20130110&quot;)
+
+        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(&quot;FREQ=WEEKLY;UNTIL=20130110&quot;)
+
+        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(&quot;FREQ=WEEKLY;UNTIL=20130110&quot;)
+
+        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)
+
+
</ins><span class="cx">     def testClearOnChange(self):
</span><span class="cx"> 
</span><span class="cx">         recur = Recurrence()
</span><span class="lines">@@ -257,7 +291,6 @@
</span><span class="cx">             examples = json.loads(f.read())
</span><span class="cx"> 
</span><span class="cx">         for ctr, i in enumerate(examples):
</span><del>-
</del><span class="cx">             for rule in i[&quot;rule&quot;]:
</span><span class="cx">                 recur = Recurrence()
</span><span class="cx">                 recur.parse(rule)
</span><span class="lines">@@ -271,5 +304,5 @@
</span><span class="cx">                 self.assertEqual(
</span><span class="cx">                     items,
</span><span class="cx">                     results,
</span><del>-                    msg=&quot;Failed rule: #{} {}&quot;.format(ctr + 1, rule)
</del><ins>+                    msg=&quot;Failed rule: #{ctr} {rule}: {items}&quot;.format(ctr=ctr + 1, rule=rule, items=items)
</ins><span class="cx">                 )
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendaricalendarxmldefinitionspy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/icalendar/xmldefinitions.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -38,7 +38,7 @@
</span><span class="cx"> recur_interval = &quot;interval&quot;
</span><span class="cx"> 
</span><span class="cx"> recur_skip = &quot;skip&quot;
</span><del>-recur_skip_yes = &quot;yes&quot;
</del><ins>+recur_skip_omit = &quot;omit&quot;
</ins><span class="cx"> recur_skip_backward = &quot;backward&quot;
</span><span class="cx"> recur_skip_forward = &quot;forward&quot;
</span><span class="cx"> 
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendarpropertypy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/property.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -301,8 +301,8 @@
</span><span class="cx"> 
</span><span class="cx">             return prop
</span><span class="cx"> 
</span><del>-        except Exception:
-            raise InvalidProperty(&quot;Invalid property&quot;, data)
</del><ins>+        except Exception as e:
+            raise InvalidProperty(&quot;Invalid property&quot;, data, cause=e)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def parseTextParameters(self, txt, data):
</span></span></pre></div>
<a id="PyCalendarbranchesrscalesrcpycalendarteststest_datetimepy"></a>
<div class="modfile"><h4>Modified: PyCalendar/branches/rscale/src/pycalendar/tests/test_datetime.py (15128 => 15129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -157,6 +157,74 @@
</span><span class="cx">             self.assertEqual(str(dt), result)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def testCacheChangeOnAdjustment(self):
+
+        # UTC first
+        dt = DateTime(2012, 1, 1, 12, 0, 0, Timezone(tzid=&quot;utc&quot;))
+        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)
+
+
</ins><span class="cx">     def testCachePreserveOnAdjustment(self):
</span><span class="cx"> 
</span><span class="cx">         # UTC first
</span></span></pre>
</div>
</div>

</body>
</html>