<!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 > 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 > 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"> """
</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"> """
</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 <= 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=""):
</del><ins>+ def __init__(self, reason, data="", 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 = "FR"
</span><span class="cx"> cICalValue_RECUR_WEEKDAY_SA = "SA"
</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 = "YES"
</del><ins>+cICalValue_RECUR_SKIP_OMIT = "OMIT"
</ins><span class="cx"> cICalValue_RECUR_SKIP_BACKWARD = "BACKWARD"
</span><span class="cx"> cICalValue_RECUR_SKIP_FORWARD = "FORWARD"
</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 = "gregorian"
</span><span class="cx"> RSCALE_HEBREW = "hebrew"
</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"> "gregorian": "",
</span><del>- "chinese": "C",
</del><ins>+ "japanese": "J",
+ "buddhist": "BT",
+ "roc": "RC",
+ "persian": "P",
</ins><span class="cx"> "islamic-civil": "I",
</span><ins>+ "islamic": "I2",
</ins><span class="cx"> "hebrew": "H",
</span><ins>+ "chinese": "C",
+ "indian": "IN",
+ "coptic": "CT",
</ins><span class="cx"> "ethiopic": "E",
</span><ins>+ "ethiopic-amete-alem": "E2",
+ "iso8601": "I8",
+ "dangi": "D",
+ "islamic-umalqura": "I3",
+ "islamic-tbla": "I4",
+ "islamic-rgsa": "I5",
</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"> """
</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"> """
</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"> """
</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"> """
</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):
+ """
+ Return the L{list} of known RSCALEs.
+
+ @return: all valid RSCALEs
+ @rtype: L{list}
+ """
+
+ return cls.VALID_RSCALES.keys()
+
+
+ @classmethod
+ def validRSCALE(cls, rscale):
+ """
+ Test to see if the specified calendar scale is valid.
+ We use a cache to maintain known values.
+
+ @param rscale: calendar scale to use
+ @type rscale: L{str}
+
+ @return: L{True} if valid, L{False} if not
+ @rtype: L{bool}
+ """
+
+ return rscale.lower() in cls.VALID_RSCALES
+
+
+ @classmethod
+ def limitsRSCALE(cls, rscale):
+ """
+ Get the day, month, yearday limits for the specified RSCALE to use in bounds
+ checking allowed values.
+
+ @param rscale: calendar scale to use
+ @type rscale: L{str}
+
+ @return: a L{dict} containing keys "monthday", "month" and "yearday"
+ @rtype: L{dict}
+ """
+
+ if rscale not in cls.RSCALE_LIMITS:
+ if rscale is None:
+ # Gregorian limits
+ result = {"monthday": 31, "month": 12, "weekno": 53, "yearday": 366}
+ else:
+ # Fetch limits from ICU
+ result = {}
+ ucal = cls._newUcal(rscale)
+ error = ffi.new("UErrorCode *", 0)
+ result["monthday"] = ICU.ucal_getLimit(ucal, ICU.UCAL_DAY_OF_MONTH, ICU.UCAL_MAXIMUM, error)
+ result["month"] = ICU.ucal_getLimit(ucal, ICU.UCAL_MONTH, ICU.UCAL_MAXIMUM, error) + 1
+ result["weekno"] = ICU.ucal_getLimit(ucal, ICU.UCAL_WEEK_OF_YEAR, ICU.UCAL_MAXIMUM, error)
+ result["yearday"] = ICU.ucal_getLimit(ucal, ICU.UCAL_DAY_OF_YEAR, ICU.UCAL_MAXIMUM, error)
+ ICU.ucal_close(ucal)
+ cls.RSCALE_LIMITS[rscale] = result
+
+ return cls.RSCALE_LIMITS[rscale]
+
+
+ @classmethod
</ins><span class="cx"> def _newUcal(cls, rscale):
</span><span class="cx"> """
</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("char[]", calsystem), ICU.UCAL_DEFAULT, error)
</span><span class="cx"> if error[0] != ICU.U_ZERO_ERROR:
</span><span class="cx"> raise ValueError("Unable to create ICU calendar for rscale '{}', code: {}".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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</span><span class="lines">@@ -406,18 +518,31 @@
</span><span class="cx"> """
</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 >= 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"> """
</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 "isleapmonth" 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"> """
</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 >= 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"> """
</span><del>- year = self.getYear()
- month = self.getMonth()
- day = self.getDay()
- isleapmonth = self.getLeapMonth()
</del><span class="cx">
</span><ins>+ error = ffi.new("UErrorCode *", 0)
+ year = ICU.ucal_get(self.ucal, ICU.UCAL_EXTENDED_YEAR, error)
+ month = self._icuToNumericMonth(ICU.ucal_get(self.ucal, ICU.UCAL_MONTH, error))
+ isleapmonth = ICU.ucal_get(self.ucal, ICU.UCAL_IS_LEAP_MONTH, error) != 0
</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("UErrorCode *", 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("UErrorCode *", 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("YYMMDDLL", ("year", "month", "day", "isleapmonth"))
+
</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"> """
</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"> """
</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] < 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 > 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] > 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 > 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"> """
</span><del>- calcode = self.RSCALE_CALCODE.get(self.rscale.lower(), "{}:".format(self.rscale))
</del><ins>+ calcode = self.VALID_RSCALES.get(self.rscale.lower(), "{}:".format(self.rscale))
</ins><span class="cx"> if calcode:
</span><span class="cx"> calcode = "{{{}}}".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("chinese", 4651, 1, 1, False)
</del><ins>+ newyear = ICUDateTime.fromDateComponents("chinese", 4653, 1, 30, False)
</ins><span class="cx"> print("From: {} to {}".format(
</span><span class="cx"> newyear.getText(),
</span><span class="cx"> newyear.convertTo("gregorian").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("From: {} to {}".format(
</span><span class="cx"> newyear.getText(),
</span><span class="cx"> newyear.convertTo("gregorian").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"> """
</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"> """
</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("gregorian", 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("gregorian", 2013, 1, 1)
+ iter = RecurrenceIterator(icudt, definitions.eRecurrence_DAILY, 1)
+ for i in range(1200):
+ gregorian = iter.next()
+ nongregorian = gregorian.convertTo("hebrew")
+ print("{}\t{}".format(gregorian.getText(), nongregorian.getText()))
</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("mRscale", rscale)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -371,10 +375,7 @@
</span><span class="cx"> """
</span><span class="cx"> The default skip value depends on whether RSCALE is used or not
</span><span class="cx"> """
</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("Recurrence: Only one BYMONTHDAY allowed")
</span><span class="cx"> self.mByMonthDay = []
</span><del>- self.parseList(tvalue, self.mByMonthDay, 1, 31, True, errmsg="Recurrence: Invalid BYMONTHDAY value")
</del><ins>+ self.parseList(tvalue, self.mByMonthDay, 1, ICUDateTime.limitsRSCALE(self.mRscale)["monthday"], True, errmsg="Recurrence: Invalid BYMONTHDAY value")
</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("Recurrence: Only one BYYEARDAY allowed")
</span><span class="cx"> self.mByYearDay = []
</span><del>- self.parseList(tvalue, self.mByYearDay, 1, 366, True, errmsg="Recurrence: Invalid BYYEARDAY value")
</del><ins>+ self.parseList(tvalue, self.mByYearDay, 1, ICUDateTime.limitsRSCALE(self.mRscale)["yearday"], True, errmsg="Recurrence: Invalid BYYEARDAY value")
</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("Recurrence: Only one BYWEEKNO allowed")
</span><span class="cx"> self.mByWeekNo = []
</span><del>- self.parseList(tvalue, self.mByWeekNo, 1, 53, True, errmsg="Recurrence: Invalid BYWEEKNO value")
</del><ins>+ self.parseList(tvalue, self.mByWeekNo, 1, ICUDateTime.limitsRSCALE(self.mRscale)["weekno"], True, errmsg="Recurrence: Invalid BYWEEKNO value")
</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("Recurrence: Only one BYMONTH allowed")
</span><span class="cx"> self.mByMonth = []
</span><del>- self.parseMonthNumList(tvalue, self.mByMonth, 1, 12, errmsg="Recurrence: Invalid BYMONTH value")
</del><ins>+ self.parseMonthNumList(tvalue, self.mByMonth, 1, ICUDateTime.limitsRSCALE(self.mRscale)["month"], errmsg="Recurrence: Invalid BYMONTH value")
</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 > 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 > 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) > maxInstances:
</span><span class="cx"> raise TooManyInstancesError("Too many instances")
</span><span class="cx">
</span><del>- # Check limits
- if self.mUseCount:
- # Exit if max count reached
- if len(results) >= self.mCount:
- return True
</del><ins>+ # Check count limit and exit if max count reached
+ if self.mUseCount and len(results) >= 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 < 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 > 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 > 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) > maxInstances:
</span><span class="cx"> raise TooManyInstancesError("Too many instances")
</span><span class="cx">
</span><del>- # Check limits
- if self.mUseCount:
- # Exit if max count reached
- if len(results) >= self.mCount:
- return True
</del><ins>+ # Check count limit and exit if max count reached
+ if self.mUseCount and len(results) >= 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>-                "title": "MonthlyRscaleStartInLeapYearSkipYes - start: {C}46501230",
-                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;SKIP=YES"],
</del><ins>+                "title": "MonthlyRscaleStartInLeapYearSkipOmit - start: {C}46501230",
+                "rule": [
+                        "RSCALE=CHINESE;FREQ=MONTHLY;SKIP=OMIT",
+ "RSCALE=CHINESE;FREQ=MONTHLY"
+                ],
</ins><span class="cx">                 "start": "20140130",
</span><span class="cx">                 "end": "20180101",
</span><span class="cx">                 "results": [
</span><span class="lines">@@ -92,10 +95,7 @@
</span><span class="cx">         },
</span><span class="cx">         {
</span><span class="cx">                 "title": "MonthlyRscaleStartInLeapYearSkipBackwardDefault - start: {C}46501230",
</span><del>-                "rule": [
- "RSCALE=CHINESE;FREQ=MONTHLY;SKIP=BACKWARD",
- "RSCALE=CHINESE;FREQ=MONTHLY"
-                ],
</del><ins>+                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;SKIP=BACKWARD"],
</ins><span class="cx">                 "start": "20140130",
</span><span class="cx">                 "end": "20180101",
</span><span class="cx">                 "results": [
</span><span class="lines">@@ -151,8 +151,11 @@
</span><span class="cx">                 ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><del>-                "title": "YearlyLeapDaySkipYes",
-                "rule": ["RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=YES;COUNT=5"],
</del><ins>+                "title": "YearlyLeapDaySkipOmit",
+                "rule": [
+                        "RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=OMIT;COUNT=5",
+ "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=5"
+                ],
</ins><span class="cx">                 "start": "20160229",
</span><span class="cx">                 "end": "21000101",
</span><span class="cx">                 "results": [
</span><span class="lines">@@ -178,10 +181,7 @@
</span><span class="cx">         },
</span><span class="cx">         {
</span><span class="cx">                 "title": "YearlyLeapDaySkipBackwardDefault",
</span><del>-                "rule": [
- "RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=BACKWARD;COUNT=5",
- "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=5"
-                ],
</del><ins>+                "rule": ["RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=BACKWARD;COUNT=5"],
</ins><span class="cx">                 "start": "20160229",
</span><span class="cx">                 "end": "21000101",
</span><span class="cx">                 "results": [
</span><span class="lines">@@ -193,8 +193,8 @@
</span><span class="cx">                 ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><del>-                "title": "ChineseMonthlyByMonthDay30SkipYes",
-                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=30;SKIP=YES"],
</del><ins>+                "title": "ChineseMonthlyByMonthDay30SkipOmit",
+                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=30;SKIP=OMIT"],
</ins><span class="cx">                 "start": "20140130T120000",
</span><span class="cx">                 "end": "20150101T000000",
</span><span class="cx">                 "results": [
</span><span class="lines">@@ -248,8 +248,8 @@
</span><span class="cx">                 ]
</span><span class="cx">         },
</span><span class="cx">         {
</span><del>-                "title": "ChineseMonthlyByMonthDayMinus30SkipYes",
-                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=-30;SKIP=YES"],
</del><ins>+                "title": "ChineseMonthlyByMonthDayMinus30SkipOmit",
+                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=-30;SKIP=OMIT"],
</ins><span class="cx">                 "start": "20140130T120000",
</span><span class="cx">                 "end": "20150101T000000",
</span><span class="cx">                 "results": [
</span><span class="lines">@@ -300,5 +300,316 @@
</span><span class="cx"> "20141122T120000",
</span><span class="cx"> "20141222T120000"
</span><span class="cx">                 ]
</span><ins>+        },
+        {
+                "title": "Ethiopic last day of year",
+                "rule": ["RSCALE=ETHIOPIC;FREQ=YEARLY;BYMONTH=13;BYMONTHDAY=-1;COUNT=6"],
+                "start": "20140910",
+                "end": "20200101",
+                "results": ["20140910","20150911","20160910","20170910","20180910","20190911"]
+        },
+
+
+        {
+                "title": "Chinese New Year",
+                "rule": ["RSCALE=CHINESE;FREQ=YEARLY;UNTIL=20180101"],
+                "start": "20130210",
+                "end": "20180101",
+                "results": ["20130210","20140131","20150219","20160208","20170128"]
+        },
+
+
+        {
+                "title": "Chinese monthly",
+                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;COUNT=4"],
+                "start": "20140920",
+                "end": "20150101",
+                "results": ["20140920","20141020","20141119","20141218"]
+        },
+
+
+        {
+                "title": "Islamic monthly",
+                "rule": ["RSCALE=ISLAMIC-CIVIL;FREQ=MONTHLY;COUNT=4"],
+                "start": "20131025",
+                "end": "20150101",
+                "results": ["20131025","20131124","20131224","20140122"]
+        },
+
+
+        {
+                "title": "Ramadan",
+                "rule": ["RSCALE=ISLAMIC-CIVIL;FREQ=YEARLY;BYMONTH=9;COUNT=5"],
+                "start": "20130709",
+                "end": "20180101",
+                "results": ["20130709","20140629","20150618","20160607","20170527"]
+        },
+
+
+        {
+                "title": "Buddha birthday",
+                "rule": ["RSCALE=DANGI;FREQ=DAILY;BYMONTHDAY=8;BYMONTH=4;UNTIL=20160101"],
+                "start": "20131025",
+                "end": "20160101",
+                "results": ["20140506","20150525"]
+        },
+
+
+        {
+                "title": "No instances in Chinese leap month",
+                "rule": ["RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9;COUNT=3"],
+                "start": "20131025",
+                "end": "20170101",
+                "results": ["20141003","20151022","20161010"]
+        },
+
+
+        {
+                "title": "Chinese 9L leap month skip - daily",
+                "rule": ["RSCALE=CHINESE;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2"],
+                "start": "20141102",
+                "end": "21500101",
+                "results": ["20141102","21091102"]
+        },
+
+
+        {
+                "title": "Chinese 9L leap month skip - monthly",
+                "rule": ["RSCALE=CHINESE;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2"],
+                "start": "20141102",
+                "end": "21500101",
+                "results": ["20141102","21091102"]
+        },
+
+
+        {
+                "title": "Chinese 9L leap month skip - yearly",
+                "rule": ["RSCALE=CHINESE;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=2"],
+                "start": "20141102",
+                "end": "21500101",
+                "results": ["20141102","21091102"]
+        },
+
+
+        {
+                "title": "Chinese 4L leap month skip - daily",
+                "rule": ["FREQ=DAILY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT"],
+                "start": "20131025",
+                "end": "21000101",
+                "results": ["20200601","20580531","20690530","20770531","20880530","20960531"]
+        },
+
+
+        {
+                "title": "Chinese 4L leap month skip - monthly",
+                "rule": ["FREQ=MONTHLY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT"],
+                "start": "20131025",
+                "end": "21000101",
+                "results": ["20200601","20580531","20690530","20770531","20880530","20960531"]
+        },
+
+
+        {
+                "title": "Chinese 4L leap month skip - yearly",
+                "rule": ["FREQ=YEARLY;RSCALE=CHINESE;UNTIL=21000101;BYMONTHDAY=10;BYMONTH=4L;SKIP=OMIT"],
+                "start": "20131025",
+                "end": "21000101",
+                "results": ["20200601","20580531","20690530","20770531","20880530","20960531"]
+        },
+
+
+        {
+                "title": "Chinese 9L leap month skip back - daily",
+                "rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3"],
+                "start": "20131025",
+                "end": "20170101",
+                "results": ["20141102"]
+        },
+
+
+        {
+                "title": "Chinese 9L leap month skip back - monthly",
+                "rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3"],
+                "start": "20131025",
+                "end": "20170101",
+                "results": ["20141102"]
+        },
+
+
+        {
+                "title": "Chinese 9L leap month skip back - yearly",
+                "rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9L;COUNT=3"],
+                "start": "20131025",
+                "end": "20170101",
+                "results": ["20141102","20151022","20161010"]
+        },
+
+
+        {
+                "title": "Chinese 9,9L leap month skip back - daily",
+                "rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=DAILY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4"],
+                "start": "20131025",
+                "end": "20170101",
+                "results": ["20141003","20141102","20151022","20161010"]
+        },
+
+        {
+                "title": "Chinese 9,9L leap month skip back - monthly",
+                "rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=MONTHLY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4"],
+                "start": "20131025",
+                "end": "20170101",
+                "results": ["20141003","20141102","20151022","20161010"]
+        },
+
+        {
+                "title": "Chinese 9,9L leap month skip back - yearly",
+                "rule": ["RSCALE=CHINESE;SKIP=BACKWARD;FREQ=YEARLY;BYMONTHDAY=10;BYMONTH=9,9L;COUNT=4"],
+                "start": "20131025",
+                "end": "20170101",
+                "results": ["20141003","20141102","20151022","20161010"]
+        },
+
+
+        {
+                "title": "Hebrew yearly skip",
+                "rule": ["RSCALE=HEBREW;FREQ=YEARLY;COUNT=4"],
+                "start": "20140205",
+                "end": "20230101",
+                "results": ["20140205","20160214","20190210","20220206"]
+        },
+
+
+        {
+                "title": "Hebrew yearly skip forward",
+                "rule": ["RSCALE=HEBREW;FREQ=YEARLY;SKIP=FORWARD;COUNT=4"],
+                "start": "20140205",
+                "end": "20180101",
+                "results": ["20140205","20150224","20160214","20170303"]
+        },
+
+
+        {
+                "title": "Hebrew yearly 5L skip forward",
+                "rule": ["RSCALE=HEBREW;FREQ=YEARLY;BYMONTH=5L;BYMONTHDAY=8;SKIP=FORWARD;COUNT=5"],
+                "start": "20140208",
+                "end": "20190101",
+                "results": ["20140208","20150227","20160217","20170306","20180223"]
+        },
+
+
+        {
+                "title": "Gregorian monthly skip back",
+                "rule": ["RSCALE=GREGORIAN;FREQ=MONTHLY;SKIP=BACKWARD;COUNT=4"],
+                "start": "20140131",
+                "end": "20150101",
+                "results": ["20140131","20140228","20140331","20140430"]
+        },
+
+
+        {
+                "title": "Gregorian monthly skip forward",
+                "rule": ["RSCALE=GREGORIAN;FREQ=MONTHLY;SKIP=FORWARD;COUNT=4"],
+                "start": "20140131",
+                "end": "20150101",
+                "results": ["20140131","20140301","20140331","20140501"]
+        },
+
+
+        {
+                "title": "Gregorian leap day skip forward",
+                "rule": ["RSCALE=GREGORIAN;FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=28,29;SKIP=FORWARD;COUNT=5"],
+                "start": "20150201",
+                "end": "20180101",
+                "results": ["20150228","20150301","20160228","20160229","20170228"]
+        },
+
+
+        {
+                "title": "Gregorian monthly interval skip forward",
+                "rule": ["RSCALE=GREGORIAN;FREQ=MONTHLY;INTERVAL=3;SKIP=FORWARD;COUNT=4"],
+                "start": "20140131",
+                "end": "20150101",
+                "results": ["20140131","20140501","20140731","20141031"]
+        },
+
+
+        {
+                "title": "Gregorian month day skip forward",
+                "rule": ["FREQ=MONTHLY;RSCALE=GREGORIAN;SKIP=FORWARD;COUNT=12;BYMONTHDAY=31"],
+                "start": "20150131",
+                "end": "20160101",
+                "results": ["20150131","20150301","20150331","20150501","20150531","20150701","20150731","20150831","20151001","20151031","20151201","20151231"]
+        },
+
+
+        {
+                "title": "Gregorian last month day skip forward",
+                "rule": ["FREQ=MONTHLY;RSCALE=GREGORIAN;SKIP=FORWARD;COUNT=12;BYMONTHDAY=-1"],
+                "start": "20150131",
+                "end": "20160101",
+                "results": ["20150131","20150228","20150331","20150430","20150531","20150630","20150731","20150831","20150930","20151031","20151130","20151231"]
+        },
+
+
+        {
+                "title": "Hebrew leap month day skip forward",
+                "rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=FORWARD;COUNT=5;BYMONTHDAY=30;BYMONTH=5L"],
+                "start": "20140302",
+                "end": "20190101",
+                "results": ["20140302","20150321","20160310","20170328","20180317"]
+        },
+
+
+        {
+                "title": "Hebrew last leap month day skip forward",
+                "rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=FORWARD;COUNT=5;BYMONTHDAY=-1;BYMONTH=5L"],
+                "start": "20140302",
+                "end": "20190101",
+                "results": ["20140302","20150320","20160310","20170327","20180316"]
+        },
+
+
+        {
+                "title": "Hebrew leap month day skip back",
+                "rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=BACKWARD;COUNT=5;BYMONTHDAY=30;BYMONTH=5L"],
+                "start": "20140302",
+                "end": "20190101",
+                "results": ["20140302","20150219","20160310","20170226","20180215"]
+        },
+
+
+        {
+                "title": "Hebrew last leap month day skip forward",
+                "rule": ["FREQ=YEARLY;RSCALE=HEBREW;SKIP=BACKWARD;COUNT=5;BYMONTHDAY=-1;BYMONTH=5L"],
+                "start": "20140302",
+                "end": "20190101",
+                "results": ["20140302","20150219","20160310","20170226","20180215"]
+        },
+
+
+        {
+                "title": "Chinese leap month day skip forward - daily",
+                "rule": ["FREQ=DAILY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L"],
+                "start": "20131025",
+                "end": "20160101",
+                "results": ["20141102"]
+        },
+
+
+        {
+                "title": "Chinese leap month day skip forward - monthly",
+                "rule": ["FREQ=MONTHLY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L"],
+                "start": "20131025",
+                "end": "20160101",
+                "results": ["20141102"]
+        },
+
+
+        {
+                "title": "Chinese leap month day skip forward - yearly",
+                "rule": ["FREQ=YEARLY;RSCALE=CHINESE;SKIP=FORWARD;COUNT=3;BYMONTHDAY=10;BYMONTH=9L"],
+                "start": "20131025",
+                "end": "20160101",
+                "results": ["20131112", "20141102","20151121"]
</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"> """
</span><span class="cx">
</span><ins>+ def testValidRscale(self):
+
+ data_rscale = (
+ ("gregorian", True,),
+ ("chinese", True,),
+ ("foo", False,),
+ ("gregorian", True,),
+ ("chinese", True,),
+ ("foo", False,),
+ )
+
+ for rscale, result in data_rscale:
+ valid = ICUDateTime.validRSCALE(rscale)
+ self.assertEqual(valid, result, "Failed on: %s" % (rscale,))
+
+
+ def testLimitsRscale(self):
+
+ data_rscale = (
+ (None, {"monthday": 31, "month": 12, "weekno": 53, "yearday": 366},),
+ ("gregorian", {"monthday": 31, "month": 12, "weekno": 53, "yearday": 366},),
+ ("chinese", {"monthday": 30, "month": 12, "weekno": 55, "yearday": 385},),
+ ("ethiopic", {"monthday": 30, "month": 13, "weekno": 53, "yearday": 366},),
+ ("hebrew", {"monthday": 30, "month": 13, "weekno": 56, "yearday": 385},),
+ )
+
+ for rscale, result in data_rscale:
+ limit = ICUDateTime.limitsRSCALE(rscale)
+ self.assertEqual(limit, result, "Failed on: %s: %s" % (rscale, limit))
+
+
</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>- ("gregorian", 2012, 1, 2, False, 2, "20120202", False,),
- ("gregorian", 2012, 1, 29, False, 2, "20120229", False,),
- ("gregorian", 2012, 1, 31, False, 2, "20120302", True,),
- ("gregorian", 2012, 2, 29, False, 3, "20120329", False,),
</del><ins>+ ("gregorian", 2012, 1, 2, False, 2, False, "20120202", False,),
+ ("gregorian", 2012, 1, 29, False, 2, False, "20120229", False,),
+ ("gregorian", 2012, 1, 31, False, 2, False, "20120302", True,),
+ ("gregorian", 2012, 2, 29, False, 3, False, "20120329", False,),
+ ("chinese", 4651, 1, 1, False, 9, False, "{C}46510901", False,),
+ ("chinese", 4651, 1, 1, False, 9, True, "{C}465109L01", False,),
+ ("chinese", 4651, 1, 1, False, 10, False, "{C}46511001", False,),
+ ("hebrew", 5774, 1, 1, False, 5, False, "{H}57740501", False,),
+ ("hebrew", 5774, 1, 1, False, 5, True, "{H}577405L01", False,),
+ ("hebrew", 5774, 1, 1, False, 6, False, "{H}57740601", False,),
</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, "Failed on: {} {} {}".format(month, result, result_invalid,))
- self.assertEqual(dt.invalid(), result_invalid, "Failed invalid on: {} {} {}".format(month, result, result_invalid,))
</del><ins>+ dt.setMonth(month, leapmonth)
+ self.assertEqual(dt.getText(), result, "Failed on: {} {} {} {}".format(month, leapmonth, result, result_invalid,))
+ self.assertEqual(dt.invalid(), result_invalid, "Failed invalid on: {} {} {} {}".format(month, leapmonth, result, result_invalid,))
</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, "Failed on: {} vs {}".format(result, dt.getText(),))
</span><ins>+
+
+ def testSkip(self):
+
+ data_date = (
+ ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_BACKWARD, False, "20150228",),
+ ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_FORWARD, False, "20150301",),
+ ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_OMIT, False, None,),
+ ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_BACKWARD, True, None,),
+ ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_FORWARD, True, None,),
+ ("gregorian", 2015, 2, 29, False, definitions.eRecurrence_SKIP_OMIT, True, None,),
+ ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_BACKWARD, False, None,),
+ ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_FORWARD, False, None,),
+ ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_OMIT, False, None,),
+ ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_BACKWARD, True, "{C}46520902",),
+ ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_FORWARD, True, "{C}46521002",),
+ ("chinese", 4652, 9, 2, True, definitions.eRecurrence_SKIP_OMIT, True, None,),
+ )
+
+ ctr = 0
+ for rscale, y, m, d, l, skip, skip_monthly, result in data_date:
+ ctr += 1
+ dt = DateTime.getToday()
+ udt = ICUDateTime.fromDateTime(dt, rscale)
+ udt.setYYMMDD(y, m, d, l)
+ self.assertEqual(udt.invalid(), True)
+ udt.invalidSkip(skip, skip_monthly)
+ test_value = udt.getText() if not udt.invalid() else None
+ self.assertEqual(test_value, result, msg="Failed: #{} {} vs {}".format(ctr, test_value, result))
+
+
+ def testDuplicates(self):
+
+ dt1 = ICUDateTime.fromDateComponents("gregorian", 2011, 1, 2, False)
+ dt2 = ICUDateTime.fromDateComponents("gregorian", 2011, 1, 2, False)
+ dt3 = ICUDateTime.fromDateComponents("gregorian", 2011, 1, 3, False)
+ self.assertEqual(len(set((dt1, dt2, dt3,))), 2)
</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("chinese", 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("chinese", 4650, 12, 30).toDateTime()
</span><span class="cx">
</span><del>- riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale="chinese", skip=definitions.eRecurrence_SKIP_YES)
</del><ins>+ riter = RecurrenceIterator(dt, definitions.eRecurrence_MONTHLY, 1, rscale="chinese", 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"> "RSCALE=CHINESE;FREQ=DAILY",
</span><del>- "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=YES",
</del><ins>+ "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=OMIT",
</ins><span class="cx"> "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=BACKWARD",
</span><span class="cx"> "RSCALE=GREGORIAN;FREQ=YEARLY;COUNT=400;SKIP=FORWARD",
</span><span class="cx"> "RSCALE=CHINESE;FREQ=YEARLY;BYMONTH=5,6,6L,7",
</span><span class="lines">@@ -89,6 +89,7 @@
</span><span class="cx"> "FREQ=MONTHLY;BYHOUR=54",
</span><span class="cx"> "FREQ=MONTHLY;SKIP=YES",
</span><span class="cx"> "RSCALE=CHINESE;FREQ=MONTHLY;SKIP=NO",
</span><ins>+ "RSCALE=CHINESE;FREQ=MONTHLY;SKIP=YES",
</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["rule"]]
</span><span class="cx"> if "RSCALE" not in rules[0]:
</span><del>- rules.append("RSCALE=GREGORIAN;{};SKIP=YES".format(rules[0]))
</del><ins>+ rules.append("RSCALE=GREGORIAN;{};SKIP=OMIT".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="Failed rule: #{} {}".format(ctr + 1, rule)
</del><ins>+ msg="Failed rule: #{ctr} {rule}: {items}".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("FREQ=WEEKLY;UNTIL=20130110")
+
+ start = DateTime(2013, 1, 1, 0, 0, 0)
+ end = DateTime(2013, 1, 9, 0, 0, 0)
+ range = Period(start, end)
+ items = []
+ limited = recur.expand(start, range, items)
+ self.assertFalse(limited)
+
+ recur = Recurrence()
+ recur.parse("FREQ=WEEKLY;UNTIL=20130110")
+
+ start = DateTime(2013, 1, 1, 0, 0, 0)
+ end = DateTime(2013, 1, 11, 0, 0, 0)
+ range = Period(start, end)
+ items = []
+ limited = recur.expand(start, range, items)
+ self.assertFalse(limited)
+
+ recur = Recurrence()
+ recur.parse("FREQ=WEEKLY;UNTIL=20130110")
+
+ start = DateTime(2013, 1, 1, 0, 0, 0)
+ end = DateTime(2013, 1, 6, 0, 0, 0)
+ range = Period(start, end)
+ items = []
+ limited = recur.expand(start, range, items)
+ self.assertTrue(limited)
+
+
</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["rule"]:
</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="Failed rule: #{} {}".format(ctr + 1, rule)
</del><ins>+ msg="Failed rule: #{ctr} {rule}: {items}".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 = "interval"
</span><span class="cx">
</span><span class="cx"> recur_skip = "skip"
</span><del>-recur_skip_yes = "yes"
</del><ins>+recur_skip_omit = "omit"
</ins><span class="cx"> recur_skip_backward = "backward"
</span><span class="cx"> recur_skip_forward = "forward"
</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("Invalid property", data)
</del><ins>+ except Exception as e:
+ raise InvalidProperty("Invalid property", 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="utc"))
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.setYear(2013)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.offsetYear(1)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.setMonth(2)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.offsetMonth(1)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.setDay(2)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.offsetDay(1)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.setHours(2)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.offsetHours(1)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.setMinutes(2)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.offsetMinutes(1)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.setSeconds(2)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+ dt.offsetSeconds(1)
+ self.assertFalse(dt.mPosixTimeCached)
+ dt.getPosixTime()
+ self.assertTrue(dt.mPosixTimeCached)
+
+
</ins><span class="cx"> def testCachePreserveOnAdjustment(self):
</span><span class="cx">
</span><span class="cx"> # UTC first
</span></span></pre>
</div>
</div>
</body>
</html>