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