[CalendarServer-changes] [7208] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Thu Mar 17 11:54:11 PDT 2011
Revision: 7208
http://trac.macosforge.org/projects/calendarserver/changeset/7208
Author: cdaboo at apple.com
Date: 2011-03-17 11:54:11 -0700 (Thu, 17 Mar 2011)
Log Message:
-----------
Merge of pycalendar branch.
Modified Paths:
--------------
CalendarServer/trunk/benchmark
CalendarServer/trunk/calendarserver/tools/anonymize.py
CalendarServer/trunk/calendarserver/tools/purge.py
CalendarServer/trunk/calendarserver/tools/test/test_purge.py
CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py
CalendarServer/trunk/contrib/tools/dtraceanalyze.py
CalendarServer/trunk/support/Makefile.Apple
CalendarServer/trunk/support/build.sh
CalendarServer/trunk/twext/python/datetime.py
CalendarServer/trunk/twistedcaldav/caldavxml.py
CalendarServer/trunk/twistedcaldav/customxml.py
CalendarServer/trunk/twistedcaldav/datafilters/calendardata.py
CalendarServer/trunk/twistedcaldav/datafilters/test/test_calendardata.py
CalendarServer/trunk/twistedcaldav/datafilters/test/test_privateevents.py
CalendarServer/trunk/twistedcaldav/dateops.py
CalendarServer/trunk/twistedcaldav/freebusyurl.py
CalendarServer/trunk/twistedcaldav/ical.py
CalendarServer/trunk/twistedcaldav/instance.py
CalendarServer/trunk/twistedcaldav/localization.py
CalendarServer/trunk/twistedcaldav/mail.py
CalendarServer/trunk/twistedcaldav/method/put_common.py
CalendarServer/trunk/twistedcaldav/method/report_common.py
CalendarServer/trunk/twistedcaldav/query/calendarquery.py
CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
CalendarServer/trunk/twistedcaldav/query/expression.py
CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py
CalendarServer/trunk/twistedcaldav/query/test/test_calendarquery.py
CalendarServer/trunk/twistedcaldav/query/test/test_queryfilter.py
CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
CalendarServer/trunk/twistedcaldav/scheduling/itip.py
CalendarServer/trunk/twistedcaldav/scheduling/processing.py
CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py
CalendarServer/trunk/twistedcaldav/scheduling/test/test_implicit.py
CalendarServer/trunk/twistedcaldav/scheduling/test/test_itip.py
CalendarServer/trunk/twistedcaldav/sharing.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
CalendarServer/trunk/twistedcaldav/test/test_localization.py
CalendarServer/trunk/twistedcaldav/test/test_mail.py
CalendarServer/trunk/twistedcaldav/test/test_multiget.py
CalendarServer/trunk/twistedcaldav/test/test_timezones.py
CalendarServer/trunk/twistedcaldav/test/test_upgrade.py
CalendarServer/trunk/twistedcaldav/test/test_validation.py
CalendarServer/trunk/twistedcaldav/timezones.py
CalendarServer/trunk/twistedcaldav/timezoneservice.py
CalendarServer/trunk/txdav/caldav/datastore/index_file.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/common.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
CalendarServer/trunk/txdav/caldav/datastore/util.py
CalendarServer/trunk/txdav/caldav/icalendarstore.py
CalendarServer/trunk/txdav/common/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
Added Paths:
-----------
CalendarServer/trunk/benchreport
CalendarServer/trunk/twistedcaldav/test/test_caldavxml.py
CalendarServer/trunk/twistedcaldav/test/test_customxml.py
Property Changed:
----------------
CalendarServer/trunk/
CalendarServer/trunk/support/build.sh
CalendarServer/trunk/txdav/caldav/datastore/index_file.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
CalendarServer/trunk/txdav/carddav/datastore/index_file.py
CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
Property changes on: CalendarServer/trunk
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
+ /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
Modified: CalendarServer/trunk/benchmark
===================================================================
--- CalendarServer/trunk/benchmark 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/benchmark 2011-03-17 18:54:11 UTC (rev 7208)
@@ -1,7 +1,7 @@
#!/bin/sh
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -21,6 +21,6 @@
wd="$(cd "$(dirname "$0")" && pwd -L)";
-export PYTHONPATH="$("${wd}/run" -p)";
+export PYTHONPATH="$("${wd}/run" -p):${wd}/../CalDAVClientLibrary/src";
exec "${wd}/contrib/performance/benchmark" "$@";
Copied: CalendarServer/trunk/benchreport (from rev 7206, CalendarServer/branches/users/cdaboo/pycalendar/benchreport)
===================================================================
--- CalendarServer/trunk/benchreport (rev 0)
+++ CalendarServer/trunk/benchreport 2011-03-17 18:54:11 UTC (rev 7208)
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+##
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+set -e;
+set -u;
+
+wd="$(cd "$(dirname "$0")" && pwd -L)";
+
+export PYTHONPATH="$("${wd}/run" -p):${wd}/../CalDAVClientLibrary/src";
+
+args="$@"
+
+echo "Parameter: 1";
+"${wd}/contrib/performance/report" ${args} 1 HTTP summarize;
+echo;
+echo "Parameter: 9";
+"${wd}/contrib/performance/report" ${args} 9 HTTP summarize;
+echo;
+echo "Parameter: 81";
+"${wd}/contrib/performance/report" ${args} 81 HTTP summarize;
Modified: CalendarServer/trunk/calendarserver/tools/anonymize.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/anonymize.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/calendarserver/tools/anonymize.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -28,12 +28,13 @@
import sys
import urllib
import uuid
-import vobject
import xattr
import zlib
from twext.python.plistlib import readPlistFromString
+from pycalendar.calendar import PyCalendar
+
COPY_CAL_XATTRS = (
'WebDAV:{DAV:}resourcetype',
'WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-timezone',
@@ -275,62 +276,62 @@
def anonymizeData(directoryMap, data):
- vobj = vobject.readComponents(data).next()
+ pyobj = PyCalendar.parseText(data)
# Delete property from the top level
try:
- for prop in vobj.contents['x-wr-calname']:
- prop.value = anonymize(prop.value)
+ for prop in pyobj.getProperties('x-wr-calname'):
+ prop.setValue(anonymize(prop.getValue().getValue()))
except KeyError:
pass
- for comp in vobj.components():
+ for comp in pyobj.getComponents():
# Replace with anonymized CUAs:
for propName in ('organizer', 'attendee'):
try:
- for prop in list(comp.contents[propName]):
- cua = prop.value
+ for prop in tuple(comp.getProperties(propName)):
+ cua = prop.getValue().getValue()
record = directoryMap.lookupCUA(cua)
if record is None:
# print "Can't find record for", cua
record = directoryMap.addRecord(cua=cua)
if record is None:
- comp.remove(prop)
+ comp.removeProperty(prop)
continue
- prop.value = "urn:uuid:%s" % (record['guid'],)
- if prop.params.has_key('X-CALENDARSERVER-EMAIL'):
- prop.params['X-CALENDARSERVER-EMAIL'] = (record['email'],)
+ prop.setValue("urn:uuid:%s" % (record['guid'],))
+ if prop.hasAttribute('X-CALENDARSERVER-EMAIL'):
+ prop.setAttribute('X-CALENDARSERVER-EMAIL', record['email'])
else:
- prop.params['EMAIL'] = (record['email'],)
- prop.params['CN'] = (record['name'],)
+ prop.setAttribute('EMAIL', record['email'])
+ prop.setAttribute('CN', record['name'])
except KeyError:
pass
# Replace with anonymized text:
for propName in ('summary', 'location', 'description'):
try:
- for prop in comp.contents[propName]:
- prop.value = anonymize(prop.value)
+ for prop in comp.getProperties(propName):
+ prop.setValue(anonymize(prop.getValue().getValue()))
except KeyError:
pass
# Replace with anonymized URL:
try:
- for prop in comp.contents['url']:
- prop.value = "http://example.com/%s/" % (anonymize(prop.value),)
+ for prop in comp.getProperties('url'):
+ prop.setValue("http://example.com/%s/" % (anonymize(prop.getValue().getValue()),))
except KeyError:
pass
# Remove properties:
for propName in ('x-apple-dropbox', 'attach'):
try:
- for prop in list(comp.contents[propName]):
- comp.remove(prop)
+ for prop in tuple(comp.getProperties(propName)):
+ comp.removeProperty(prop)
except KeyError:
pass
- return vobj.serialize()
+ return pyobj.serialize()
class DirectoryMap(object):
Modified: CalendarServer/trunk/calendarserver/tools/purge.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/purge.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/calendarserver/tools/purge.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -20,11 +20,8 @@
import sys
from errno import ENOENT, EACCES
-from datetime import date, timedelta, datetime
from getopt import getopt, GetoptError
-from vobject.icalendar import utc
-
from twisted.application.service import Service
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue
@@ -45,6 +42,7 @@
from calendarserver.tap.util import getRootResource
from calendarserver.tools.principals import removeProxy
from calendarserver.tools.util import loadConfig
+from pycalendar.datetime import PyCalendarDateTime
log = Logger()
@@ -289,7 +287,10 @@
if dryrun:
verbose = True
- cutoff = (date.today()-timedelta(days=days)).strftime("%Y%m%dT000000Z")
+ cutoff = PyCalendarDateTime.getToday()
+ cutoff.setDateOnly(False)
+ cutoff.offsetDay(-days)
+ cutoff = cutoff.getText()
PurgeOldEventsService.cutoff = cutoff
PurgeOldEventsService.batchSize = batchSize
PurgeOldEventsService.dryrun = dryrun
@@ -536,7 +537,7 @@
@type event: L{twistedcaldav.ical.Component}
@param when: the cutoff date (anything after which is removed)
- @type when: datetime with tzinfo
+ @type when: PyCalendarDateTime
@param cua: Calendar User Address of principal being purged, to compare
to see if it's the organizer of the event or just an attendee
@@ -547,7 +548,8 @@
@return: one of the 4 constants above to indicate what action to take
"""
- whenDate = when.date()
+ whenDate = when.duplicate()
+ whenDate.setDateOnly(True)
master = event.masterComponent()
@@ -557,14 +559,9 @@
# Anything completely in the future is deleted
dtstart = master.getStartDateUTC()
- if isinstance(dtstart, datetime):
- isDateTime = True
- if dtstart > when:
- return CANCELEVENT_SHOULD_DELETE
- else:
- isDateTime = False
- if dtstart > whenDate:
- return CANCELEVENT_SHOULD_DELETE
+ isDateTime = not dtstart.isDateOnly()
+ if dtstart > when:
+ return CANCELEVENT_SHOULD_DELETE
organizer = master.getOrganizer()
@@ -584,19 +581,15 @@
# Set the UNTIL on RRULE to cease at the cutoff
if master.hasProperty("RRULE"):
for rrule in master.properties("RRULE"):
- tokens = {}
- tokens.update([valuePart.split("=") for valuePart in rrule.value().split(";")])
- if tokens.has_key("COUNT"):
- dirty = True
- del tokens["COUNT"]
+ rrule = rrule.value()
+ if rrule.getUseCount():
+ rrule.setUseCount(False)
+ rrule.setUseUntil(True)
if isDateTime:
- tokens["UNTIL"] = when.strftime("%Y%m%dT%H%M%SZ")
+ rrule.setUntil(when)
else:
- tokens["UNTIL"] = when.strftime("%Y%m%d")
-
- newValue = ";".join(["%s=%s" % (key, value,) for key, value in tokens.iteritems()])
- rrule.setValue(newValue)
+ rrule.setUntil(whenDate)
dirty = True
# Remove any EXDATEs and RDATEs beyond the cutoff
@@ -605,18 +598,14 @@
for exdate_rdate in master.properties(dateType):
newValues = []
for value in exdate_rdate.value():
- if isinstance(value, datetime):
- if value < when:
- newValues.append(value)
+ if value.getValue() < when:
+ newValues.append(value)
else:
- if value < whenDate:
- newValues.append(value)
+ exdate_rdate.value().remove(value)
+ dirty = True
if not newValues:
master.removeProperty(exdate_rdate)
dirty = True
- else:
- exdate_rdate.setValue(newValues)
- dirty = True
# Remove any overridden components beyond the cutoff
@@ -624,12 +613,8 @@
if component.name() == "VEVENT":
dtstart = component.getStartDateUTC()
remove = False
- if isinstance(dtstart, datetime):
- if dtstart > when:
- remove = True
- else:
- if dtstart > whenDate:
- remove = True
+ if dtstart > when:
+ remove = True
if remove:
event.removeComponent(component)
dirty = True
@@ -645,8 +630,7 @@
when=None):
if when is None:
- when = datetime.now(tz=utc)
- # when = datetime(2010, 12, 6, 12, 0, 0, 0, utc)
+ when = PyCalendarDateTime.getNowUTC()
# Does the record exist?
record = directory.recordWithGUID(guid)
@@ -676,7 +660,7 @@
calendarHome = yield principal.calendarHome(request)
# Anything in the past is left alone
- whenString = when.strftime("%Y%m%dT%H%M%SZ")
+ whenString = when.getText()
filter = caldavxml.Filter(
caldavxml.ComponentFilter(
caldavxml.ComponentFilter(
Modified: CalendarServer/trunk/calendarserver/tools/test/test_purge.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_purge.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/calendarserver/tools/test/test_purge.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -15,20 +15,26 @@
##
-from twistedcaldav.ical import Component
from calendarserver.tools.purge import cancelEvent
from calendarserver.tools.purge import CANCELEVENT_MODIFIED, CANCELEVENT_SHOULD_DELETE
-from vobject.icalendar import utc
-from datetime import datetime, timedelta
+from twistedcaldav.ical import Component
from twistedcaldav.test.util import TestCase
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
-future = (datetime.utcnow() + timedelta(days=1)).strftime("%Y%m%dT%H%M%SZ")
-past = (datetime.utcnow() - timedelta(days=1)).strftime("%Y%m%dT%H%M%SZ")
+future = PyCalendarDateTime.getNowUTC()
+future.offsetDay(1)
+future = future.getText()
+
+past = PyCalendarDateTime.getNowUTC()
+past.offsetDay(-1)
+past = past.getText()
+
# For test_purgeExistingGUID
# No organizer/attendee
@@ -218,7 +224,7 @@
def test_cancelRepeating(self):
# A repeating event where purged CUA is organizer
event = Component.fromString(REPEATING_1_ICS_BEFORE)
- action = cancelEvent(event, datetime(2010, 12, 6, 12, 0, 0, 0, utc),
+ action = cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, CANCELEVENT_MODIFIED)
self.assertEquals(str(event), REPEATING_1_ICS_AFTER)
@@ -226,7 +232,7 @@
def test_cancelAllDayRepeating(self):
# A repeating All Day event where purged CUA is organizer
event = Component.fromString(REPEATING_2_ICS_BEFORE)
- action = cancelEvent(event, datetime(2010, 12, 6, 12, 0, 0, 0, utc),
+ action = cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, CANCELEVENT_MODIFIED)
self.assertEquals(str(event), REPEATING_2_ICS_AFTER)
@@ -234,21 +240,21 @@
def test_cancelFutureEvent(self):
# A future event
event = Component.fromString(FUTURE_EVENT_ICS)
- action = cancelEvent(event, datetime(2010, 12, 6, 12, 0, 0, 0, utc),
+ action = cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, CANCELEVENT_SHOULD_DELETE)
def test_cancelNonMeeting(self):
# A repeating non-meeting event
event = Component.fromString(REPEATING_NON_MEETING_ICS)
- action = cancelEvent(event, datetime(2010, 12, 6, 12, 0, 0, 0, utc),
+ action = cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, CANCELEVENT_SHOULD_DELETE)
def test_cancelAsAttendee(self):
# A repeating meeting event where purged CUA is an attendee
event = Component.fromString(REPEATING_ATTENDEE_MEETING_ICS)
- action = cancelEvent(event, datetime(2010, 12, 6, 12, 0, 0, 0, utc),
+ action = cancelEvent(event, PyCalendarDateTime(2010, 12, 6, 12, 0, 0, PyCalendarTimezone(utc=True)),
"urn:uuid:0F168477-CF3D-45D3-AE60-9875EA02C4D1")
self.assertEquals(action, CANCELEVENT_SHOULD_DELETE)
@@ -263,20 +269,20 @@
PRODID:-//Apple Inc.//iCal 4.0.4//EN
BEGIN:VTIMEZONE
TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:59E260E3-1644-4BDF-BBC6-6130B0C3A520
@@ -337,20 +343,20 @@
PRODID:-//Apple Inc.//iCal 4.0.4//EN
BEGIN:VTIMEZONE
TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:59E260E3-1644-4BDF-BBC6-6130B0C3A520
@@ -498,20 +504,20 @@
PRODID:-//Apple Inc.//iCal 4.0.4//EN
BEGIN:VTIMEZONE
TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:97B243D3-D252-4034-AA6D-9AE34E063991
@@ -532,20 +538,20 @@
PRODID:-//Apple Inc.//iCal 4.0.4//EN
BEGIN:VTIMEZONE
TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:4E4D0C8C-6546-4777-9BF5-AD629C05E7D5
@@ -567,20 +573,20 @@
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VTIMEZONE
TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:111A679F-EF8E-4CA5-9262-7C805E2C184D
Modified: CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -17,23 +17,23 @@
"""
Tests for calendarserver.tools.purge
"""
+from calendarserver.tap.util import getRootResource
+from calendarserver.tools.purge import purgeOldEvents, purgeGUID, purgeOrphanedAttachments
-from twisted.trial import unittest
-from twisted.internet.defer import inlineCallbacks, returnValue
from twext.web2.http_headers import MimeType
-from twistedcaldav.vcard import Component as VCardComponent
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.trial import unittest
+from twistedcaldav.config import config
+from twistedcaldav.memcacher import Memcacher
+from twistedcaldav.vcard import Component as VCardComponent
from txdav.common.datastore.test.util import buildStore, populateCalendarsFrom, CommonCommonTests
-from calendarserver.tap.util import getRootResource
-from calendarserver.tools.purge import purgeOldEvents, purgeGUID, purgeOrphanedAttachments
-from twistedcaldav.config import config
-from twistedcaldav.memcacher import Memcacher
-from vobject.icalendar import utc
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
-import datetime
import os
@@ -384,7 +384,7 @@
@inlineCallbacks
def test_eventsOlderThan(self):
- cutoff = datetime.datetime(2010, 4, 1)
+ cutoff = PyCalendarDateTime(2010, 4, 1, 0, 0, 0)
txn = self._sqlCalendarStore.newTransaction()
# Query for all old events
@@ -410,7 +410,7 @@
@inlineCallbacks
def test_removeOldEvents(self):
- cutoff = datetime.datetime(2010, 4, 1)
+ cutoff = PyCalendarDateTime(2010, 4, 1, 0, 0, 0)
txn = self._sqlCalendarStore.newTransaction()
# Remove oldest event
@@ -473,7 +473,7 @@
self.assertTrue(os.path.exists(attachmentPath))
# Delete all old events (including the event containing the attachment)
- cutoff = datetime.datetime(2010, 4, 1)
+ cutoff = PyCalendarDateTime(2010, 4, 1, 0, 0, 0)
count = (yield txn.removeOldEvents(cutoff))
# Just look for orphaned attachments but don't delete
@@ -499,18 +499,18 @@
# Dry run
total = (yield purgeOldEvents(self._sqlCalendarStore, self.directory,
- self.rootResource, datetime.datetime(2010, 4, 1), 2, dryrun=True,
+ self.rootResource, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, dryrun=True,
verbose=False))
self.assertEquals(total, 4)
# Actually remove
total = (yield purgeOldEvents(self._sqlCalendarStore, self.directory,
- self.rootResource, datetime.datetime(2010, 4, 1), 2, verbose=False))
+ self.rootResource, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, verbose=False))
self.assertEquals(total, 4)
# There should be no more left
total = (yield purgeOldEvents(self._sqlCalendarStore, self.directory,
- self.rootResource, datetime.datetime(2010, 4, 1), 2, verbose=False))
+ self.rootResource, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, verbose=False))
self.assertEquals(total, 0)
test_purgeOldEvents.todo = "New lazy indexing broke this"
@@ -537,7 +537,7 @@
# Purge home1
total, ignored = (yield purgeGUID("home1", self.directory,
self.rootResource, verbose=False, proxies=False,
- when=datetime.datetime(2010, 4, 1, 12, 0, 0, 0, utc)))
+ when=PyCalendarDateTime(2010, 4, 1, 12, 0, 0, 0, PyCalendarTimezone(utc=True))))
# 2 items deleted: 1 event and 1 vcard
self.assertEquals(total, 2)
@@ -555,7 +555,7 @@
# Remove old events first
total = (yield purgeOldEvents(self._sqlCalendarStore, self.directory,
- self.rootResource, datetime.datetime(2010, 4, 1), 2, verbose=False))
+ self.rootResource, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, verbose=False))
self.assertEquals(total, 4)
# Dry run
Modified: CalendarServer/trunk/contrib/tools/dtraceanalyze.py
===================================================================
--- CalendarServer/trunk/contrib/tools/dtraceanalyze.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/contrib/tools/dtraceanalyze.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -31,8 +31,10 @@
prefix_maps = {
"/usr/share/caldavd/lib/python/": "{caldavd}/",
+ "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.6": "{Python}",
"/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6": "{Python}",
"/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5": "{Python}",
+ "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python": "{Extras}",
"/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python": "{Extras}",
"/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python": "{Extras}",
}
Modified: CalendarServer/trunk/support/Makefile.Apple
===================================================================
--- CalendarServer/trunk/support/Makefile.Apple 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/support/Makefile.Apple 2011-03-17 18:54:11 UTC (rev 7208)
@@ -39,25 +39,26 @@
# Build
#
-.phony: $(Project) vobject build setup prep install install-ossfiles buildit
+.phony: $(Project) vobject pycalendar build setup prep install install-ossfiles buildit
CalDAVTester:: $(BuildDirectory)/CalDAVTester
PyKerberos:: $(BuildDirectory)/PyKerberos
PyOpenDirectory:: $(BuildDirectory)/PyOpenDirectory
PyXML-0.8.4:: $(BuildDirectory)/PyXML-0.8.4
vobject:: $(BuildDirectory)/vobject
+pycalendar:: $(BuildDirectory)/pycalendar
PyGreSQL-4.0:: $(BuildDirectory)/PyGreSQL-4.0
-sqlparse-0.1.2:: $(BuildDirectory)/sqlparse-0.1.2
+sqlparse-0.1.2:: $(BuildDirectory)/sqlparse-0.1.2
$(Project):: $(BuildDirectory)/$(Project)
-build:: PyKerberos PyOpenDirectory PyXML-0.8.4 vobject PyGreSQL-4.0 sqlparse-0.1.2 $(Project)
+build:: PyKerberos PyOpenDirectory PyXML-0.8.4 vobject pycalendar PyGreSQL-4.0 sqlparse-0.1.2 $(Project)
setup:
$(_v) ./run -g
-prep:: setup CalDAVTester.tgz PyKerberos.tgz PyOpenDirectory.tgz PyXML-0.8.4.tgz vobject.tgz PyGreSQL-4.0.tgz sqlparse-0.1.2.tgz
+prep:: setup CalDAVTester.tgz PyKerberos.tgz PyOpenDirectory.tgz PyXML-0.8.4.tgz vobject.tgz pycalendar.tgz PyGreSQL-4.0.tgz sqlparse-0.1.2.tgz
-PyKerberos PyOpenDirectory PyXML-0.8.4 vobject PyGreSQL-4.0 sqlparse-0.1.2 $(Project)::
+PyKerberos PyOpenDirectory PyXML-0.8.4 vobject pycalendar PyGreSQL-4.0 sqlparse-0.1.2 $(Project)::
@echo "Building $@..."
$(_v) cd $(BuildDirectory)/$@ && $(Environment) $(PYTHON) setup.py build
@@ -72,6 +73,7 @@
$(_v) cd $(BuildDirectory)/PyOpenDirectory && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
$(_v) cd $(BuildDirectory)/PyXML-0.8.4 && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
$(_v) cd $(BuildDirectory)/vobject && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+ $(_v) cd $(BuildDirectory)/pycalendar && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
$(_v) cd $(BuildDirectory)/PyGreSQL-4.0 && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
$(_v) cd $(BuildDirectory)/sqlparse-0.1.2 && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
$(_v) for so in $$(find "$(DSTROOT)$(PY_HOME)/lib" -type f -name '*.so'); do $(STRIP) -Sx "$${so}"; done
Modified: CalendarServer/trunk/support/build.sh
===================================================================
--- CalendarServer/trunk/support/build.sh 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/support/build.sh 2011-03-17 18:54:11 UTC (rev 7208)
@@ -632,6 +632,11 @@
"vobject" "vobject" "vobject" \
"http://svn.osafoundation.org/vobject/trunk";
+ # XXX actually PyCalendar should be imported in-place.
+ py_dependency -fie -r 144 \
+ "pycalendar" "pycalendar" "pycalendar" \
+ "http://svn.mulberrymail.com/repos/PyCalendar/branches/server";
+
#
# Tool dependencies. The code itself doesn't depend on these, but
# they are useful to developers.
Property changes on: CalendarServer/trunk/support/build.sh
___________________________________________________________________
Added: svn:mergeinfo
+ /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593
Modified: CalendarServer/trunk/twext/python/datetime.py
===================================================================
--- CalendarServer/trunk/twext/python/datetime.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twext/python/datetime.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -24,7 +24,6 @@
"dateordatetime",
"timerange",
"asTimeZone",
- "asUTC",
"iCalendarString",
]
@@ -32,7 +31,7 @@
datetime = __import__("datetime").datetime
from vobject.icalendar import dateTimeToString, dateToString
-from vobject.icalendar import utc, getTzid as tzWithID
+from vobject.icalendar import utc
# FIXME, add constants for begining/end of time
@@ -297,21 +296,17 @@
# Convenience functions
##
-def asTimeZone(dateOrDatetime, tzinfo):
+def asTimeZone(pydt, pytz):
"""
- Convert a L{date} or L{datetime} to the given time zone.
+ Convert a L{PyCalendarDateTime} to the given time zone.
"""
- return dateordatetime(dateOrDatetime).asTimeZone(tzinfo).dateOrDatetime()
+ dup = pydt.duplicate()
+ dup.adjustTimezone(pytz)
+ return dup
-def asUTC(dateOrDatetime):
+def iCalendarString(pydt):
"""
- Convert a L{date} or L{datetime} to UTC.
- """
- return dateordatetime(dateOrDatetime).asUTC().dateOrDatetime()
-
-def iCalendarString(dateOrDatetime):
- """
- Convert a L{date} or L{datetime} to a string appropriate for use
+ Convert a L{PyCalendarDateTime} to a string appropriate for use
in an iCalendar property.
"""
- return dateordatetime(dateOrDatetime).iCalendarString()
+ return pydt.getText()
Modified: CalendarServer/trunk/twistedcaldav/caldavxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/caldavxml.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/caldavxml.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -25,16 +25,13 @@
See draft spec: http://ietf.webdav.org/caldav/draft-dusseault-caldav.txt
"""
-import datetime
+from pycalendar.datetime import PyCalendarDateTime
-from vobject.icalendar import utc, TimezoneComponent
-
from twext.web2.dav import davxml
from twext.python.log import Logger
from twistedcaldav.ical import Component as iComponent
-from twistedcaldav.ical import parse_date_or_datetime
log = Logger()
@@ -93,8 +90,8 @@
if "start" not in attributes and "end" not in attributes:
raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range")
- self.start = parse_date_or_datetime(attributes["start"]) if "start" in attributes else None
- self.end = parse_date_or_datetime(attributes["end"]) if "end" in attributes else None
+ self.start = PyCalendarDateTime.parseText(attributes["start"]) if "start" in attributes else None
+ self.end = PyCalendarDateTime.parseText(attributes["end"]) if "end" in attributes else None
def valid(self, level=0):
"""
@@ -103,16 +100,16 @@
@return: True if valid, False otherwise
"""
- if self.start is not None and not isinstance(self.start, datetime.datetime):
+ if self.start is not None and self.start.isDateOnly():
log.msg("start attribute in <time-range> is not a date-time: %s" % (self.start,))
return False
- if self.end is not None and not isinstance(self.end, datetime.datetime):
+ if self.end is not None and self.end.isDateOnly():
log.msg("end attribute in <time-range> is not a date-time: %s" % (self.end,))
return False
- if self.start is not None and self.start.tzinfo != utc:
+ if self.start is not None and not self.start.utc():
log.msg("start attribute in <time-range> is not UTC: %s" % (self.start,))
return False
- if self.end is not None and self.end.tzinfo != utc:
+ if self.end is not None and not self.end.utc():
log.msg("end attribute in <time-range> is not UTC: %s" % (self.end,))
return False
@@ -148,10 +145,7 @@
found = False
for subcomponent in calendar.subcomponents():
- if (
- subcomponent.name() == "VTIMEZONE" and
- isinstance(subcomponent._vobject, TimezoneComponent)
- ):
+ if (subcomponent.name() == "VTIMEZONE"):
if found:
return False
else:
Modified: CalendarServer/trunk/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/customxml.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/customxml.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -31,11 +31,8 @@
from twistedcaldav import caldavxml, carddavxml
from twistedcaldav.ical import Component as iComponent
-from vobject.icalendar import utc
-from vobject.icalendar import dateTimeToString
+from pycalendar.datetime import PyCalendarDateTime
-import datetime
-
calendarserver_namespace = "http://calendarserver.org/ns/"
calendarserver_proxy_compliance = (
@@ -512,7 +509,7 @@
def __init__(self, *children):
super(DTStamp, self).__init__(children)
- self.children = (davxml.PCDATAElement(dateTimeToString(datetime.datetime.now(tz=utc))),)
+ self.children = (davxml.PCDATAElement(PyCalendarDateTime.getNowUTC().getText()),)
class Action (davxml.WebDAVElement):
"""
Modified: CalendarServer/trunk/twistedcaldav/datafilters/calendardata.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/datafilters/calendardata.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/datafilters/calendardata.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -19,6 +19,7 @@
from twistedcaldav.datafilters.filter import CalendarFilter
from twistedcaldav.dateops import clipPeriod
from twistedcaldav.ical import Component
+from pycalendar.period import PyCalendarPeriod
__all__ = [
"CalendarDataFilter",
@@ -156,7 +157,7 @@
for property in component.properties("FREEBUSY"):
newvalue = []
for period in property.value():
- clipped = clipPeriod(period, (self.calendardata.freebusy_set.start, self.calendardata.freebusy_set.end))
+ clipped = clipPeriod(period.getValue(), PyCalendarPeriod(self.calendardata.freebusy_set.start, self.calendardata.freebusy_set.end))
if clipped:
newvalue.append(clipped)
if len(newvalue):
Modified: CalendarServer/trunk/twistedcaldav/datafilters/test/test_calendardata.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/datafilters/test/test_calendardata.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/datafilters/test/test_calendardata.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -94,8 +94,6 @@
""".replace("\n", "\r\n")
result = """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//PYVOBJECT//NONSGML Version 1//EN
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
Modified: CalendarServer/trunk/twistedcaldav/datafilters/test/test_privateevents.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/datafilters/test/test_privateevents.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/datafilters/test/test_privateevents.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -66,6 +66,7 @@
data = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:PUBLIC
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
@@ -74,7 +75,6 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
END:VEVENT
-X-CALENDARSERVER-ACCESS:PUBLIC
END:VCALENDAR
""".replace("\n", "\r\n")
@@ -87,6 +87,7 @@
data = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:PRIVATE
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
@@ -95,7 +96,6 @@
ATTENDEE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
END:VEVENT
-X-CALENDARSERVER-ACCESS:PRIVATE
END:VCALENDAR
""".replace("\n", "\r\n")
@@ -109,6 +109,7 @@
data = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:CONFIDENTIAL
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
@@ -120,19 +121,18 @@
ORGANIZER;CN=User 01:mailto:user1 at example.com
SUMMARY:Confidential
END:VEVENT
-X-CALENDARSERVER-ACCESS:CONFIDENTIAL
END:VCALENDAR
""".replace("\n", "\r\n")
filtered = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:CONFIDENTIAL
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
END:VEVENT
-X-CALENDARSERVER-ACCESS:CONFIDENTIAL
END:VCALENDAR
""".replace("\n", "\r\n")
@@ -145,6 +145,7 @@
data = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:RESTRICTED
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
@@ -156,13 +157,13 @@
ORGANIZER;CN=User 01:mailto:user1 at example.com
SUMMARY:Confidential
END:VEVENT
-X-CALENDARSERVER-ACCESS:RESTRICTED
END:VCALENDAR
""".replace("\n", "\r\n")
filtered = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+X-CALENDARSERVER-ACCESS:RESTRICTED
BEGIN:VEVENT
UID:12345-67890
DTSTART:20080601T120000Z
@@ -170,7 +171,6 @@
LOCATION:My office
SUMMARY:Confidential
END:VEVENT
-X-CALENDARSERVER-ACCESS:RESTRICTED
END:VCALENDAR
""".replace("\n", "\r\n")
Modified: CalendarServer/trunk/twistedcaldav/dateops.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/dateops.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/dateops.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -13,6 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.period import PyCalendarPeriod
+import datetime
+from vobject.icalendar import utc
"""
Date/time Utilities
@@ -30,91 +35,98 @@
]
import calendar
-import datetime
-from vobject.icalendar import utc
-from twext.python.datetime import dateordatetime
-
def normalizeForIndex(dt):
"""
- Normalize a L{datetime.date} or L{datetime.datetime} object for use in the Index.
- If it's a L{datetime.date}, convert to L{datetime.datetime} with HH:MM:SS set to 00:00:00 in UTC.
- If it's a L{datetime.datetime}, convert to UTC.
- @param dt: a L{datetime.date} or L{datetime.datetime} object to normalize
- @return: the normalized date or datetime
+ Normalize a L{PyCalendarDateTime} object for use in the Index.
+ Convert to date-time in UTC.
+ @param dt: a L{PyCalendarDateTime} object to normalize
+ @return: the normalized PyCalendarDateTime
"""
- if not isinstance(dt, datetime.date):
- raise TypeError("%r is not a datetime.date instance" % (dt,))
+ if not isinstance(dt, PyCalendarDateTime):
+ raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
- if isinstance(dt, datetime.datetime):
- if dt.tzinfo is not None:
- return dt.astimezone(utc)
- else:
- return dt
+ dt = dt.duplicate()
+ if dt.isDateOnly():
+ dt.setDateOnly(False)
+ dt.setHHMMSS(0, 0, 0)
+ dt.setTimezoneID(None) # Keep it floating
+ return dt
+ elif dt.floating():
+ return dt
else:
- return datetime.datetime.fromordinal(dt.toordinal())
+ dt.adjustToUTC()
+ return dt
def normalizeToUTC(dt):
"""
- Normalize a L{datetime.date} or L{datetime.datetime} object to UTC.
+ Normalize a L{PyCalendarDateTime} object to UTC.
"""
- if not isinstance(dt, datetime.date):
- raise TypeError("%r is not a datetime.date instance" % (dt,))
+ if not isinstance(dt, PyCalendarDateTime):
+ raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
- if isinstance(dt, datetime.datetime):
- if dt.tzinfo is not None:
- return dt.astimezone(utc)
- else:
- return dt.replace(tzinfo=utc)
+ dt = dt.duplicate()
+ if dt.isDateOnly():
+ dt.setDateOnly(False)
+ dt.setHHMMSS(0, 0, 0)
+ dt.setTimezoneUTC(True)
+ return dt
+ elif dt.floating():
+ dt.setTimezoneUTC(True)
+ return dt
else:
- return datetime.datetime.fromordinal(dt.toordinal()).replace(tzinfo=utc)
+ dt.adjustToUTC()
+ return dt
-def floatoffset(dt, tzinfo):
+def floatoffset(dt, pytz):
"""
Apply the timezone offset to the supplied time, then force tz to utc. This gives the local
date-time as if the local tz were UTC. It can be used in floating time comparisons with UTC date-times.
- @param dt: a L{datetime.datetime} object to normalize
- @param tzinfo: a L{datetime.tzinfo} object to apply offset from
- @return: the normalized datetime
+ @param dt: a L{PyCalendarDateTime} object to normalize
+ @param pytz: a L{PyCalendarTimezone} object to apply offset from
+ @return: the normalized PyCalendarDateTime
"""
- if tzinfo is None:
- tzinfo = utc
- return dt.astimezone(tzinfo).replace(tzinfo=utc)
+ if pytz is None:
+ pytz = PyCalendarTimezone(utc=True)
+
+ dt = dt.duplicate()
+ dt.adjustTimezone(pytz)
+ dt.setTimezoneUTC(True)
+ return dt
+def adjustFloatingToTimezone(dtadjust, dtcopyfrom, pytz=None):
+
+ dtadjust = dtadjust.duplicate()
+ dtadjust.setTimezone(pytz if pytz else dtcopyfrom.getTimezone())
+ return dtadjust
+
def compareDateTime(dt1, dt2, defaulttz=None):
- dt1 = dateordatetime(dt1, defaultTZ=defaulttz)
- dt2 = dateordatetime(dt2, defaultTZ=defaulttz)
- if dt1 == dt2:
- return 0
- elif dt1 < dt2:
- return -1
- else:
- return 1
+
+ if dt1.floating() and not dt2.floating():
+ dt1 = adjustFloatingToTimezone(dt1, dt2, defaulttz)
+ elif dt2.floating() and not dt1.floating():
+ dt2 = adjustFloatingToTimezone(dt2, dt1, defaulttz)
+
+ return dt1.compareDateTime(dt2)
def differenceDateTime(start, end, defaulttz = None):
- return dateordatetime(end, defaultTZ=defaulttz) - dateordatetime(start)
-#def timeRangesOverlap(start1, end1, start2, end2, defaulttz = None):
-# def dodt(d):
-# if d is None:
-# return None
-# else:
-# return dateordatetime(d, defaulttz)
-#
-# dodt1 = timerange(dodt(start1), dodt(end1))
-# dodt2 = timerange(dodt(start2), dodt(end2))
-#
-# return dodt1.overlapsWith(dodt2)
+ if start.floating() and not end.floating():
+ start = adjustFloatingToTimezone(start, end, defaulttz)
+ elif end.floating() and not start.floating():
+ end = adjustFloatingToTimezone(end, start, defaulttz)
+ return end - start
+
def timeRangesOverlap(start1, end1, start2, end2, defaulttz = None):
- # Can't compare datetime.date and datetime.datetime objects, so normalize
- # to date if they are mixed.
- if isinstance(start1, datetime.datetime) and (start2 is not None) and not isinstance(start2, datetime.datetime): start1 = start1.date()
- if isinstance(start2, datetime.datetime) and (start1 is not None) and not isinstance(start1, datetime.datetime): start2 = start2.date()
- if isinstance(end1, datetime.datetime) and (end2 is not None) and not isinstance(end2, datetime.datetime): end1 = end1.date()
- if isinstance(end2, datetime.datetime) and (end1 is not None) and not isinstance(end1, datetime.datetime): end2 = end2.date()
+ # Can't compare date-time and date only, so normalize
+ # to date only if they are mixed.
+ if (start1 is not None) and not start1.isDateOnly() and (start2 is not None) and start2.isDateOnly(): start1 = start1.setDateOnly(True)
+ if (start2 is not None) and not start2.isDateOnly() and (start1 is not None) and start1.isDateOnly(): start2 = start2.setDateOnly(True)
+ if (end1 is not None) and not end1.isDateOnly() and (end2 is not None) and end2.isDateOnly(): end1 = end1.setDateOnly(True)
+ if (end2 is not None) and not end2.isDateOnly() and (end1 is not None) and end1.isDateOnly(): end2 = end2.setDateOnly(True)
# Note that start times are inclusive and end times are not.
if start1 is not None and start2 is not None:
@@ -133,83 +145,61 @@
else:
return False
-def periodEnd(p):
+def normalizePeriodList(periods):
"""
- Calculate the end datetime of the period. Since a period is a
- tuple consisting of a pair of L{datetime.datetime}'s, or one
- L{datetime.datetime} and one L{datetime.timedelta}, we may need
- to add the duration to the start to get the actual end.
- @param p: the period whose end is to be determined.
- @return: the L{datetime.datetime} for the end.
- """
- assert len(p) == 2, "Period is not a tuple of two items: %r" % (p,)
- assert isinstance(p[0], datetime.datetime), "Period start is not a datetime: %r" % (p,)
- assert isinstance(p[1], datetime.datetime) or isinstance(p[1], datetime.timedelta), "Period end is not a datetime or timedelta: %r" % (p,)
-
- if isinstance(p[1], datetime.timedelta):
- return p[0] + p[1]
- else:
- return p[1]
-
-def normalizePeriodList(list):
- """
Normalize the list of periods by merging overlapping or consecutive ranges
and sorting the list by each periods start.
- @param list: a list of tuples of L{datetime.datetime} pairs. The list is changed in place.
+ @param list: a list of tuples of L{PyCalendarPeriod}. The list is changed in place.
"""
# First sort the list
def sortPeriods(p1, p2):
"""
Compare two periods. Sort by their start and then end times.
- A period is a tuple consisting of a pair of L{datetime.datetime}'s, or one
- L{datetime.datetime} and one L{datetime.timedelta}.
+ A period is a L{PyCalendarPeriod}.
@param p1: first period
@param p2: second period
@return: 1 if p1>p2, 0 if p1==p2, -1 if p1<p2
"""
- assert len(p1) == 2, "Period is not a tuple of two items: %r" % (p1,)
- assert isinstance(p1[0], datetime.datetime), "Period start is not a datetime: %r" % (p1,)
- assert isinstance(p1[1], datetime.datetime) or isinstance(p1[1], datetime.timedelta), "Period end is not a datetime or timedelta: %r" % (p1,)
+ assert isinstance(p1, PyCalendarPeriod), "Period is not a PyCalendarPeriod: %r" % (p1,)
+ assert isinstance(p2, PyCalendarPeriod), "Period is not a PyCalendarPeriod: %r" % (p2,)
- assert len(p2) == 2, "Period is not a tuple of two items: %r" % (p2,)
- assert isinstance(p2[0], datetime.datetime), "Period start is not a datetime: %r" % (p2,)
- assert isinstance(p2[1], datetime.datetime) or isinstance(p2[1], datetime.timedelta), "Period end is not a datetime or timedelta: %r" % (p2,)
-
- if p1[0] == p2[0]:
- cmp1 = periodEnd(p1)
- cmp2 = periodEnd(p2)
+ if p1.getStart() == p2.getStart():
+ cmp1 = p1.getEnd()
+ cmp2 = p2.getEnd()
else:
- cmp1 = p1[0]
- cmp2 = p2[0]
+ cmp1 = p1.getStart()
+ cmp2 = p2.getStart()
return compareDateTime(cmp1, cmp2)
- list.sort(cmp=sortPeriods)
+ for period in periods:
+ period.adjustToUTC()
+ periods.sort(cmp=sortPeriods)
# Now merge overlaps and consecutive periods
index = None
p = None
pe = None
- for i in xrange(len(list)):
+ for i in xrange(len(periods)):
if p is None:
index = i
- p = list[i]
- pe = periodEnd(p)
+ p = periods[i]
+ pe = p.getEnd()
continue
- ie = periodEnd(list[i])
- if (pe >= list[i][0]):
+ ie = periods[i].getEnd()
+ if (pe >= periods[i].getStart()):
if ie > pe:
- list[index] = (list[index][0], ie)
+ periods[index] = PyCalendarPeriod(periods[index].getStart(), ie)
pe = ie
- list[i] = None
+ periods[i] = None
else:
index = i
- p = list[i]
- pe = periodEnd(p)
- list[:] = [x for x in list if x]
+ p = periods[i]
+ pe =p.getEnd()
+ periods[:] = [x for x in periods if x]
def clipPeriod(period, clipPeriod):
"""
@@ -219,10 +209,10 @@
@return: the (start, end) tuple for the clipped period, or
None if the period is outside the clip period
"""
- start = period[0]
- end = periodEnd(period)
- clipStart = clipPeriod[0]
- clipEnd = periodEnd(clipPeriod)
+ start = period.getStart()
+ end = period.getEnd()
+ clipStart = clipPeriod.getStart()
+ clipEnd = clipPeriod.getEnd()
if start < clipStart:
start = clipStart
@@ -234,11 +224,25 @@
return None
else:
# Try to preserve use of duration in period
- if isinstance(period[1], datetime.timedelta):
- return (start, end - start)
- else:
- return (start, end)
+ result = PyCalendarPeriod(start, end)
+ result.setUseDuration(period.getUseDuration())
+ return result
+def pyCalendarTodatetime(pydt):
+
+ if pydt.isDateOnly():
+ return datetime.date(year=pydt.getYear(), month=pydt.getMonth(), day=pydt.getDay())
+ else:
+ return datetime.datetime(
+ year=pydt.getYear(),
+ month=pydt.getMonth(),
+ day=pydt.getDay(),
+ hour=pydt.getHours(),
+ minute=pydt.getMinutes(),
+ second=pydt.getSeconds(),
+ tzinfo=utc
+ )
+
SQL_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
def parseSQLTimestamp(ts):
@@ -247,6 +251,18 @@
ts += ".0"
return datetime.datetime.strptime(ts, SQL_TIMESTAMP_FORMAT)
+def parseSQLTimestampToPyCalendar(ts):
+ """
+ Parse an SQL formated timestamp into a PyCalendarDateTime
+ @param ts: the SQL timestamp
+ @type ts: C{str}
+
+ @return: L{PyCalendarDateTime} result
+ """
+
+ dt = datetime.datetime.strptime(ts[:19], "%Y-%m-%d %H:%M:%S")
+ return PyCalendarDateTime(year=dt.year, month=dt.month, day=dt.day, hours=dt.hour, minutes=dt.minute, seconds=dt.second)
+
def datetimeMktime(dt):
assert isinstance(dt, datetime.date)
Modified: CalendarServer/trunk/twistedcaldav/freebusyurl.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/freebusyurl.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/freebusyurl.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -22,10 +22,6 @@
"FreeBusyURLResource",
]
-import datetime
-
-from vobject.icalendar import utc
-
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twext.python.log import Logger
@@ -44,14 +40,15 @@
from twistedcaldav.config import config
from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.ical import Property
-from twistedcaldav.ical import parse_datetime
-from twistedcaldav.ical import parse_duration
from twistedcaldav.resource import CalDAVResource, ReadOnlyNoCopyResourceMixIn
from twistedcaldav.schedule import deliverSchedulePrivilegeSet
from twistedcaldav.scheduling.caldav import ScheduleViaCalDAV
from twistedcaldav.scheduling.cuaddress import LocalCalendarUser
from twistedcaldav.scheduling.scheduler import Scheduler
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
+
log = Logger()
@@ -181,15 +178,15 @@
# Start/end/duration must be valid iCalendar DATE-TIME UTC or DURATION values
try:
if self.start:
- self.start = parse_datetime(self.start)
- if self.start.tzinfo != utc:
+ self.start = PyCalendarDateTime.parseText(self.start)
+ if not self.start.utc():
raise ValueError()
if self.end:
- self.end = parse_datetime(self.end)
- if self.end.tzinfo != utc:
+ self.end = PyCalendarDateTime.parseText(self.end)
+ if not self.end.utc():
raise ValueError()
if self.duration:
- self.duration = parse_duration(self.duration)
+ self.duration = PyCalendarDuration.parseText(self.duration)
except ValueError:
raise HTTPError(ErrorResponse(
responsecode.BAD_REQUEST,
@@ -208,7 +205,7 @@
))
# Duration must be positive
- if self.duration and self.duration.days < 0:
+ if self.duration and self.duration.getTotalSeconds() < 0:
raise HTTPError(ErrorResponse(
responsecode.BAD_REQUEST,
(calendarserver_namespace, "valid-query-parameters"),
@@ -217,12 +214,12 @@
# Now fill in the missing pieces
if self.start is None:
- now = datetime.datetime.now()
- self.start = now.replace(hour=0, minute=0, second=0, tzinfo=utc)
+ self.start = PyCalendarDateTime.getNowUTC()
+ self.start.setHHMMSS(0, 0, 0)
if self.duration:
self.end = self.start + self.duration
if self.end is None:
- self.end = self.start + datetime.timedelta(days=config.FreeBusyURL.TimePeriod)
+ self.end = self.start + PyCalendarDuration(days=config.FreeBusyURL.TimePeriod)
# End > start
if self.end <= self.start:
Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/ical.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -14,6 +14,7 @@
# limitations under the License.
##
+
"""
iCalendar Utilities
"""
@@ -24,26 +25,15 @@
"allowedComponents",
"Property",
"Component",
- "FixedOffset",
- "parse_date",
- "parse_time",
- "parse_datetime",
- "parse_date_or_datetime",
- "parse_duration",
"tzexpand",
]
import cStringIO as StringIO
-import datetime
+import codecs
import heapq
import itertools
-from vobject import newFromBehavior, readComponents
-from vobject.base import Component as vComponent, ContentLine as vContentLine, ParseError as vParseError
-from vobject.icalendar import TimezoneComponent, dateTimeToString, deltaToOffset, getTransition, stringToDate, stringToDateTime, stringToDurations, utc
-
from twext.python.log import Logger
-from twext.python.datetime import dateordatetime, timerange, asUTC, iCalendarString
from twext.web2.stream import IStream
from twext.web2.dav.util import allDataFromStream
@@ -51,6 +41,17 @@
from twistedcaldav.instance import InstanceList
from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
+from pycalendar import definitions
+from pycalendar.attribute import PyCalendarAttribute
+from pycalendar.calendar import PyCalendar
+from pycalendar.componentbase import PyCalendarComponentBase
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.exceptions import PyCalendarInvalidData
+from pycalendar.period import PyCalendarPeriod
+from pycalendar.property import PyCalendarProperty
+from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
log = Logger()
@@ -81,7 +82,7 @@
"GEO": (None, {"VALUE": "FLOAT"}),
"LOCATION": (None, {"VALUE": "TEXT"}),
"PERCENT-COMPLETE": (None, {"VALUE": "INTEGER"}),
- "PRIORITY": ("0", {"VALUE": "INTEGER"}),
+ "PRIORITY": (0, {"VALUE": "INTEGER"}),
"RESOURCES": (None, {"VALUE": "TEXT"}),
"STATUS": (None, {"VALUE": "TEXT"}),
"SUMMARY": (None, {"VALUE": "TEXT"}),
@@ -116,12 +117,12 @@
"RDATE": (None, {"VALUE": "DATE-TIME"}),
"RRULE": (None, {"VALUE": "RECUR"}),
"ACTION": (None, {"VALUE": "TEXT"}),
- "REPEAT": ("0", {"VALUE": "INTEGER"}),
+ "REPEAT": (0, {"VALUE": "INTEGER"}),
"TRIGGER": (None, {"VALUE": "DURATION"}),
"CREATED": (None, {"VALUE": "DATE-TIME"}),
"DTSTAMP": (None, {"VALUE": "DATE-TIME"}),
"LAST-MODIFIED": (None, {"VALUE": "DATE-TIME"}),
- "SEQUENCE": ("0", {"VALUE": "INTEGER"}),
+ "SEQUENCE": (0, {"VALUE": "INTEGER"}),
"REQUEST-STATUS": (None, {"VALUE": "TEXT"}),
}
@@ -151,18 +152,19 @@
assert value is None
assert params is None
- vobj = kwargs["vobject"]
+ pyobj = kwargs["pycalendar"]
- if not isinstance(vobj, vContentLine):
- raise TypeError("Not a vContentLine: %r" % (property,))
+ if not isinstance(pyobj, PyCalendarProperty):
+ raise TypeError("Not a PyCalendarProperty: %r" % (property,))
- self._vobject = vobj
+ self._pycalendar = pyobj
else:
- # Convert params dictionary to list of lists format used by vobject
- lparams = [[key] + lvalue for key, lvalue in params.items()]
- self._vobject = vContentLine(name, lparams, value, isNative=True)
+ # Convert params dictionary to list of lists format used by pycalendar
+ self._pycalendar = PyCalendarProperty(name, value)
+ for attrname, attrvalue in params.items():
+ self._pycalendar.addAttribute(PyCalendarAttribute(attrname, attrvalue))
- def __str__ (self): return self._vobject.serialize()
+ def __str__ (self): return str(self._pycalendar)
def __repr__(self): return "<%s: %r: %r>" % (self.__class__.__name__, self.name(), self.value())
def __hash__(self):
@@ -171,7 +173,7 @@
def __ne__(self, other): return not self.__eq__(other)
def __eq__(self, other):
if not isinstance(other, Property): return False
- return self.name() == other.name() and self.value() == other.value() and self.params() == other.params()
+ return self._pycalendar == other._pycalendar
def __gt__(self, other): return not (self.__eq__(other) or self.__lt__(other))
def __lt__(self, other):
@@ -186,44 +188,66 @@
def __ge__(self, other): return self.__eq__(other) or self.__gt__(other)
def __le__(self, other): return self.__eq__(other) or self.__lt__(other)
- def name (self): return self._vobject.name
+ def name (self): return self._pycalendar.getName()
- def value (self): return self._vobject.value
+ def value (self): return self._pycalendar.getValue().getValue()
+
+ def strvalue (self): return str(self._pycalendar.getValue())
+
def setValue(self, value):
- self._vobject.value = value
+ self._pycalendar.setValue(value)
- def params(self):
+ def parameterNames(self):
"""
- Returns a mapping object containing parameters for this property.
-
- Keys are parameter names, values are sequences containing
- values for the named parameter.
+ Returns a set containing parameter names for this property.
"""
- return self._vobject.params
+ result = set()
+ for pyattrlist in self._pycalendar.getAttributes().values():
+ for pyattr in pyattrlist:
+ result.add(pyattr.getName())
+ return result
- def paramValue(self, name):
+ def parameterValue(self, name, default=None):
"""
Returns a single value for the given parameter. Raises
InvalidICalendarDataError if the parameter has more than one value.
"""
- values = self._vobject.params.get(name, [None,])
- assert type(values) is list, "vobject returned non-list value for parameter %r in property %r" % (name, self)
- if len(values) != 1:
- raise InvalidICalendarDataError("Not exactly one %s value in property %r" % (name, self))
- return values[0]
+ try:
+ return self._pycalendar.getAttributeValue(name)
+ except KeyError:
+ return default
- def containsTimeRange(self, start, end, tzinfo=None):
+ def hasParameter(self, paramname):
+ return self._pycalendar.hasAttribute(paramname)
+
+ def setParameter(self, paramname, paramvalue):
+ self._pycalendar.replaceAttribute(PyCalendarAttribute(paramname, paramvalue))
+
+ def removeParameter(self, paramname):
+ self._pycalendar.removeAttributes(paramname)
+
+ def removeAllParameters(self):
+ self._pycalendar.setAttributes({})
+
+ def removeParameterValue(self, paramname, paramvalue):
+
+ for attr in tuple(self._pycalendar.getAttributes()):
+ if attr.getName() == paramname:
+ for value in attr.getValues():
+ if value == paramvalue:
+ if not attr.removeValue(value):
+ self._pycalendar.removeAttributes(paramname)
+
+ def containsTimeRange(self, start, end, defaulttz=None):
"""
Determines whether this property contains a date/date-time within the specified
start/end period.
The only properties allowed for this query are: COMPLETED, CREATED, DTSTAMP and
LAST-MODIFIED (caldav -09).
- @param start: a L{datetime.datetime} or L{datetime.date} specifying the
- beginning of the given time span.
- @param end: a L{datetime.datetime} or L{datetime.date} specifying the
- end of the given time span. C{end} may be None, indicating that
- there is no end date.
- @param tzinfo: the default L{datetime.tzinfo} to use in datetime comparisons.
+ @param start: a L{PyCalendarDateTime} specifying the beginning of the given time span.
+ @param end: a L{PyCalendarDateTime} specifying the end of the given time span.
+ C{end} may be None, indicating that there is no end date.
+ @param defaulttz: the default L{PyTimezone} to use in datetime comparisons.
@return: True if the property's date/date-time value is within the given time range,
False if not, or the property is not an appropriate date/date-time value.
"""
@@ -234,24 +258,11 @@
return False
# get date/date-time value
- dt = self.value()
- assert isinstance(dt, datetime.date), "Not a date/date-time value: %r" % (self,)
+ dt = self._pycalendar.getValue().getValue()
+ assert isinstance(dt, PyCalendarDateTime), "Not a date/date-time value: %r" % (self,)
- return timeRangesOverlap(dt, None, start, end, tzinfo)
+ return timeRangesOverlap(dt, None, start, end, defaulttz)
- def transformAllFromNative(self):
- transformed = self._vobject.isNative
- if transformed:
- self._vobject = self._vobject.transformFromNative()
- self._vobject.transformChildrenFromNative()
- return transformed
-
- def transformAllToNative(self):
- transformed = not self._vobject.isNative
- if transformed:
- self._vobject = self._vobject.transformToNative()
- self._vobject.transformChildrenToNative()
- return transformed
class Component (object):
"""
@@ -323,6 +334,14 @@
"""
if type(string) is unicode:
string = string.encode("utf-8")
+ else:
+ # Valid utf-8 please
+ string.decode("utf-8")
+
+ # No BOMs please
+ if string[:3] == codecs.BOM_UTF8:
+ string = string[3:]
+
return clazz.fromStream(StringIO.StringIO(string))
@classmethod
@@ -333,15 +352,15 @@
@return: a L{Component} representing the first component described by
C{stream}.
"""
+ cal = PyCalendar()
try:
- return clazz(None, vobject=readComponents(stream, findBegin=False).next())
- except UnicodeDecodeError, e:
+ result = cal.parse(stream)
+ except PyCalendarInvalidData:
+ result = None
+ if not result:
stream.seek(0)
- raise InvalidICalendarDataError("%s: %s" % (e, stream.read()))
- except vParseError, e:
- raise InvalidICalendarDataError(e)
- except StopIteration, e:
- raise InvalidICalendarDataError(e)
+ raise InvalidICalendarDataError("%s" % (stream.read(),))
+ return clazz(None, pycalendar=cal)
@classmethod
def fromIStream(clazz, stream):
@@ -369,14 +388,14 @@
component.
"""
if name is None:
- if "vobject" in kwargs:
- vobj = kwargs["vobject"]
+ if "pycalendar" in kwargs:
+ pyobj = kwargs["pycalendar"]
- if vobj is not None:
- if not isinstance(vobj, vComponent):
- raise TypeError("Not a vComponent: %r" % (vobj,))
+ if pyobj is not None:
+ if not isinstance(pyobj, PyCalendarComponentBase):
+ raise TypeError("Not a PyCalendarComponentBase: %r" % (pyobj,))
- self._vobject = vobj
+ self._pycalendar = pyobj
else:
raise AssertionError("name may not be None")
@@ -393,59 +412,37 @@
else:
self._parent = None
else:
- self._vobject = newFromBehavior(name)
+ # FIXME: figure out creating an arbitrary component
+ self._pycalendar = PyCalendar(add_defaults=False) if name == "VCALENDAR" else PyCalendar.makeComponent(name, None)
self._parent = None
- def __str__ (self): return self._vobject.serialize()
- def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, str(self._vobject))
+ def __str__ (self):
+ return str(self._pycalendar)
+ def __repr__(self):
+ return "<%s: %r>" % (self.__class__.__name__, str(self._pycalendar))
+
def __hash__(self):
return hash(str(self))
- def __ne__(self, other): return not self.__eq__(other)
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
def __eq__(self, other):
if not isinstance(other, Component):
return False
+ return self._pycalendar == other._pycalendar
- my_properties = set(self.properties())
- for property in other.properties():
- if property in my_properties:
- my_properties.remove(property)
- else:
- return False
- if my_properties:
- return False
-
- my_subcomponents = set(self.subcomponents())
- for subcomponent in other.subcomponents():
- for testcomponent in my_subcomponents:
- if subcomponent == testcomponent:
- my_subcomponents.remove(testcomponent)
- break
- else:
- return False
- if my_subcomponents:
- return False
-
- return True
-
# FIXME: Should this not be in __eq__?
def same(self, other):
- return self._vobject == other._vobject
+ return self._pycalendar == other._pycalendar
def name(self):
"""
@return: the name of the iCalendar type of this component.
"""
- return self._vobject.name
+ return self._pycalendar.getType()
- def setBehavior(self, behavior):
- """
- Set the behavior of the underlying iCal object.
- @param behavior: the behavior type to set.
- """
- self._vobject.setBehavior(behavior)
-
def mainType(self):
"""
Determine the primary type of iCal component in this calendar.
@@ -507,20 +504,20 @@
Return the overridden iCal component in this calendar matching the supplied RECURRENCE-ID property.
@param recurrence_id: The RECURRENCE-ID property value to match.
- @type recurrence_id: L{datetime.datetime} or L{datetime.date}
+ @type recurrence_id: L{PyCalendarDateTime}
@return: the L{Component} for the overridden component,
or C{None} if there isn't one.
"""
assert self.name() == "VCALENDAR", "Must be a VCALENDAR: %r" % (self,)
if isinstance(recurrence_id, str):
- recurrence_id = parse_date_or_datetime(recurrence_id) if recurrence_id else None
+ recurrence_id = PyCalendarDateTime.parseText(recurrence_id) if recurrence_id else None
for component in self.subcomponents():
if component.name() in ignoredComponents:
continue
rid = component.getRecurrenceIDUTC()
- if rid and recurrence_id and dateordatetime(rid) == recurrence_id:
+ if rid and recurrence_id and rid == recurrence_id:
return component
elif rid is None and recurrence_id is None:
return component
@@ -544,7 +541,7 @@
Duplicate this object and all its contents.
@return: the duplicated calendar.
"""
- return Component(None, vobject=vComponent.duplicate(self._vobject))
+ return Component(None, pycalendar=self._pycalendar.duplicate())
def subcomponents(self):
"""
@@ -552,9 +549,8 @@
of this component.
"""
return (
- Component(None, vobject=c, parent=self)
- for c in self._vobject.getChildren()
- if isinstance(c, vComponent)
+ Component(None, pycalendar=c, parent=self)
+ for c in self._pycalendar.getComponents()
)
def addComponent(self, component):
@@ -563,7 +559,7 @@
@param component: the L{Component} to add as a subcomponent of this
component.
"""
- self._vobject.add(component._vobject)
+ self._pycalendar.addComponent(component._pycalendar)
component._parent = self
def removeComponent(self, component):
@@ -571,17 +567,14 @@
Removes a subcomponent from this component.
@param component: the L{Component} to remove.
"""
- self._vobject.remove(component._vobject)
+ self._pycalendar.removeComponent(component._pycalendar)
def hasProperty(self, name):
"""
@param name: the name of the property whose existence is being tested.
@return: True if the named property exists, False otherwise.
"""
- try:
- return len(self._vobject.contents[name.lower()]) > 0
- except KeyError:
- return False
+ return self._pycalendar.hasProperty(name)
def getProperty(self, name):
"""
@@ -602,18 +595,15 @@
@return: an iterable of L{Property} objects, one for each property of
this component.
"""
+ properties = []
if name is None:
- properties = self._vobject.getChildren()
- else:
- try:
- properties = self._vobject.contents[name.lower()]
- except KeyError:
- return ()
+ [properties.extend(i) for i in self._pycalendar.getProperties().values()]
+ elif self._pycalendar.countProperty(name) > 0:
+ properties = self._pycalendar.getProperties(name)
return (
- Property(None, None, None, vobject=p)
+ Property(None, None, None, pycalendar=p)
for p in properties
- if isinstance(p, vContentLine)
)
def propertyValue(self, name):
@@ -625,42 +615,15 @@
return None
- def propertyNativeValue(self, name):
- """
- Return the native property value for the named property in the supplied component.
- NB Assumes a single property exists in the component.
- @param name: the name of the property whose value is required
- @return: the native property value
- """
- properties = tuple(self.properties(name))
-
- if len(properties) == 1:
- transormed = properties[0].transformAllToNative()
-
- result = properties[0].value()
-
- if transormed:
- properties[0].transformAllFromNative()
-
- return result
-
- elif len(properties) > 1:
- raise InvalidICalendarDataError("More than one %s property in component %r" % (name, self))
- else:
- return None
-
def getStartDateUTC(self):
"""
Return the start date or date-time for the specified component
converted to UTC.
@param component: the Component whose start should be returned.
- @return: the datetime.date or datetime.datetime for the start.
+ @return: the L{PyCalendarDateTime} for the start.
"""
- dtstart = self.propertyNativeValue("DTSTART")
- if dtstart is not None:
- return asUTC(dtstart)
- else:
- return None
+ dtstart = self.propertyValue("DTSTART")
+ return dtstart.duplicateAsUTC() if dtstart is not None else None
def getEndDateUTC(self):
"""
@@ -668,51 +631,41 @@
taking into account the presence or absence of DTEND/DURATION properties.
The returned date-time is converted to UTC.
@param component: the Component whose end should be returned.
- @return: the datetime.date or datetime.datetime for the end.
+ @return: the L{PyCalendarDateTime} for the end.
"""
- dtend = self.propertyNativeValue("DTEND")
+ dtend = self.propertyValue("DTEND")
if dtend is None:
- dtstart = self.propertyNativeValue("DTSTART")
- duration = self.propertyNativeValue("DURATION")
+ dtstart = self.propertyValue("DTSTART")
+ duration = self.propertyValue("DURATION")
if duration is not None:
dtend = dtstart + duration
- if dtend is not None:
- return asUTC(dtend)
- else:
- return None
+ return dtend.duplicateAsUTC() if dtend is not None else None
def getDueDateUTC(self):
"""
Return the due date or date-time for the specified component
converted to UTC. Use DTSTART/DURATION if no DUE property.
@param component: the Component whose start should be returned.
- @return: the datetime.date or datetime.datetime for the start.
+ @return: the L{PyCalendarDateTime} for the start.
"""
- due = self.propertyNativeValue("DUE")
+ due = self.propertyValue("DUE")
if due is None:
- dtstart = self.propertyNativeValue("DTSTART")
- duration = self.propertyNativeValue("DURATION")
+ dtstart = self.propertyValue("DTSTART")
+ duration = self.propertyValue("DURATION")
if dtstart is not None and duration is not None:
due = dtstart + duration
- if due is not None:
- return asUTC(due)
- else:
- return None
+ return due.duplicateAsUTC() if due is not None else None
def getRecurrenceIDUTC(self):
"""
Return the recurrence-id for the specified component.
@param component: the Component whose r-id should be returned.
- @return: the datetime.date or datetime.datetime for the r-id.
+ @return: the L{PyCalendarDateTime} for the r-id.
"""
- rid = self.propertyNativeValue("RECURRENCE-ID")
-
- if rid is not None:
- return asUTC(rid)
- else:
- return None
+ rid = self.propertyValue("RECURRENCE-ID")
+ return rid.duplicateAsUTC() if rid is not None else None
def getRange(self):
"""
@@ -722,7 +675,7 @@
"""
ridprop = self.getProperty("RECURRENCE-ID")
if ridprop is not None:
- range = ridprop.paramValue("RANGE")
+ range = ridprop.parameterValue("RANGE")
if range is not None:
return (range == "THISANDFUTURE")
@@ -733,7 +686,7 @@
Return the trigger information for the specified alarm component.
@param component: the Component whose start should be returned.
@return: ta tuple consisting of:
- trigger : the 'native' trigger value (either datetime.date or datetime.timedelta)
+ trigger : the 'native' trigger value
related : either True (for START) or False (for END)
repeat : an integer for the REPEAT count
duration: the repeat duration if present, otherwise None
@@ -741,38 +694,33 @@
assert self.name() == "VALARM", "Component is not a VAlARM: %r" % (self,)
# The trigger value
- trigger = self.propertyNativeValue("TRIGGER")
+ trigger = self.propertyValue("TRIGGER")
if trigger is None:
raise InvalidICalendarDataError("VALARM has no TRIGGER property: %r" % (self,))
# The related parameter
- related = self.getProperty("TRIGGER").paramValue("RELATED")
+ related = self.getProperty("TRIGGER").parameterValue("RELATED")
if related is None:
related = True
else:
related = (related == "START")
# Repeat property
- repeat = self.propertyNativeValue("REPEAT")
+ repeat = self.propertyValue("REPEAT")
if repeat is None: repeat = 0
else: repeat = int(repeat)
# Duration property
- duration = self.propertyNativeValue("DURATION")
+ duration = self.propertyValue("DURATION")
if repeat > 0 and duration is None:
raise InvalidICalendarDataError("VALARM has invalid REPEAT/DURATIOn properties: %r" % (self,))
return (trigger, related, repeat, duration)
- def getRRuleSet(self, addRDate = False):
- self.transformAllToNative()
- return self._vobject.getrruleset(addRDate)
+ def getRecurrenceSet(self):
+ return self._pycalendar.getRecurrenceSet()
- def setRRuleSet(self, rruleset):
- #self.transformAllToNative()
- return self._vobject.setrruleset(rruleset)
-
def getEffectiveStartEnd(self):
# Get the start/end range needed for instance comparisons
@@ -808,14 +756,16 @@
Adds a property to this component.
@param property: the L{Property} to add to this component.
"""
- self._vobject.add(property._vobject)
+ self._pycalendar.addProperty(property._pycalendar)
+ self._pycalendar.finalise()
def removeProperty(self, property):
"""
Remove a property from this component.
@param property: the L{Property} to remove from this component.
"""
- self._vobject.remove(property._vobject)
+ self._pycalendar.removeProperty(property._pycalendar)
+ self._pycalendar.finalise()
def replaceProperty(self, property):
"""
@@ -824,8 +774,7 @@
"""
# Remove all existing ones first
- for removeit in tuple(self.properties(property.name())):
- self.removeProperty(removeit)
+ self._pycalendar.removeProperties(property.name())
self.addProperty(property)
def timezoneIDs(self):
@@ -837,20 +786,10 @@
result = set()
for property in self.properties():
- for propertyname in ("TZID", "X-VOBJ-ORIGINAL-TZID"):
- tzid = property.paramValue(propertyname)
- if tzid is not None:
- result.add(tzid)
- break
- else:
- items = property.value()
- if type(items) is not list:
- items = [items]
- for item in items:
- tzinfo = getattr(item, 'tzinfo', None)
- tzid = TimezoneComponent.pickTzid(tzinfo)
- if tzid is not None:
- result.add(tzid)
+ tzid = property.parameterValue("TZID")
+ if tzid is not None:
+ result.add(tzid)
+ break
return result
@@ -879,47 +818,48 @@
@type maximumCount: C{int}
@return: a C{bool} indicating whether a change was made or not
"""
-
+
changed = False
master = self.masterComponent()
if master and master.isRecurring():
- rrules = master.getRRuleSet()
+ rrules = master._pycalendar.getRecurrenceSet()
if rrules:
- for rrule in rrules._rrule:
- if rrule._count is not None:
+ for rrule in rrules.getRules():
+ if rrule.getUseCount():
# Make sure COUNT is less than the limit
- if rrule._count > maximumCount:
- rrule._count = maximumCount
+ if rrule.getCount() > maximumCount:
+ rrule.setCount(maximumCount)
changed = True
- elif rrule._until is not None:
+ elif rrule.getUseUntil():
# Need to figure out how to determine number of instances
# with this UNTIL and truncate if needed
start = master.getStartDateUTC()
- diff = differenceDateTime(start, rrule._until)
- diff = diff.days * 24 * 60 * 60 + diff.seconds
+ diff = differenceDateTime(start, rrule.getUntil())
+ diff = diff.getDays() * 24 * 60 * 60 + diff.getSeconds()
period = {
- 0: 365 * 24 * 60 * 60,
- 1: 30 * 24 * 60 * 60,
- 2: 7 * 24 * 60 * 60,
- 3: 1 * 24 * 60 * 60,
- 4: 60 * 60,
- 5: 60,
- 6: 1
- }[rrule._freq] * rrule._interval
+ definitions.eRecurrence_YEARLY: 365 * 24 * 60 * 60,
+ definitions.eRecurrence_MONTHLY: 30 * 24 * 60 * 60,
+ definitions.eRecurrence_WEEKLY: 7 * 24 * 60 * 60,
+ definitions.eRecurrence_DAILY: 1 * 24 * 60 * 60,
+ definitions.eRecurrence_HOURLY: 60 * 60,
+ definitions.eRecurrence_MINUTELY: 60,
+ definitions.eRecurrence_SECONDLY: 1
+ }[rrule.getFreq()] * rrule.getInterval()
if diff / period > maximumCount:
- rrule._until = None
- rrule._count = maximumCount
+ rrule.setUseUntil(False)
+ rrule.setUseCount(True)
+ rrule.setCount(maximumCount)
+ rrules.changed()
changed = True
else:
# For frequencies other than yearly we will truncate at our limit
- if rrule._freq != 0:
- rrule._count = maximumCount
+ if rrule.getFreq() != definitions.eRecurrence_YEARLY:
+ rrule.setUseCount(True)
+ rrule.setCount(maximumCount)
+ rrules.changed()
changed = True
-
- if changed:
- master.setRRuleSet(rrules)
return changed
@@ -928,13 +868,13 @@
Expand the components into a set of new components, one for each
instance in the specified range. Date-times are converted to UTC. A
new calendar object is returned.
- @param start: the L{datetime.datetime} for the start of the range.
- @param end: the L{datetime.datetime} for the end of the range.
+ @param start: the L{PyCalendarDateTime} for the start of the range.
+ @param end: the L{PyCalendarDateTime} for the end of the range.
@param timezone: the L{Component} the VTIMEZONE to use for floating/all-day.
@return: the L{Component} for the new calendar with expanded instances.
"""
- tzinfo = timezone.gettzinfo() if timezone else None
+ pytz = PyCalendarTimezone(tzid=timezone.propertyValue("TZID")) if timezone else None
# Create new calendar object with same properties as the original, but
# none of the originals sub-components
@@ -949,7 +889,7 @@
first = True
for key in instances:
instance = instances[key]
- if timeRangesOverlap(instance.start, instance.end, start, end, tzinfo):
+ if timeRangesOverlap(instance.start, instance.end, start, end, pytz):
calendar.addComponent(self.expandComponent(instance, first))
first = False
@@ -974,8 +914,8 @@
# Convert all datetime properties to UTC unless they are floating
for property in newcomp.properties():
value = property.value()
- if isinstance(value, datetime.datetime) and value.tzinfo is not None:
- property.setValue(value.astimezone(utc))
+ if isinstance(value, PyCalendarDateTime) and value.local():
+ property.setValue(value.duplicateAsUTC())
# Now reset DTSTART, DTEND/DURATION
for property in newcomp.properties("DTSTART"):
@@ -988,7 +928,6 @@
# Add RECURRENCE-ID if not first instance
if not first:
newcomp.addProperty(Property("RECURRENCE-ID", instance.rid))
- newcomp.transformAllToNative()
return newcomp
@@ -998,7 +937,7 @@
so we can return cached results in the future.
@param limit: the max datetime to cache up to.
- @type limit: L{datetime.datetime} or L{datetime.date}
+ @type limit: L{PyCalendarDateTime}
"""
# Checked for cached values first
@@ -1018,7 +957,7 @@
contained within this VCALENDAR component. We will assume
that this component has already been validated as a CalDAV resource
(i.e. only one type of component, all with the same UID)
- @param limit: datetime.date value representing the end of the expansion.
+ @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
@param ignoreInvalidInstances: C{bool} whether to ignore instance errors.
@return: a set of Instances for each recurrence in the set.
"""
@@ -1032,7 +971,7 @@
What we do is first expand the master instance into the set of generate
instances. Then we merge the overridden instances, taking into account
THISANDFUTURE and THISANDPRIOR.
- @param limit: datetime.date value representing the end of the expansion.
+ @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
@param componentSet: the set of components that are to make up the
recurrence set. These MUST all be components with the same UID
and type, forming a proper recurring set.
@@ -1087,8 +1026,7 @@
if master:
rrules = master.properties("RRULE")
for rrule in rrules:
- s = str(rrule)
- if "COUNT" not in s and "UNTIL" not in s:
+ if not rrule.value().getUseCount() and not rrule.value().getUseUntil():
return True
return False
@@ -1100,7 +1038,7 @@
is added as STATUS:CANCELLED and the EXDATE removed.
@param rid: recurrence-id value
- @type rid: L{datetime.datetime}
+ @type rid: L{PyCalendarDateTime}
@param allowCancelled: whether to allow a STATUS:CANCELLED override
@type allowCancelled: C{bool}
@@ -1117,7 +1055,7 @@
didCancel = False
for exdate in tuple(master.properties("EXDATE")):
for exdateValue in exdate.value():
- if exdateValue == rid:
+ if exdateValue.getValue() == rid:
if allowCancelled:
exdate.value().remove(exdateValue)
if len(exdate.value()) == 0:
@@ -1135,13 +1073,13 @@
# Check whether recurrence-id matches an RDATE - if so it is OK
rdates = set()
for rdate in master.properties("RDATE"):
- rdates.update([asUTC(item) for item in rdate.value()])
+ rdates.update([item.getValue().duplicateAsUTC() for item in rdate.value()])
if rid not in rdates:
# Check whether we have a truncated RRULE
rrules = master.properties("RRULE")
if len(tuple(rrules)):
- limit = rid
- limit += datetime.timedelta(days=365)
+ limit = rid.duplicate()
+ limit += PyCalendarDuration(days=365)
instances = self.cacheExpandedTimeRanges(limit)
rids = set([instances[key].rid for key in instances])
instance_rid = normalizeForIndex(rid)
@@ -1166,28 +1104,26 @@
if newcomp.hasProperty("DTEND"):
dtend = newcomp.getProperty("DTEND")
oldduration = dtend.value() - dtstart.value()
-
- if isinstance(dtstart.value(), datetime.datetime):
- if dtstart.value().tzinfo:
- newdtstartValue = rid.astimezone(dtstart.value().tzinfo)
- else:
- newdtstartValue = rid
+
+ newdtstartValue = rid.duplicate()
+ if not dtstart.value().isDateOnly():
+ if dtstart.value().local():
+ newdtstartValue.adjustTimezone(dtstart.value().getTimezone())
else:
- newdtstartValue = datetime.date.fromordinal(rid.toordinal())
+ newdtstartValue.setDateOnly(True)
dtstart.setValue(newdtstartValue)
if newcomp.hasProperty("DTEND"):
dtend.setValue(newdtstartValue + oldduration)
- try:
- rid_params = {"X-VOBJ-ORIGINAL-TZID":dtstart.params()["X-VOBJ-ORIGINAL-TZID"]}
- except KeyError:
- rid_params = {}
- newcomp.addProperty(Property("RECURRENCE-ID", dtstart.value(), params=rid_params))
+ newcomp.addProperty(Property("RECURRENCE-ID", dtstart.value(), params={}))
if didCancel:
newcomp.addProperty(Property("STATUS", "CANCELLED"))
+ # After creating/changing a component we need to do this to keep PyCalendar happy
+ newcomp._pycalendar.finalise()
+
return newcomp
def validInstances(self, rids):
@@ -1204,7 +1140,7 @@
non_master_rids = [rid for rid in rids if rid is not None]
if non_master_rids:
highest_rid = max(non_master_rids)
- self.cacheExpandedTimeRanges(highest_rid + datetime.timedelta(days=1))
+ self.cacheExpandedTimeRanges(highest_rid + PyCalendarDuration(days=1))
for rid in rids:
if self.validInstance(rid, clear_cache=False):
valid.add(rid)
@@ -1215,7 +1151,7 @@
Test whether the specified recurrence-id is a valid instance in this event.
@param rid: recurrence-id value
- @type rid: L{datetime.datetime}
+ @type rid: L{PyCalendarDateTime}
@return: C{bool}
"""
@@ -1231,7 +1167,7 @@
return False
# Get expansion
- instances = self.cacheExpandedTimeRanges(rid + datetime.timedelta(days=1))
+ instances = self.cacheExpandedTimeRanges(rid + PyCalendarDuration(days=1))
new_rids = set([instances[key].rid for key in instances])
return rid in new_rids
@@ -1406,28 +1342,25 @@
# If they're not both date or both date-time, raise error
if (subcomponent.hasProperty("DTSTART") and
subcomponent.hasProperty("RRULE")):
- dtValue = subcomponent.propertyNativeValue("DTSTART")
- dtType = type(dtValue)
+ dtValue = subcomponent.propertyValue("DTSTART")
+ dtutc = dtValue.duplicateAsUTC()
# Using properties("RRULE") rather than getRRuleSet() here
# because the dateutil rrule's _until values are datetime
# even if the UNTIL is a date (and therefore we can't
# check validity without doing the following):
- for rrule in subcomponent.properties("RRULE"):
- indexedTokens = {}
- indexedTokens.update([valuePart.split("=")
- for valuePart in rrule.value().split(";")])
- until = indexedTokens.get("UNTIL", None)
- if until:
- untilType = datetime.date if len(until) == 8 else datetime.datetime
- if untilType is not dtType:
+ rrules = subcomponent._pycalendar.getRecurrenceSet()
+ for rrule in rrules.getRules():
+ if rrule.getUseUntil():
+ if rrule.getUntil().isDateOnly() ^ dtValue.isDateOnly():
msg = "Calendar resources must have matching type for DTSTART and UNTIL"
log.debug(msg)
if fix:
- rrules = subcomponent.getRRuleSet()
- if rrules:
- log.debug("Fixing mismatch")
- # vobject fixes DTSTART/UNTIL mismatches
- subcomponent.setRRuleSet(rrules)
+ log.debug("Fixing mismatch")
+ rrule.getUntil().setDateOnly(dtValue.isDateOnly())
+ if not dtValue.isDateOnly():
+ rrule.getUntil().setHHMMSS(dtutc.getHours(), dtutc.getMinutes(), dtutc.getSeconds())
+ rrule.getUntil().setTimezone(PyCalendarTimezone(utc=True))
+ rrules.changed()
else:
raise InvalidICalendarDataError(msg)
@@ -1492,26 +1425,18 @@
return foundOrganizer
- def transformAllFromNative(self):
- self._vobject = self._vobject.transformFromNative()
- self._vobject.transformChildrenFromNative(False)
-
- def transformAllToNative(self):
- self._vobject = self._vobject.transformToNative()
- self._vobject.transformChildrenToNative()
-
- def gettzinfo(self):
+ def gettimezone(self):
"""
- Get the tzinfo for a Timezone component.
+ Get the PyCalendarTimezone for a Timezone component.
- @return: L{datetime.tzinfo} if this is a VTIMEZONE, otherwise None.
+ @return: L{PyCalendarTimezone} if this is a VTIMEZONE, otherwise None.
"""
if self.name() == "VTIMEZONE":
- return self._vobject.gettzinfo()
+ return PyCalendarTimezone(tzid=self._pycalendar.getID())
elif self.name() == "VCALENDAR":
for component in self.subcomponents():
if component.name() == "VTIMEZONE":
- return component.gettzinfo()
+ return component.gettimezone()
else:
return None
else:
@@ -1639,8 +1564,8 @@
is_server = False
organizerProp = self.getOrganizerProperty()
- if "SCHEDULE-AGENT" in organizerProp.params():
- if organizerProp.paramValue("SCHEDULE-AGENT") == "SERVER":
+ if organizerProp.hasParameter("SCHEDULE-AGENT"):
+ if organizerProp.parameterValue("SCHEDULE-AGENT") == "SERVER":
is_server = True
else:
is_server = True
@@ -1691,8 +1616,8 @@
for attendee in tuple(self.properties("ATTENDEE")):
if onlyScheduleAgentServer:
- if "SCHEDULE-AGENT" in attendee.params():
- if attendee.paramValue("SCHEDULE-AGENT") != "SERVER":
+ if attendee.hasParameter("SCHEDULE-AGENT"):
+ if attendee.parameterValue("SCHEDULE-AGENT") != "SERVER":
continue
cuaddr = attendee.value()
@@ -1810,7 +1735,7 @@
continue
for property in component.properties(propname):
if propvalue is None or property.value() == propvalue:
- property.params()[paramname] = [paramvalue]
+ property.setParameter(paramname, paramvalue)
def hasPropertyInAnyComponent(self, properties):
"""
@@ -1980,8 +1905,8 @@
found_all_attendees = False
break
if onlyScheduleAgentServer:
- if "SCHEDULE-AGENT" in foundAttendee.params():
- if foundAttendee.paramValue("SCHEDULE-AGENT") != "SERVER":
+ if foundAttendee.hasParameter("SCHEDULE-AGENT"):
+ if foundAttendee.parameterValue("SCHEDULE-AGENT") != "SERVER":
found_all_attendees = False
break
if not found_all_attendees:
@@ -2019,7 +1944,7 @@
remaining -= 1
continue
rid = component.getRecurrenceIDUTC()
- if (iCalendarString(rid) if rid else "") not in rids:
+ if (rid.getText() if rid else "") not in rids:
self.removeComponent(component)
remaining -= 1
@@ -2093,9 +2018,9 @@
if xpname and p.name() not in keep_properties:
self.removeProperty(p)
elif not xpname and remove_x_parameters:
- for paramname in tuple(p.params()):
+ for paramname in p.parameterNames():
if paramname.startswith("X-"):
- del p.params()[paramname]
+ p.removeParameter(paramname)
def removePropertyParameters(self, property, params):
"""
@@ -2111,10 +2036,7 @@
props = self.properties(property)
for prop in props:
for param in params:
- try:
- del prop.params()[param]
- except KeyError:
- pass
+ prop.removeParameter(param)
def removePropertyParametersByValue(self, property, paramvalues):
"""
@@ -2130,14 +2052,7 @@
props = self.properties(property)
for prop in props:
for param, value in paramvalues:
- try:
- prop.params()[param].remove(value)
- if len(prop.params()[param]) == 0:
- del prop.params()[param]
- except KeyError:
- pass
- except InvalidICalendarDataError:
- pass
+ prop.removeParameterValue(param, value)
def normalizeAll(self):
@@ -2152,13 +2067,14 @@
default_params = {"VALUE": "TEXT"}
# Remove any default parameters
- for name, value in prop.params().items():
- if value == [default_params.get(name),]:
- del prop.params()[name]
+ for name in prop.parameterNames():
+ value = prop.parameterValue(name)
+ if value == default_params.get(name):
+ prop.removeParameter(name)
# If there are no parameters, remove the property if it has the default value
- if len(prop.params()) == 0:
- if prop.value() == default_value:
+ if len(prop.parameterNames()) == 0:
+ if default_value is not None and prop.value() == default_value:
self.removeProperty(prop)
continue
@@ -2193,48 +2109,54 @@
dtend = self.getProperty("DTEND")
duration = self.getProperty("DURATION")
- timeRange = timerange(
+ timeRange = PyCalendarPeriod(
start = dtstart.value(),
end = dtend.value() if dtend is not None else None,
duration = duration.value() if duration is not None else None,
)
- dtstart.setValue(timeRange.start().asUTC().dateOrDatetime())
- if "X-VOBJ-ORIGINAL-TZID" in dtstart.params():
- dtstart.params()["ORIGINAL-TZID"] = dtstart.params()["X-VOBJ-ORIGINAL-TZID"]
- del dtstart.params()["X-VOBJ-ORIGINAL-TZID"]
+ # Have to fake the TZID value here when we convert date-times to UTC
+ # as we need to know what the original one was
+ if dtstart.hasParameter("TZID"):
+ dtstart.setParameter("_TZID", dtstart.parameterValue("TZID"))
+ dtstart.removeParameter("TZID")
+ dtstart.value().adjustToUTC()
if dtend is not None:
- dtend.setValue(timeRange.end().asUTC().dateOrDatetime())
- if "X-VOBJ-ORIGINAL-TZID" in dtend.params():
- dtend.params()["ORIGINAL-TZID"] = dtend.params()["X-VOBJ-ORIGINAL-TZID"]
- del dtend.params()["X-VOBJ-ORIGINAL-TZID"]
+ if dtend.hasParameter("TZID"):
+ dtend.setParameter("_TZID", dtend.parameterValue("TZID"))
+ dtend.removeParameter("TZID")
+ dtend.value().adjustToUTC()
elif duration is not None:
self.removeProperty(duration)
- self.addProperty(Property("DTEND", timeRange.end().asUTC().dateOrDatetime()))
+ self.addProperty(Property("DTEND", timeRange.getEnd().duplicateAsUTC()))
+ rdates = self.properties("RDATE")
+ for rdate in rdates:
+ if rdate.hasParameter("TZID"):
+ rdate.setParameter("_TZID", rdate.parameterValue("TZID"))
+ rdate.removeParameter("TZID")
+ for value in rdate.value():
+ value.getValue().adjustToUTC()
+
exdates = self.properties("EXDATE")
for exdate in exdates:
- exdate.setValue([asUTC(value) for value in exdate.value()])
- try:
- del exdate.params()["TZID"]
- except KeyError:
- pass
+ if exdate.hasParameter("TZID"):
+ exdate.setParameter("_TZID", exdate.parameterValue("TZID"))
+ exdate.removeParameter("TZID")
+ for value in exdate.value():
+ value.getValue().adjustToUTC()
rid = self.getProperty("RECURRENCE-ID")
if rid is not None:
- rid.setValue(asUTC(rid.value()))
- try:
- del rid.params()["TZID"]
- except KeyError:
- pass
+ rid.removeParameter("TZID")
+ rid.setValue(rid.value().duplicateAsUTC())
# Recurrence rules - we need to normalize the order of the value parts
- rrules = self.properties("RRULE")
- for rrule in rrules:
- indexedTokens = {}
- indexedTokens.update([valuePart.split("=") for valuePart in rrule.value().split(";")])
- sortedValue = ";".join(["%s=%s" % (key, value,) for key, value in sorted(indexedTokens.iteritems(), key=lambda x:x[0])])
- rrule.setValue(sortedValue)
+# for rrule in self._pycalendar.getRecurrenceSet().getRules():
+# indexedTokens = {}
+# indexedTokens.update([valuePart.split("=") for valuePart in rrule.value().split(";")])
+# sortedValue = ";".join(["%s=%s" % (key, value,) for key, value in sorted(indexedTokens.iteritems(), key=lambda x:x[0])])
+# rrule.setValue(sortedValue)
def normalizePropertyValueLists(self, propname):
"""
@@ -2252,7 +2174,7 @@
if type(prop.value()) is list and len(prop.value()) > 1:
self.removeProperty(prop)
for value in prop.value():
- self.addProperty(Property(propname, [value,]))
+ self.addProperty(Property(propname, [value.getValue(),]))
def normalizeAttachments(self):
"""
@@ -2269,7 +2191,7 @@
if dropboxPrefix is None:
return
for attachment in tuple(self.properties("ATTACH")):
- valueType = attachment.paramValue("VALUE")
+ valueType = attachment.parameterValue("VALUE")
if valueType in (None, "URI"):
dataValue = attachment.value()
if dataValue.find(dropboxPrefix) != -1:
@@ -2298,7 +2220,7 @@
continue
# Get any EMAIL parameter
- oldemail = prop.params().get("EMAIL", (None,))[0]
+ oldemail = prop.parameterValue("EMAIL")
if oldemail:
oldemail = "mailto:%s" % (oldemail,)
@@ -2341,12 +2263,9 @@
# Always re-write the CN parameter
if name:
- prop.params()["CN"] = [name,]
+ prop.setParameter("CN", name)
else:
- try:
- del prop.params()["CN"]
- except KeyError:
- pass
+ prop.removeParameter("CN")
# Re-write the EMAIL if its value no longer matches
if oldemail and oldemail not in cuaddrs or oldemail is None and toUUID:
@@ -2361,12 +2280,9 @@
email = None
if email:
- prop.params()["EMAIL"] = [email,]
+ prop.setParameter("EMAIL", email)
else:
- try:
- del prop.params()["EMAIL"]
- except KeyError:
- pass
+ prop.removeParameter("EMAIL")
def allPerUserUIDs(self):
@@ -2412,86 +2328,6 @@
return tuple(results)
##
-# Dates and date-times
-##
-
-class FixedOffset (datetime.tzinfo):
- """
- Fixed offset in minutes east from UTC.
- """
- def __init__(self, offset, name=None):
- self._offset = datetime.timedelta(minutes=offset)
- self._name = name
-
- def utcoffset(self, dt): return self._offset
- def tzname (self, dt): return self._name
- def dst (self, dt): return datetime.timedelta(0)
-
-def parse_date(date_string):
- """
- Parse an iCalendar-format DATE string. (RFC 2445, section 4.3.4)
- @param date_string: an iCalendar-format DATE string.
- @return: a L{datetime.date} object for the given C{date_string}.
- """
- try:
- return stringToDate(date_string)
- except (vParseError, InvalidICalendarDataError):
- raise InvalidICalendarDataError("Invalid iCalendar DATE: %r" % (date_string,))
-
-def parse_time(time_string):
- """
- Parse iCalendar-format TIME string. (RFC 2445, section 4.3.12)
- @param time_string: an iCalendar-format TIME string.
- @return: a L{datetime.time} object for the given C{time_string}.
- """
- try:
- # Parse this as a fake date-time string by prepending date
- with_date = "20000101T" + time_string
- return stringToDateTime(with_date).time()
- except (vParseError, InvalidICalendarDataError):
- raise InvalidICalendarDataError("Invalid iCalendar TIME: %r" % (time_string,))
-
-def parse_datetime(datetime_string):
- """
- Parse iCalendar-format DATE-TIME string. (RFC 2445, section 4.3.5)
- @param datetime_string: an iCalendar-format DATE-TIME string.
- @return: a L{datetime.datetime} object for the given C{datetime_string}.
- """
- try:
- return stringToDateTime(datetime_string)
- except (vParseError, InvalidICalendarDataError):
- raise InvalidICalendarDataError("Invalid iCalendar DATE-TIME: %r" % (datetime_string,))
-
-def parse_date_or_datetime(date_string):
- """
- Parse iCalendar-format DATE or DATE-TIME string. (RFC 2445, sections 4.3.4
- and 4.3.5)
- @param date_string: an iCalendar-format DATE or DATE-TIME string.
- @return: a L{datetime.date} or L{datetime.datetime} object for the given
- C{date_string}.
- """
- try:
- if len(date_string) == 8:
- return parse_date(date_string)
- else:
- return parse_datetime(date_string)
- except InvalidICalendarDataError:
- raise InvalidICalendarDataError("Invalid iCalendar DATE or DATE-TIME: %r" % (date_string,))
-
-def parse_duration(duration_string):
- """
- Parse iCalendar-format DURATION string. (RFC 2445, sections 4.3.6)
- @param duration_string: an iCalendar-format DURATION string.
- @return: a L{datetime.timedelta} object for the given C{duration_string}.
- """
- try:
- return stringToDurations(duration_string)[0]
- except (vParseError, InvalidICalendarDataError):
- raise InvalidICalendarDataError("Invalid iCalendar DURATION: %r" % (duration_string,))
-
-_regex_duration = None
-
-##
# Timezones
##
@@ -2510,8 +2346,6 @@
@return: a C{list} of tuples of (C{datetime}, C{str})
"""
- start = datetime.datetime.fromordinal(start.toordinal())
- end = datetime.datetime.fromordinal(end.toordinal())
icalobj = Component.fromString(tzdata)
tzcomp = None
for comp in icalobj.subcomponents():
@@ -2521,52 +2355,23 @@
else:
raise InvalidICalendarDataError("No VTIMEZONE component in %s" % (tzdata,))
- tzinfo = tzcomp.gettzinfo()
+ tzexpanded = tzcomp._pycalendar.expandAll(start, end)
results = []
- # Get the start utc-offset - that is our first value
- results.append((dateTimeToString(start), deltaToOffset(tzinfo.utcoffset(start)),))
- last_dt = start
+ # Always need to ensure the start appears in the result
+ start.setDateOnly(False)
+ if tzexpanded:
+ if start != tzexpanded[0][0]:
+ results.append((str(start), PyCalendarUTCOffsetValue(tzexpanded[0][1]).getText(),))
+ else:
+ results.append((str(start), PyCalendarUTCOffsetValue(tzcomp._pycalendar.getTimezoneOffsetSeconds(start)).getText(),))
+ for tzstart, _ignore_tzoffsetfrom, tzoffsetto in tzexpanded:
+ results.append((
+ tzstart.getText(),
+ PyCalendarUTCOffsetValue(tzoffsetto).getText(),
+ ))
- while last_dt < end:
- # Get the transitions for the current year
- standard = getTransition("standard", last_dt.year, tzinfo)
- daylight = getTransition("daylight", last_dt.year, tzinfo)
-
- # Order the transitions
- if standard and daylight:
- if standard < daylight:
- first = standard
- second = daylight
- else:
- first = daylight
- second = standard
- elif standard:
- first = standard
- second = None
- else:
- first = daylight
- second = None
-
- for transition in (first, second):
- # Terminate if the next transition is outside the time range
- if transition and transition > end:
- break
-
- # If the next transition is after the last one, then add its info if
- # the utc-offset actually changed.
- if transition and transition > last_dt:
- utcoffset = deltaToOffset(tzinfo.utcoffset(transition + datetime.timedelta(days=1)))
- if utcoffset != results[-1][1]:
- results.append((dateTimeToString(transition), utcoffset,))
- last_dt = transition
-
- # Bump last transition up to the start of the next year
- last_dt = datetime.datetime(last_dt.year + 1, 1, 1, 0, 0, 0)
- if last_dt >= end:
- break
-
return results
##
Modified: CalendarServer/trunk/twistedcaldav/instance.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/instance.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/instance.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -18,14 +18,13 @@
iCalendar Recurrence Expansion Utilities
"""
-import datetime
+from twistedcaldav.dateops import normalizeForIndex, differenceDateTime
-from vobject.icalendar import utc
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.period import PyCalendarPeriod
+from pycalendar.timezone import PyCalendarTimezone
-from twext.python.datetime import dateordatetime
-
-from twistedcaldav.dateops import normalizeForIndex, differenceDateTime, periodEnd
-
# The maximum number of instances we will expand out to.
# Raise a TooManyInstancesError exception if we exceed this.
max_allowed_instances = 1000
@@ -48,22 +47,11 @@
class Instance(object):
- __slots__ = ["component", "start", "end", "rid", "overridden", "future"]
-
def __init__(self, component, start = None, end = None, rid = None, overridden = False, future = False):
self.component = component
- if start is None:
- self.start = component.getStartDateUTC()
- else:
- self.start = start
- if end is None:
- self.end = component.getEndDateUTC()
- else:
- self.end = end
- if rid is None:
- self.rid = self.start
- else:
- self.rid = rid
+ self.start = component.getStartDateUTC() if start is None else start
+ self.end = component.getEndDateUTC() if end is None else end
+ self.rid = self.start if rid is None else rid
self.overridden = overridden
self.future = future
@@ -78,28 +66,25 @@
(trigger, related, repeat, duration) = alarm.getTriggerDetails()
# Handle relative vs absolute triggers
- if isinstance(trigger, datetime.date):
+ if isinstance(trigger, PyCalendarDateTime):
# Absolute trigger
start = trigger
else:
# Relative trigger
- if related:
- start = self.start + trigger
- else:
- start = self.end + trigger
+ start = (self.start if related else self.end) + trigger
triggers.add(start)
# Handle repeats
if repeat > 0:
- for i in xrange(1, repeat+1):
- triggers.add(start + (duration * i))
+ tstart = start.duplicate()
+ for _ignore in xrange(1, repeat+1):
+ tstart += duration
+ triggers.add(tstart)
return triggers
class InstanceList(object):
- __slots__ = ["instances", "limit", "ignoreInvalidInstances",]
-
def __init__(self, ignoreInvalidInstances=False):
self.instances = {}
self.limit = None
@@ -122,7 +107,7 @@
@param componentSet: the set of components that are to make up the
recurrence set. These MUST all be components with the same UID
and type, forming a proper recurring set.
- @param limit: datetime.date value representing the end of the expansion.
+ @param limit: L{PyCalendarDateTime} value representing the end of the expansion.
"""
# Look at each component type
@@ -185,26 +170,27 @@
Add the specified master VEVENT Component to the instance list, expanding it
within the supplied time range.
@param component: the Component to expand
- @param limit: the end datetime.datetime for expansion
+ @param limit: the end L{PyCalendarDateTime} for expansion
"""
start = component.getStartDateUTC()
if start is None:
return
+ rulestart = component.propertyValue("DTSTART")
end = component.getEndDateUTC()
duration = None
if end is None:
- if isinstance(start, datetime.datetime):
+ if not start.isDateOnly():
# Timed event with zero duration
- duration = datetime.timedelta(days=0)
+ duration = PyCalendarDuration(days=0)
else:
# All day event default duration is one day
- duration = datetime.timedelta(days=1)
+ duration = PyCalendarDuration(days=1)
end = start + duration
else:
duration = differenceDateTime(start, end)
- self._addMasterComponent(component, limit, start, end, duration)
+ self._addMasterComponent(component, limit, rulestart, start, end, duration)
def _addOverrideEventComponent(self, component, limit, got_master):
"""
@@ -223,15 +209,13 @@
end = component.getEndDateUTC()
duration = None
if end is None:
- if isinstance(start, datetime.datetime):
+ if not start.isDateOnly():
# Timed event with zero duration
- duration = datetime.timedelta(days=0)
+ duration = PyCalendarDuration(days=0)
else:
# All day event default duration is one day
- duration = datetime.timedelta(days=1)
+ duration = PyCalendarDuration(days=1)
end = start + duration
- else:
- duration = differenceDateTime(start, end)
self._addOverrideComponent(component, limit, start, end, got_master)
@@ -240,7 +224,7 @@
Add the specified master VTODO Component to the instance list, expanding it
within the supplied time range.
@param component: the Component to expand
- @param limit: the end datetime.datetime for expansion
+ @param limit: the end L{PyCalendarDateTime} for expansion
"""
start = component.getStartDateUTC()
due = component.getDueDateUTC()
@@ -248,13 +232,15 @@
if start is None and due is None:
return
+ rulestart = component.propertyValue("DTSTART")
if start is None:
start = due
+ rulestart = component.propertyValue("DUE")
elif due is None:
due = start
duration = differenceDateTime(start, due)
- self._addMasterComponent(component, limit, start, due, duration)
+ self._addMasterComponent(component, limit, rulestart, start, due, duration)
def _addOverrideToDoComponent(self, component, limit, got_master):
"""
@@ -279,35 +265,27 @@
self._addOverrideComponent(component, limit, start, due, got_master)
- def _addMasterComponent(self, component, limit, start, end, duration):
- # Always add first instance if included in range.
- if dateordatetime(start) < limit:
- # dateutils does not do all-day - so convert to datetime.datetime
- start = normalizeForIndex(start)
- end = normalizeForIndex(end)
-
- # Do not add if in EXDATEs
- exdates = []
- for prop in component.properties("EXDATE"):
- exdates.extend(prop.value())
- exdates = [normalizeForIndex(exdate) for exdate in exdates]
- if start not in exdates:
- self.addInstance(Instance(component, start, end))
- else:
- self.limit = limit
+ def _addMasterComponent(self, component, limit, rulestart, start, end, duration):
- # Now expand recurrence
- # FIXME: Current Python implementation fails when RDATEs are PERIODs
- recur = component.getRRuleSet(True)
- if recur is not None:
- for startDate in recur:
- if dateordatetime(startDate) >= limit:
- self.limit = limit
- break
- endDate = startDate + duration
+ rrules = component.getRecurrenceSet()
+ if rrules is not None:
+ # Do recurrence set expansion
+ expanded = []
+ limited = rrules.expand(rulestart, PyCalendarPeriod(start, limit), expanded)
+ for startDate in expanded:
startDate = normalizeForIndex(startDate)
- endDate = normalizeForIndex(endDate)
+ endDate = startDate + duration
self.addInstance(Instance(component, startDate, endDate))
+ if limited:
+ self.limit = limit
+ else:
+ # Always add main instance if included in range.
+ if start < limit:
+ start = normalizeForIndex(start)
+ end = normalizeForIndex(end)
+ self.addInstance(Instance(component, start, end))
+ else:
+ self.limit = limit
def _addOverrideComponent(self, component, limit, start, end, got_master):
@@ -321,12 +299,12 @@
rid = normalizeForIndex(rid)
# Make sure start is within the limit
- if dateordatetime(start) > limit and dateordatetime(rid) > limit:
+ if start > limit and rid > limit:
return
# Make sure override RECURRENCE-ID is a valid instance of the master
if got_master:
- if str(rid) not in self.instances and dateordatetime(rid) < limit:
+ if str(rid) not in self.instances and rid < limit:
if self.ignoreInvalidInstances:
return
else:
@@ -371,11 +349,11 @@
Add the specified master VFREEBUSY Component to the instance list, expanding it
within the supplied time range.
@param component: the Component to expand
- @param limit: the end datetime.datetime for expansion
+ @param limit: the end L{PyCalendarDateTime} for expansion
"""
start = component.getStartDateUTC()
- if start is not None and dateordatetime(start) >= limit:
+ if start is not None and start >= limit:
# If the free busy is beyond the end of the range we want, ignore it
return
@@ -389,10 +367,11 @@
assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
for period in fb.value():
# Ignore if period starts after limit
- if dateordatetime(period[0]) >= limit:
+ period = period.getValue()
+ if period.getStart() >= limit:
continue
- start = normalizeForIndex(period[0])
- end = normalizeForIndex(periodEnd(period))
+ start = normalizeForIndex(period.getStart())
+ end = normalizeForIndex(period.getEnd())
self.addInstance(Instance(component, start, end))
def _addAvailabilityComponent(self, component, limit):
@@ -403,20 +382,20 @@
depending on the presence of the properties. If unbounded at one or both ends, we will
set the time to 1/1/1900 in the past and 1/1/3000 in the future.
@param component: the Component to expand
- @param limit: the end datetime.datetime for expansion
+ @param limit: the end L{PyCalendarDateTime} for expansion
"""
start = component.getStartDateUTC()
- if start is not None and dateordatetime(start) >= limit:
+ if start is not None and start >= limit:
# If the free busy is beyond the end of the range we want, ignore it
return
if start is None:
- start = datetime.datetime(1900, 1, 1, 0, 0, 0, tzinfo=utc)
+ start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
start = normalizeForIndex(start)
end = component.getEndDateUTC()
if end is None:
- end = datetime.datetime(3000, 1, 1, 0, 0, 0, tzinfo=utc)
+ end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
end = normalizeForIndex(end)
self.addInstance(Instance(component, start, end))
Modified: CalendarServer/trunk/twistedcaldav/localization.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/localization.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/localization.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -24,6 +24,7 @@
from locale import normalize
from twext.python.log import Logger
+from pycalendar.duration import PyCalendarDuration
try:
from Foundation import (
@@ -90,12 +91,12 @@
helper methods for date formatting:
with translationTo('en') as trans:
- print trans.dtDate(datetime.today())
+ print trans.dtDate(PyCalendarDateTime.getToday())
... Thursday, October 23, 2008
with translationTo('fr') as trans:
- print trans.dtDate(datetime.today())
+ print trans.dtDate(PyCalendarDateTime.getToday())
... Jeudi, Octobre 23, 2008
@@ -151,7 +152,7 @@
return self.translation.ugettext(monthsAbbrev[monthNumber])
def date(self, component):
- dtStart = component.propertyNativeValue("DTSTART")
+ dtStart = component.propertyValue("DTSTART")
return self.dtDate(dtStart)
def time(self, component):
@@ -173,32 +174,29 @@
_ = self.translation.ugettext
tzStart = tzEnd = None
- dtStart = component.propertyNativeValue("DTSTART")
- if isinstance(dtStart, datetime.datetime):
- tzStart = dtStart.tzname()
+ dtStart = component.propertyValue("DTSTART")
+ if dtStart.isDateOnly():
+ return ("", _("All day"))
else:
- return ("", _("All day"))
+ tzStart = dtStart.timeZoneDescriptor()
- # tzStart = component.getProperty("DTSTART").params().get("TZID", "UTC")
-
- dtEnd = component.propertyNativeValue("DTEND")
+ dtEnd = component.propertyValue("DTEND")
if dtEnd:
- if isinstance(dtEnd, datetime.datetime):
- tzEnd = dtEnd.tzname()
- # tzEnd = component.getProperty("DTEND").params().get("TZID", "UTC")
+ if not dtEnd.isDateOnly():
+ tzEnd = dtEnd.timeZoneDescriptor()
duration = dtEnd - dtStart
else:
tzEnd = tzStart
- duration = component.propertyNativeValue("DURATION")
+ duration = component.propertyValue("DURATION")
if duration:
dtEnd = dtStart + duration
else:
- if isinstance(dtStart, datetime.date):
+ if dtStart.isDateOnly():
dtEnd = None
- duration = datetime.timedelta(days=1)
+ duration = PyCalendarDuration(days=1)
else:
- dtEnd = dtStart + datetime.timedelta(days=1)
- dtEnd.hour = dtEnd.minute = dtEnd.second = 0
+ dtEnd = dtStart + PyCalendarDuration(days=1)
+ dtEnd.setHHMMSS(0, 0, 0)
duration = dtEnd - dtStart
if dtStart == dtEnd:
@@ -222,37 +220,37 @@
return (
_("%(dayName)s, %(monthName)s %(dayNumber)d, %(yearNumber)d")
% {
- 'dayName' : _(daysFull[val.weekday()]),
- 'monthName' : _(monthsFull[val.month]),
- 'dayNumber' : val.day,
- 'yearNumber' : val.year,
+ 'dayName' : _(daysFull[(val.getDayOfWeek() + 6) % 7]),
+ 'monthName' : _(monthsFull[val.getMonth()]),
+ 'dayNumber' : val.getDay(),
+ 'yearNumber' : val.getYear(),
}
)
def dtTime(self, val, includeTimezone=True):
- if not isinstance(val, (datetime.datetime, datetime.time)):
+ if val.isDateOnly():
return ""
# Bind to '_' so pygettext.py will pick this up for translation
_ = self.translation.ugettext
- ampm = _("AM") if val.hour < 12 else _("PM")
- hour12 = val.hour % 12
+ ampm = _("AM") if val.getHours() < 12 else _("PM")
+ hour12 = val.getHours() % 12
if hour12 == 0:
hour12 = 12
result = (
_("%(hour12Number)d:%(minuteNumber)02d %(ampm)s")
% {
- 'hour24Number' : val.hour, # 0-23
+ 'hour24Number' : val.getHours(), # 0-23
'hour12Number' : hour12, # 1-12
- 'minuteNumber' : val.minute, # 0-59
+ 'minuteNumber' : val.getMinutes(), # 0-59
'ampm' : _(ampm),
}
)
- if includeTimezone and val.tzname():
- result += " %s" % (val.tzname())
+ if includeTimezone and val.local():
+ result += " %s" % (val.timeZoneDescriptor(),)
return result
@@ -263,15 +261,17 @@
parts = []
- if val.days == 1:
+ total = val.getTotalSeconds()
+ days = total / (24 * 60 * 60)
+ if days == 1:
parts.append(_("1 day"))
- elif val.days > 1:
+ elif days > 1:
parts.append(_("%(dayCount)d days" %
- { 'dayCount' : val.days }))
+ { 'dayCount' : days }))
- hours = val.seconds / 3600
- minutes = divmod(val.seconds / 60, 60)[1]
- seconds = divmod(val.seconds, 60)[1]
+ hours = divmod(total / 3600, 24)[1]
+ minutes = divmod(total / 60, 60)[1]
+ seconds = divmod(total, 60)[1]
if hours == 1:
parts.append(_("1 hour"))
@@ -320,7 +320,7 @@
]
monthsFull = [
- "datetime.month is 1-based",
+ "month is 1-based",
_("January"),
_("February"),
_("March"),
@@ -336,7 +336,7 @@
]
monthsAbbrev = [
- "datetime.month is 1-based",
+ "month is 1-based",
_("JAN"),
_("FEB"),
_("MAR"),
Modified: CalendarServer/trunk/twistedcaldav/mail.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/mail.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/mail.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -781,7 +781,7 @@
# The organizer will then see that the reply was not successful.
attendeeProp = Property("ATTENDEE", attendee,
params = {
- "SCHEDULE-STATUS": [iTIPRequestStatus.SERVICE_UNAVAILABLE],
+ "SCHEDULE-STATUS": iTIPRequestStatus.SERVICE_UNAVAILABLE,
}
)
event.addProperty(attendeeProp)
@@ -833,17 +833,16 @@
# readable email message (not modifying the calendar body)
attendees = []
for attendeeProp in calendar.getAllAttendeeProperties():
- params = attendeeProp.params()
- cutype = params.get('CUTYPE', (None,))[0]
+ cutype = attendeeProp.parameterValue('CUTYPE', None)
if cutype == "INDIVIDUAL":
- cn = params.get("CN", (None,))[0]
+ cn = attendeeProp.parameterValue("CN", None)
cuaddr = normalizeCUAddr(attendeeProp.value())
if cuaddr.startswith("mailto:"):
mailto = cuaddr[7:]
if not cn:
cn = mailto
else:
- emailAddress = params.get("EMAIL", (None,))[0]
+ emailAddress = attendeeProp.parameterValue("EMAIL", None)
if emailAddress:
mailto = emailAddress
else:
@@ -898,7 +897,7 @@
else:
fromAddr = serverAddress
orgEmail = None
- cn = calendar.getOrganizerProperty().params().get('CN', (None,))[0]
+ cn = calendar.getOrganizerProperty().parameterValue('CN', None)
if cn is None:
cn = 'Calendar Server'
orgCN = orgEmail
@@ -920,19 +919,18 @@
raise ValueError("ORGANIZER address '%s' must be mailto: for REPLY." % (organizerMailto,))
orgEmail = organizerMailto[7:]
- orgCN = calendar.getOrganizerProperty().params().get('CN', (None,))[0]
+ orgCN = calendar.getOrganizerProperty().parameterValue('CN', None)
addressWithToken = formattedFrom
# Now prevent any "internal" CUAs from being exposed by converting
# to mailto: if we have one
for attendeeProp in calendar.getAllAttendeeProperties():
- params = attendeeProp.params()
- cutype = params.get('CUTYPE', (None,))[0]
+ cutype = attendeeProp.parameterValue('CUTYPE', None)
if cutype == "INDIVIDUAL":
cuaddr = normalizeCUAddr(attendeeProp.value())
if not cuaddr.startswith("mailto:"):
- emailAddress = params.get("EMAIL", (None,))[0]
+ emailAddress = attendeeProp.parameterValue("EMAIL", None)
if emailAddress:
attendeeProp.setValue("mailto:%s" % (emailAddress,))
@@ -1221,9 +1219,9 @@
results = { }
- dtStart = component.propertyNativeValue("DTSTART")
- results['month'] = dtStart.month
- results['day'] = dtStart.day
+ dtStart = component.propertyValue("DTSTART")
+ results['month'] = dtStart.getMonth()
+ results['day'] = dtStart.getDay()
summary = component.propertyValue("SUMMARY")
if summary is None:
Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -628,7 +628,7 @@
)
attachments = [
attachment for attachment in attachments
- if attachment.params().get("VALUE", ("TEXT",))[0] == "URI" and attachment.value().startswith("http")
+ if attachment.paramsValue("VALUE", "TEXT") == "URI" and attachment.value().startswith("http")
]
if len(xdropboxes) or len(attachments):
Modified: CalendarServer/trunk/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_common.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/method/report_common.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -29,7 +29,6 @@
"buildFreeBusyResult",
]
-import datetime
import time
try:
@@ -37,8 +36,6 @@
except ImportError:
from md5 import new as md5
-from vobject.icalendar import utc, dateTimeToString
-
from twisted.internet.defer import inlineCallbacks, returnValue, maybeDeferred
from twisted.python.failure import Failure
from twext.web2 import responsecode
@@ -62,7 +59,7 @@
from twistedcaldav.datafilters.privateevents import PrivateEventFilter
from twistedcaldav.datafilters.addressdata import AddressDataFilter
from twistedcaldav.dateops import clipPeriod, normalizePeriodList, timeRangesOverlap,\
- compareDateTime, normalizeToUTC
+ compareDateTime, normalizeToUTC, parseSQLTimestampToPyCalendar
from twistedcaldav.ical import Component, Property, iCalendarProductID
from twistedcaldav.instance import InstanceList
from twistedcaldav.memcacher import Memcacher
@@ -71,6 +68,11 @@
from txdav.common.icommondatastore import IndexedSearchException
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.period import PyCalendarPeriod
+
log = Logger()
COLLECTION_TYPE_REGULAR = "collection"
@@ -403,8 +405,8 @@
if entry:
# Offset one day at either end to account for floating
- cached_start = entry.timerange.start + datetime.timedelta(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
- cached_end = entry.timerange.end - datetime.timedelta(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_start = entry.timerange.start + PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
+ cached_end = entry.timerange.end - PyCalendarDuration(days=FBCacheEntry.CACHE_DAYS_FLOATING_ADJUST)
# Verify that the requested timerange lies within the cache timerange
if compareDateTime(timerange.end, cached_end) <= 0 and compareDateTime(timerange.start, cached_start) >= 0:
@@ -481,12 +483,12 @@
request.extendedLogItems["fb-uncached"] = request.extendedLogItems.get("fb-uncached", 0) + 1
# We want to cache a large range of time based on the current date
- cache_start = normalizeToUTC(datetime.date.today() - datetime.timedelta(days=config.FreeBusyCacheDaysBack))
- cache_end = normalizeToUTC(datetime.date.today() + datetime.timedelta(days=config.FreeBusyCacheDaysForward))
+ cache_start = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=-config.FreeBusyCacheDaysBack))
+ cache_end = normalizeToUTC(PyCalendarDateTime.getToday() + PyCalendarDuration(days=config.FreeBusyCacheDaysForward))
# If the requested timerange would fit in our allowed cache range, trigger the cache creation
if compareDateTime(timerange.start, cache_start) >= 0 and compareDateTime(timerange.end, cache_end) <= 0:
- cache_timerange = TimeRange(start=dateTimeToString(cache_start), end=dateTimeToString(cache_end))
+ cache_timerange = TimeRange(start=cache_start.getText(), end=cache_end.getText())
caching = True
#
@@ -568,19 +570,19 @@
continue
# Apply a timezone to any floating times
- fbstart = datetime.datetime.strptime(start[:19], "%Y-%m-%d %H:%M:%S")
+ fbstart = parseSQLTimestampToPyCalendar(start)
if float == 'Y':
- fbstart = fbstart.replace(tzinfo=tzinfo)
+ fbstart.setTimezone(tzinfo)
else:
- fbstart = fbstart.replace(tzinfo=utc)
- fbend =datetime.datetime.strptime(end[:19], "%Y-%m-%d %H:%M:%S")
+ fbstart.setTimezone(PyCalendarTimezone(utc=True))
+ fbend = parseSQLTimestampToPyCalendar(end)
if float == 'Y':
- fbend = fbend.replace(tzinfo=tzinfo)
+ fbend.setTimezone(tzinfo)
else:
- fbend = fbend.replace(tzinfo=utc)
+ fbend.setTimezone(PyCalendarTimezone(utc=True))
- # Click instance to time range
- clipped = clipPeriod((fbstart, fbend - fbstart), (timerange.start, timerange.end))
+ # Clip instance to time range
+ clipped = clipPeriod(PyCalendarPeriod(fbstart, duration=fbend-fbstart), PyCalendarPeriod(timerange.start, timerange.end))
# Double check for overlap
if clipped:
@@ -642,7 +644,7 @@
@param calendar: the L{Component} that is the VCALENDAR containing the VEVENT's.
@param fbinfo: the tuple used to store the three types of fb data.
@param timerange: the time range to restrict free busy data to.
- @param tzinfo: the L{datetime.tzinfo} for the timezone to use for floating/all-day events.
+ @param tzinfo: the L{PyCalendarTimezone} for the timezone to use for floating/all-day events.
"""
# Expand out the set of instances for the event with in the required range
@@ -651,7 +653,7 @@
# Can only do timed events
for key in instances:
instance = instances[key]
- if not isinstance(instance.start, datetime.datetime):
+ if instance.start.isDateOnly():
return
break
else:
@@ -662,11 +664,11 @@
# Apply a timezone to any floating times
fbstart = instance.start
- if fbstart.tzinfo is None:
- fbstart = fbstart.replace(tzinfo=tzinfo)
+ if fbstart.floating():
+ fbstart.setTimezone(tzinfo)
fbend = instance.end
- if fbend.tzinfo is None:
- fbend = fbend.replace(tzinfo=tzinfo)
+ if fbend.floating():
+ fbend.setTimezone(tzinfo)
# Check TRANSP property of underlying component
if instance.component.hasProperty("TRANSP"):
@@ -687,10 +689,10 @@
# Clip period for this instance - use duration for period end if that
# is what original component used
if instance.component.hasProperty("DURATION"):
- period = (fbstart, fbend - fbstart)
+ period = PyCalendarPeriod(fbstart, duration=fbend-fbstart)
else:
- period = (fbstart, fbend)
- clipped = clipPeriod(period, (timerange.start, timerange.end))
+ period = PyCalendarPeriod(fbstart, fbend)
+ clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
# Double check for overlap
if clipped:
@@ -706,7 +708,7 @@
@param fbinfo: the tuple used to store the three types of fb data.
@param timerange: the time range to restrict free busy data to.
"""
-
+
for vfb in [x for x in calendar.subcomponents() if x.name() == "VFREEBUSY"]:
# First check any start/end in the actual component
start = vfb.getStartDateUTC()
@@ -718,10 +720,7 @@
# Now look at each FREEBUSY property
for fb in vfb.properties("FREEBUSY"):
# Check the type
- if "FBTYPE" in fb.params():
- fbtype = fb.params()["FBTYPE"][0]
- else:
- fbtype = "BUSY"
+ fbtype = fb.parameterValue("FBTYPE", default="BUSY")
if fbtype == "FREE":
continue
@@ -729,7 +728,7 @@
assert isinstance(fb.value(), list), "FREEBUSY property does not contain a list of values: %r" % (fb,)
for period in fb.value():
# Clip period for this instance
- clipped = clipPeriod(period, (timerange.start, timerange.end))
+ clipped = clipPeriod(period.getValue(), PyCalendarPeriod(timerange.start, timerange.end))
if clipped:
fbinfo[fbtype_mapper.get(fbtype, 0)].append(clipped)
@@ -746,12 +745,12 @@
# Get overall start/end
start = vav.getStartDateUTC()
if start is None:
- start = datetime.datetime(1900, 1, 1, 0, 0, 0, tzinfo=utc)
+ start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
end = vav.getEndDateUTC()
if end is None:
- end = datetime.datetime(3000, 1, 1, 0, 0, 0, tzinfo=utc)
- period = (start, end)
- overall = clipPeriod(period, (timerange.start, timerange.end))
+ end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ period = PyCalendarPeriod(start, end)
+ overall = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
if overall is None:
continue
@@ -762,11 +761,11 @@
busyperiods = []
last_end = timerange.start
for period in periods:
- if last_end < period[0]:
- busyperiods.append((last_end, period[0]))
- last_end = period[1]
+ if last_end < period.getStart():
+ busyperiods.append(PyCalendarPeriod(last_end, period.getStart()))
+ last_end = period.getEnd()
if last_end < timerange.end:
- busyperiods.append((last_end, timerange.end))
+ busyperiods.append(PyCalendarPeriod(last_end, timerange.end))
# Add to actual results mapped by busy type
fbtype = vav.propertyValue("BUSYTYPE")
@@ -803,19 +802,19 @@
# Ignore any with floating times (which should not happen as the spec requires UTC or local
# but we will try and be safe here).
start = instance.start
- if start.tzinfo is None:
+ if start.floating():
continue
end = instance.end
- if end.tzinfo is None:
+ if end.floating():
continue
# Clip period for this instance - use duration for period end if that
# is what original component used
if instance.component.hasProperty("DURATION"):
- period = (start, end - start)
+ period = PyCalendarPeriod(start, duration=end-start)
else:
- period = (start, end)
- clipped = clipPeriod(period, (timerange.start, timerange.end))
+ period = PyCalendarPeriod(start, end)
+ clipped = clipPeriod(period, PyCalendarPeriod(timerange.start, timerange.end))
if clipped:
periods.append(clipped)
@@ -842,6 +841,7 @@
# Now build a new calendar object with the free busy info we have
fbcalendar = Component("VCALENDAR")
+ fbcalendar.addProperty(Property("VERSION", "2.0"))
fbcalendar.addProperty(Property("PRODID", iCalendarProductID))
if method:
fbcalendar.addProperty(Property("METHOD", method))
@@ -853,13 +853,13 @@
fb.addProperty(attendee)
fb.addProperty(Property("DTSTART", timerange.start))
fb.addProperty(Property("DTEND", timerange.end))
- fb.addProperty(Property("DTSTAMP", datetime.datetime.now(tz=utc)))
+ fb.addProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
if len(fbinfo[0]) != 0:
- fb.addProperty(Property("FREEBUSY", fbinfo[0], {"FBTYPE": ["BUSY"]}))
+ fb.addProperty(Property("FREEBUSY", fbinfo[0], {"FBTYPE": "BUSY"}))
if len(fbinfo[1]) != 0:
- fb.addProperty(Property("FREEBUSY", fbinfo[1], {"FBTYPE": ["BUSY-TENTATIVE"]}))
+ fb.addProperty(Property("FREEBUSY", fbinfo[1], {"FBTYPE": "BUSY-TENTATIVE"}))
if len(fbinfo[2]) != 0:
- fb.addProperty(Property("FREEBUSY", fbinfo[2], {"FBTYPE": ["BUSY-UNAVAILABLE"]}))
+ fb.addProperty(Property("FREEBUSY", fbinfo[2], {"FBTYPE": "BUSY-UNAVAILABLE"}))
if uid is not None:
fb.addProperty(Property("UID", uid))
else:
Modified: CalendarServer/trunk/twistedcaldav/query/calendarquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/calendarquery.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/query/calendarquery.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -26,7 +26,7 @@
"sqlcalendarquery",
]
-from twistedcaldav.dateops import floatoffset
+from twistedcaldav.dateops import floatoffset, pyCalendarTodatetime
from twistedcaldav.query import expression, sqlgenerator, calendarqueryfilter
# SQL Index column (field) names
@@ -197,10 +197,10 @@
endfloat = floatoffset(end, tzinfo) if end else None
return (
- (start) if start else None,
- (end) if end else None,
- (startfloat) if startfloat else None,
- (endfloat) if endfloat else None,
+ pyCalendarTodatetime(start) if start else None,
+ pyCalendarTodatetime(end) if end else None,
+ pyCalendarTodatetime(startfloat) if startfloat else None,
+ pyCalendarTodatetime(endfloat) if endfloat else None,
)
def sqlcalendarquery(filter, calendarid=None, userid=None, generator=sqlgenerator.sqlgenerator):
Modified: CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -26,10 +26,11 @@
from twistedcaldav.caldavxml import caldav_namespace, CalDAVTimeZoneElement
from twistedcaldav.dateops import timeRangesOverlap
-from twistedcaldav.ical import Component, Property, parse_date_or_datetime
-from vobject.icalendar import utc
-import datetime
+from twistedcaldav.ical import Component, Property
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
+
log = Logger()
class FilterBase(object):
@@ -82,7 +83,7 @@
instances = None
else:
# Expand the instances up to infinity
- instances = component.expandTimeRanges(datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc), ignoreInvalidInstances=True)
+ instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), ignoreInvalidInstances=True)
else:
instances = component.expandTimeRanges(maxend, ignoreInvalidInstances=True)
else:
@@ -108,7 +109,7 @@
Set the default timezone to use with this query.
@param calendar: a L{Component} for the VCALENDAR containing the one
VTIMEZONE that we want
- @return: the L{datetime.tzinfo} derived from the VTIMEZONE or utc.
+ @return: the L{PyCalendarTimezone} derived from the VTIMEZONE or utc.
"""
assert tzelement is None or isinstance(tzelement, CalDAVTimeZoneElement)
@@ -118,11 +119,12 @@
for subcomponent in calendar.subcomponents():
if subcomponent.name() == "VTIMEZONE":
# <filter> contains exactly one <comp-filter>
- tzinfo = subcomponent.gettzinfo()
- self.child.settzinfo(tzinfo)
- return tzinfo
+ tz = subcomponent.gettimezone()
+ self.child.settzinfo(tz)
+ return tz
# Default to using utc tzinfo
+ utc = PyCalendarTimezone(utc=True)
self.child.settzinfo(utc)
return utc
@@ -319,7 +321,7 @@
def settzinfo(self, tzinfo):
"""
Set the default timezone to use with this query.
- @param tzinfo: a L{datetime.tzinfo} to use.
+ @param tzinfo: a L{PyCalendarTimezone} to use.
"""
# Give tzinfo to any TimeRange we have
@@ -332,10 +334,10 @@
def getmaxtimerange(self, currentMaximum, currentIsStartTime):
"""
- Get the date furthest into the future in any time-range elements
+ Get the date farthest into the future in any time-range elements
@param currentMaximum: current future value to compare with
- @type currentMaximum: L{datetime.datetime}
+ @type currentMaximum: L{PyCalendarDateTime}
"""
# Give tzinfo to any TimeRange we have
@@ -405,7 +407,7 @@
def settzinfo(self, tzinfo):
"""
Set the default timezone to use with this query.
- @param tzinfo: a L{datetime.tzinfo} to use.
+ @param tzinfo: a L{PyCalendarTimezone} to use.
"""
# Give tzinfo to any TimeRange we have
@@ -417,7 +419,7 @@
Get the date furthest into the future in any time-range elements
@param currentMaximum: current future value to compare with
- @type currentMaximum: L{datetime.datetime}
+ @type currentMaximum: L{PyCalendarDateTime}
"""
# Give tzinfo to any TimeRange we have
@@ -438,24 +440,13 @@
def _match(self, property, access):
- # We have to deal with the problem that the 'Native' form of a property
- # will be missing the TZID parameter due to the conversion performed. Converting
- # to non-native for the entire calendar object causes problems elsewhere, so its
- # best to do it here for this one special case.
- if self.filter_name == "TZID":
- transformed = property.transformAllFromNative()
- else:
- transformed = False
-
- # At least one property must match (or is-not-defined is set)
+ # At least one parameter must match (or is-not-defined is set)
result = not self.defined
- for parameterName in property.params().keys():
- if parameterName == self.filter_name and self.match(property.params()[parameterName], access):
+ for parameterName in property.parameterNames():
+ if parameterName == self.filter_name and self.match([property.parameterValue(parameterName)], access):
result = self.defined
break
- if transformed:
- property.transformAllToNative()
return result
class IsNotDefined (FilterBase):
@@ -507,7 +498,7 @@
if item is None: return False
if isinstance(item, Property):
- values = [item.value()]
+ values = [item.strvalue()]
else:
values = item
@@ -526,7 +517,7 @@
for value in values:
# NB Its possible that we have a text list value which appears as a Python list,
- # so we need to check for that an iterate over the list.
+ # so we need to check for that and iterate over the list.
if isinstance(value, list):
for subvalue in value:
matched, result = _textCompare(subvalue)
@@ -552,14 +543,14 @@
if "start" not in xml_element.attributes and "end" not in xml_element.attributes:
raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range")
- self.start = parse_date_or_datetime(xml_element.attributes["start"]) if "start" in xml_element.attributes else None
- self.end = parse_date_or_datetime(xml_element.attributes["end"]) if "end" in xml_element.attributes else None
+ self.start = PyCalendarDateTime.parseText(xml_element.attributes["start"]) if "start" in xml_element.attributes else None
+ self.end = PyCalendarDateTime.parseText(xml_element.attributes["end"]) if "end" in xml_element.attributes else None
self.tzinfo = None
def settzinfo(self, tzinfo):
"""
Set the default timezone to use with this query.
- @param tzinfo: a L{datetime.tzinfo} to use.
+ @param tzinfo: a L{PyCalendarTimezone} to use.
"""
# Give tzinfo to any TimeRange we have
@@ -572,16 +563,16 @@
@return: True if valid, False otherwise
"""
- if self.start is not None and not isinstance(self.start, datetime.datetime):
+ if self.start is not None and self.start.isDateOnly():
log.msg("start attribute in <time-range> is not a date-time: %s" % (self.start,))
return False
- if self.end is not None and not isinstance(self.end, datetime.datetime):
+ if self.end is not None and self.end.isDateOnly():
log.msg("end attribute in <time-range> is not a date-time: %s" % (self.end,))
return False
- if self.start is not None and self.start.tzinfo != utc:
+ if self.start is not None and not self.start.utc():
log.msg("start attribute in <time-range> is not UTC: %s" % (self.start,))
return False
- if self.end is not None and self.end.tzinfo != utc:
+ if self.end is not None and not self.end.utc():
log.msg("end attribute in <time-range> is not UTC: %s" % (self.end,))
return False
Modified: CalendarServer/trunk/twistedcaldav/query/expression.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/expression.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/query/expression.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -75,7 +75,7 @@
def __str__(self):
"""
- Generate a suitable text descriptor of this epxression.
+ Generate a suitable text descriptor of this expression.
@return: a C{str} of the text for this expression.
"""
Modified: CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/query/sqlgenerator.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -266,8 +266,8 @@
e3 = expression.notcontainsExpression("SUMMARY", "help", True)
e5 = expression.andExpression([e1, e2, e3])
print e5
- #sql = sqlgenerator(e5, 'dummy-cal', 'dummy-user')
- #print sql.generate()
+ sql = sqlgenerator(e5, 'dummy-cal', 'dummy-user')
+ print sql.generate()
e6 = expression.inExpression("TYPE", ("VEVENT", "VTODO",), False)
print e6
sql = sqlgenerator(e6, 'dummy-cal', 'dummy-user')
Modified: CalendarServer/trunk/twistedcaldav/query/test/test_calendarquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/test/test_calendarquery.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/query/test/test_calendarquery.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -16,8 +16,8 @@
from twistedcaldav import caldavxml
from twistedcaldav.query import calendarqueryfilter
-import datetime
import twistedcaldav.test.util
+from pycalendar.timezone import PyCalendarTimezone
class Tests(twistedcaldav.test.util.TestCase):
@@ -33,64 +33,5 @@
)
)
filter = calendarqueryfilter.Filter(filter)
-
- # A complete implementation of current DST rules for major US time zones.
-
- def first_sunday_on_or_after(dt):
- days_to_go = 6 - dt.weekday()
- if days_to_go:
- dt += datetime.timedelta(days_to_go)
- return dt
-
- # In the US, DST starts at 2am (standard time) on the first Sunday in April.
- DSTSTART = datetime.datetime(1, 4, 1, 2)
- # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
- # which is the first Sunday on or after Oct 25.
- DSTEND = datetime.datetime(1, 10, 25, 1)
-
- ZERO = datetime.timedelta(0)
- HOUR = datetime.timedelta(hours=1)
-
- class USTimeZone(datetime.tzinfo):
-
- def __init__(self, hours, reprname, stdname, dstname):
- self.stdoffset = datetime.timedelta(hours=hours)
- self.reprname = reprname
- self.stdname = stdname
- self.dstname = dstname
-
- def __repr__(self):
- return self.reprname
-
- def tzname(self, dt):
- if self.dst(dt):
- return self.dstname
- else:
- return self.stdname
-
- def utcoffset(self, dt):
- return self.stdoffset + self.dst(dt)
-
- def dst(self, dt):
- if dt is None or dt.tzinfo is None:
- # An exception may be sensible here, in one or both cases.
- # It depends on how you want to treat them. The default
- # fromutc() implementation (called by the default astimezone()
- # implementation) passes a datetime with dt.tzinfo is self.
- return ZERO
- assert dt.tzinfo is self
-
- # Find first Sunday in April & the last in October.
- start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
- end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
-
- # Can't compare naive to aware objects, so strip the timezone from
- # dt first.
- if start <= dt.replace(tzinfo=None) < end:
- return HOUR
- else:
- return ZERO
-
- Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
- filter.child.settzinfo(Eastern)
+ filter.child.settzinfo(PyCalendarTimezone(tzid="America/New_York"))
\ No newline at end of file
Modified: CalendarServer/trunk/twistedcaldav/query/test/test_queryfilter.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/test/test_queryfilter.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/query/test/test_queryfilter.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -17,6 +17,8 @@
from twistedcaldav import caldavxml
from twistedcaldav.query import calendarqueryfilter
import twistedcaldav.test.util
+from twistedcaldav.caldavxml import TimeZone
+from pycalendar.timezone import PyCalendarTimezone
class Tests(twistedcaldav.test.util.TestCase):
@@ -74,4 +76,151 @@
)
calendarqueryfilter.Filter(xml_element)
-
\ No newline at end of file
+
+ def test_queryWithTimezone(self):
+
+ xml_element = caldavxml.Filter(
+ caldavxml.ComponentFilter(
+ caldavxml.ComponentFilter(
+ caldavxml.TimeRange(**{"start":"20060605T160000Z", "end":"20060605T170000Z"}),
+ **{"name":"VEVENT"}
+ ),
+ **{"name":"VCALENDAR"}
+ )
+ )
+
+ filter = calendarqueryfilter.Filter(xml_element)
+ tz = filter.settimezone(TimeZone.fromString("""BEGIN:VCALENDAR
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:America/New_York
+X-LIC-LOCATION:America/New_York
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19180331T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19200328T070000Z
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19181027T020000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=19201031T060000Z
+END:STANDARD
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19210424T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19410427T070000Z
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19210925T020000
+RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU;UNTIL=19410928T060000Z
+END:STANDARD
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19460428T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19730429T070000Z
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19460929T020000
+RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU;UNTIL=19540926T060000Z
+END:STANDARD
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19551030T020000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=20061029T060000Z
+END:STANDARD
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19760425T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19860427T070000Z
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19870405T020000
+RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=20060402T070000Z
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+END:STANDARD
+BEGIN:STANDARD
+TZOFFSETFROM:-045602
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:18831118T120358
+RDATE:18831118T120358
+END:STANDARD
+BEGIN:STANDARD
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19200101T000000
+RDATE:19200101T000000
+RDATE:19420101T000000
+RDATE:19460101T000000
+RDATE:19670101T000000
+END:STANDARD
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EWT
+DTSTART:19420209T020000
+RDATE:19420209T020000
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0400
+TZNAME:EPT
+DTSTART:19450814T190000
+RDATE:19450814T190000
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+TZNAME:EST
+DTSTART:19450930T020000
+RDATE:19450930T020000
+END:STANDARD
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+TZNAME:EDT
+DTSTART:19740106T020000
+RDATE:19740106T020000
+RDATE:19750223T020000
+END:DAYLIGHT
+END:VTIMEZONE
+END:VCALENDAR
+"""))
+
+ self.assertTrue(isinstance(tz, PyCalendarTimezone))
Modified: CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -15,7 +15,7 @@
##
from twext.python.log import Logger
-from twext.python.datetime import timerange, asUTC, iCalendarString
+#from twext.python.datetime import timerange, asUTC, iCalendarString
from twistedcaldav.config import config
from twistedcaldav.ical import Component, Property
@@ -24,6 +24,8 @@
from twistedcaldav import accounting
from difflib import unified_diff
+from pycalendar.period import PyCalendarPeriod
+from pycalendar.datetime import PyCalendarDateTime
"""
Class that handles diff'ing two calendar objects.
@@ -209,7 +211,7 @@
# Whenever SCHEDULE-FORCE-SEND is explicitly set by the Organizer we assume the Organizer
# is deliberately overwriting PARTSTAT
- if new_attendee.params().get("SCHEDULE-FORCE-SEND", ["",])[0] == "REQUEST":
+ if new_attendee.parameterValue("SCHEDULE-FORCE-SEND", "") == "REQUEST":
continue
# Transfer parameters from any old Attendees found
@@ -221,14 +223,14 @@
self._transferParameter(old_attendee, new_attendee, "SCHEDULE-STATUS")
def _transferParameter(self, old_property, new_property, parameter):
- paramvalue = old_property.params().get(parameter)
+ paramvalue = old_property.parameterValue(parameter)
if paramvalue is None:
try:
- del new_property.params()[parameter]
+ new_property.removeParameter(parameter)
except KeyError:
pass
else:
- new_property.params()[parameter] = paramvalue
+ new_property.setParameter(parameter, paramvalue)
def attendeeMerge(self, attendee):
"""
@@ -282,7 +284,7 @@
# Get all EXDATEs in UTC
exdates = set()
for exdate in master.properties("EXDATE"):
- exdates.update([asUTC(value) for value in exdate.value()])
+ exdates.update([value.getValue().adjustToUTC() for value in exdate.value()])
return exdates, map, master
@@ -331,7 +333,7 @@
# Mark Attendee as DECLINED in the server instance
if self._attendeeDecline(self.newCalendar.overriddenComponent(rid)):
changeCausesReply = True
- changedRids.append(iCalendarString(rid) if rid else "")
+ changedRids.append(rid.getText() if rid else "")
else:
# We used to generate a 403 here - but instead we now ignore this error and let the server data
# override the client
@@ -407,7 +409,7 @@
#return False, False, (), None
changeCausesReply |= reply
if reply:
- changedRids.append(iCalendarString(rid) if rid else "")
+ changedRids.append(rid.getText() if rid else "")
# We need to derive instances for any declined using an EXDATE
for decline in sorted(declines):
@@ -418,7 +420,7 @@
self.newCalendar.addComponent(overridden)
if self._attendeeDecline(overridden):
changeCausesReply = True
- changedRids.append(iCalendarString(decline) if decline else "")
+ changedRids.append(decline.getText() if decline else "")
else:
self._logDiffError("attendeeMerge: Unable to override an instance to mark as DECLINED: %s" % (decline,))
return False, False, (), None
@@ -456,17 +458,17 @@
# ATTENDEE/PARTSTAT/RSVP
serverAttendee = serverComponent.getAttendeeProperty((self.attendee,))
clientAttendee = clientComponent.getAttendeeProperty((self.attendee,))
- if serverAttendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0] != clientAttendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0]:
- serverAttendee.params()["PARTSTAT"] = clientAttendee.params().get("PARTSTAT", "NEEDS-ACTION")
+ if serverAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION") != clientAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION"):
+ serverAttendee.setParameter("PARTSTAT", clientAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION"))
replyNeeded = True
- if serverAttendee.params().get("RSVP", ("FALSE",))[0] != clientAttendee.params().get("RSVP", ("FALSE",))[0]:
- if clientAttendee.params().get("RSVP", ("FALSE",))[0] == "FALSE":
+ if serverAttendee.parameterValue("RSVP", "FALSE") != clientAttendee.parameterValue("RSVP", "FALSE"):
+ if clientAttendee.parameterValue("RSVP", "FALSE") == "FALSE":
try:
- del serverAttendee.params()["RSVP"]
+ serverAttendee.removeParameter("RSVP")
except KeyError:
pass
else:
- serverAttendee.params()["RSVP"] = ["TRUE",]
+ serverAttendee.setParameter("RSVP", "TRUE")
# Transfer these properties from the client data
replyNeeded |= self._transferProperty("X-CALENDARSERVER-PRIVATE-COMMENT", serverComponent, clientComponent)
@@ -506,7 +508,7 @@
# Remove existing ATTACH's from server
for attachment in tuple(serverComponent.properties("ATTACH")):
- valueType = attachment.paramValue("VALUE")
+ valueType = attachment.parameterValue("VALUE")
if valueType in (None, "URI"):
dataValue = attachment.value()
if dataValue.find(serverDropbox) != -1:
@@ -514,7 +516,7 @@
# Copy new ATTACH's to server
for attachment in tuple(clientComponent.properties("ATTACH")):
- valueType = attachment.paramValue("VALUE")
+ valueType = attachment.parameterValue("VALUE")
if valueType in (None, "URI"):
dataValue = attachment.value()
if dataValue.find(serverDropbox) != -1:
@@ -540,7 +542,7 @@
# Bad if EXDATEs have been removed
missing = serverProps[-1] - clientProps[-1]
if missing:
- log.debug("EXDATEs missing: %s" % (", ".join([iCalendarString(exdate) for exdate in missing]),))
+ log.debug("EXDATEs missing: %s" % (", ".join([exdate.getText() for exdate in missing]),))
return False
declines.extend(clientProps[-1] - serverProps[-1])
return True
@@ -555,7 +557,7 @@
dtend = component.getProperty("DTEND")
duration = component.getProperty("DURATION")
- timeRange = timerange(
+ timeRange = PyCalendarPeriod(
start = dtstart.value() if dtstart is not None else None,
end = dtend.value() if dtend is not None else None,
duration = duration.value() if duration is not None else None,
@@ -567,23 +569,23 @@
duration = component.getProperty("DURATION")
if dtstart or duration:
- timeRange = timerange(
+ timeRange = PyCalendarPeriod(
start = dtstart.value() if dtstart is not None else None,
duration = duration.value() if duration is not None else None,
)
else:
- timeRange = timerange()
+ timeRange = PyCalendarPeriod()
newdue = component.getProperty("DUE")
if newdue is not None:
- newdue = asUTC(newdue.value())
+ newdue.value().adjustToUTC()
# Recurrence rules - we need to normalize the order of the value parts
newrrules = set()
rrules = component.properties("RRULE")
for rrule in rrules:
indexedTokens = {}
- indexedTokens.update([valuePart.split("=") for valuePart in rrule.value().split(";")])
+ indexedTokens.update([valuePart.split("=") for valuePart in rrule.value().getText().split(";")])
sortedValue = ";".join(["%s=%s" % (key, value,) for key, value in sorted(indexedTokens.iteritems(), key=lambda x:x[0])])
newrrules.add(sortedValue)
@@ -591,15 +593,18 @@
newrdates = set()
rdates = component.properties("RDATE")
for rdate in rdates:
- newrdates.update([asUTC(value) for value in rdate.value()])
+ for value in rdate.value():
+ if isinstance(PyCalendarDateTime()):
+ value.adjustToUTC()
+ newrdates.add(value)
# EXDATEs
newexdates = set()
exdates = component.properties("EXDATE")
for exdate in exdates:
- newexdates.update([asUTC(value) for value in exdate.value()])
+ newexdates.update([value.getValue().adjustToUTC() for value in exdate.value()])
- return timeRange.start(), timeRange.end(), newdue, newrrules, newrdates, newexdates
+ return timeRange.getStart(), timeRange.getEnd(), newdue, newrrules, newrdates, newexdates
def _transferProperty(self, propName, serverComponent, clientComponent):
@@ -625,8 +630,8 @@
@return: C{bool} indicating whether the PARTSTAT value was in fact changed
"""
attendee = component.getAttendeeProperty((self.attendee,))
- partstatChanged = attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0] != "DECLINED"
- attendee.params()["PARTSTAT"] = ["DECLINED",]
+ partstatChanged = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") != "DECLINED"
+ attendee.setParameter("PARTSTAT", "DECLINED")
prop = component.getProperty("X-APPLE-NEEDS-REPLY")
if prop:
component.removeProperty(prop)
@@ -705,11 +710,7 @@
comp2 = self._componentDuplicateAndNormalize(comp2)
# Diff all the properties
- comp1.transformAllFromNative()
- comp2.transformAllFromNative()
propdiff = set(comp1.properties()) ^ set(comp2.properties())
- comp1.transformAllToNative()
- comp2.transformAllToNative()
addedChanges = False
propsChanged = {}
@@ -728,17 +729,17 @@
prop1s = tuple(comp1.properties(prop.name()))
prop2s = tuple(comp2.properties(prop.name()))
if len(prop1s) == 1 and len(prop2s) == 1:
- param1s = set(["%s=%s" % (name, value) for name, value in prop1s[0].params().iteritems()])
- param2s = set(["%s=%s" % (name, value) for name, value in prop2s[0].params().iteritems()])
+ param1s = set(["%s=%s" % (name, prop1s[0].parameterValue(name)) for name in prop1s[0].parameterNames()])
+ param2s = set(["%s=%s" % (name, prop2s[0].parameterValue(name)) for name in prop2s[0].parameterNames()])
paramDiffs = param1s ^ param2s
propsChanged[prop.name()].update([param.split("=")[0] for param in paramDiffs])
- if "ORIGINAL-TZID" in propsChanged[prop.name()]:
- propsChanged[prop.name()].remove("ORIGINAL-TZID")
+ if "_TZID" in propsChanged[prop.name()]:
+ propsChanged[prop.name()].remove("_TZID")
propsChanged[prop.name()].add("TZID")
if addedChanges:
rid = comp1.getRecurrenceIDUTC()
- rids[iCalendarString(rid) if rid is not None else ""] = propsChanged
+ rids[rid.getText() if rid is not None else ""] = propsChanged
def _logDiffError(self, title):
Modified: CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/implicit.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/implicit.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -279,7 +279,7 @@
self.request.suppressRefresh = False
for attendee in self.calendar.getAllAttendeeProperties():
- if attendee.params().get("PARTSTAT", ["NEEDS-ACTION"])[0] == "NEEDS-ACTION":
+ if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") == "NEEDS-ACTION":
self.request.suppressRefresh = True
if hasattr(self.request, "doing_attendee_refresh"):
@@ -491,7 +491,7 @@
comp = self.calendar.overriddenComponent(rid)
for attendee in comp.getAllAttendeeProperties():
- if attendee.params().has_key("PARTSTAT"):
+ if attendee.hasParameter("PARTSTAT"):
cuaddr = attendee.value()
if cuaddr in self.organizerPrincipal.calendarUserAddresses():
@@ -500,7 +500,7 @@
# The organizer is automatically ACCEPTED to the event.
continue
- attendee.params()["PARTSTAT"] = ["NEEDS-ACTION",]
+ attendee.setParameter("PARTSTAT", "NEEDS-ACTION")
# Check for removed attendees
if not recurrence_reschedule:
@@ -511,15 +511,15 @@
# Always set RSVP=TRUE for any NEEDS-ACTION
for attendee in self.calendar.getAllAttendeeProperties():
- if attendee.params().get("PARTSTAT", ["NEEDS-ACTION"])[0] == "NEEDS-ACTION":
- attendee.params()["RSVP"] = ["TRUE",]
+ if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") == "NEEDS-ACTION":
+ attendee.setParameter("RSVP", "TRUE")
yield self.scheduleWithAttendees()
# Always clear SCHEDULE-FORCE-SEND from all attendees after scheduling
for attendee in self.calendar.getAllAttendeeProperties():
try:
- del attendee.params()["SCHEDULE-FORCE-SEND"]
+ attendee.removeParameter("SCHEDULE-FORCE-SEND")
except KeyError:
pass
@@ -571,7 +571,7 @@
reinvites = set()
for attendee in self.calendar.getAllAttendeeProperties():
try:
- if attendee.params()["SCHEDULE-FORCE-SEND"][0] == "REQUEST":
+ if attendee.parameterValue("SCHEDULE-FORCE-SEND") == "REQUEST":
reinvites.add(attendee.value())
except KeyError:
pass
@@ -609,10 +609,10 @@
# Also look for new EXDATEs
oldexdates = set()
for property in self.oldcalendar.masterComponent().properties("EXDATE"):
- oldexdates.update(property.value())
+ oldexdates.update([value.getValue() for value in property.value()])
newexdates = set()
for property in self.calendar.masterComponent().properties("EXDATE"):
- newexdates.update(property.value())
+ newexdates.update([value.getValue() for value in property.value()])
addedexdates = newexdates - oldexdates
Modified: CalendarServer/trunk/twistedcaldav/scheduling/itip.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/itip.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/itip.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -30,17 +30,13 @@
# know how to deal with overridden instances.
#
-import datetime
-
-from vobject.icalendar import utc
-from vobject.icalendar import dateTimeToString
-
from twext.python.log import Logger
-from twext.python.datetime import asUTC, iCalendarString
from twistedcaldav.config import config
from twistedcaldav.ical import Property, iCalendarProductID, Component
+from pycalendar.datetime import PyCalendarDateTime
+
log = Logger()
__version__ = "0.0"
@@ -101,7 +97,7 @@
private_comments = current_master.properties("X-CALENDARSERVER-PRIVATE-COMMENT")
transps = current_master.properties("TRANSP")
organizer = current_master.getProperty("ORGANIZER")
- organizer_schedule_status = organizer.params().get("SCHEDULE-STATUS", None) if organizer else None
+ organizer_schedule_status = organizer.parameterValue("SCHEDULE-STATUS", None) if organizer else None
else:
master_valarms = ()
private_comments = ()
@@ -124,7 +120,7 @@
if organizer_schedule_status:
organizer = master_component.getProperty("ORGANIZER")
if organizer:
- organizer.params()["SCHEDULE-STATUS"] = organizer_schedule_status
+ organizer.setParameter("SCHEDULE-STATUS", organizer_schedule_status)
# Now try to match recurrences
for component in new_calendar.subcomponents():
@@ -321,7 +317,7 @@
if attendee:
attendees.add(attendee)
if rids is not None and (partstat or private_comment):
- rids.add((iCalendarString(rid), partstat, private_comment,))
+ rids.add((rid.getText(), partstat, private_comment,))
# Check for an invalid instance by itself
len_attendees = len(attendees)
@@ -363,20 +359,20 @@
return None, False, False
attendee = attendees[0]
- partstat = attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0]
+ partstat = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
# Now find matching ATTENDEE in to_component
existing_attendee = to_component.getAttendeeProperty((attendee.value(),))
if existing_attendee:
- oldpartstat = existing_attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0]
- existing_attendee.params()["PARTSTAT"] = [partstat]
- existing_attendee.params()["SCHEDULE-STATUS"] = [reqstatus]
+ oldpartstat = existing_attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
+ existing_attendee.setParameter("PARTSTAT", partstat)
+ existing_attendee.setParameter("SCHEDULE-STATUS", reqstatus)
partstat_changed = (oldpartstat != partstat)
# Always delete RSVP on PARTSTAT change
if partstat_changed:
try:
- del existing_attendee.params()["RSVP"]
+ existing_attendee.removeParameter("RSVP")
except KeyError:
pass
@@ -389,12 +385,8 @@
# Look for matching X-CALENDARSERVER-ATTENDEE-COMMENT property in existing data (State 2 in spec)
private_comments = tuple(to_component.properties("X-CALENDARSERVER-ATTENDEE-COMMENT"))
for comment in private_comments:
- params = comment.params()["X-CALENDARSERVER-ATTENDEE-REF"]
- if len(params) != 1:
- log.error("Must be one and only one X-CALENDARSERVER-ATTENDEE-REF parameter in X-CALENDARSERVER-ATTENDEE-COMMENT")
- params = (None,)
- param = params[0]
- if param == attendee.value():
+ attendeeref = comment.parameterValue("X-CALENDARSERVER-ATTENDEE-REF")
+ if attendeeref == attendee.value():
private_comment = comment
break
else:
@@ -410,11 +402,11 @@
elif attendee_comment is None and private_comment is not None:
# Remove all property parameters
- private_comment.params().clear()
+ private_comment.removeAllParameters()
# Add default parameters
- private_comment.params()["X-CALENDARSERVER-ATTENDEE-REF"] = [attendee.value()]
- private_comment.params()["X-CALENDARSERVER-DTSTAMP"] = [dateTimeToString(datetime.datetime.now(tz=utc))]
+ private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
+ private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
# Set value empty
private_comment.setValue("")
@@ -428,8 +420,8 @@
"X-CALENDARSERVER-ATTENDEE-COMMENT",
attendee_comment.value(),
params = {
- "X-CALENDARSERVER-ATTENDEE-REF": [attendee.value()],
- "X-CALENDARSERVER-DTSTAMP": [dateTimeToString(datetime.datetime.now(tz=utc))],
+ "X-CALENDARSERVER-ATTENDEE-REF": attendee.value(),
+ "X-CALENDARSERVER-DTSTAMP": PyCalendarDateTime.getNowUTC().getText(),
}
)
to_component.addProperty(private_comment)
@@ -440,11 +432,11 @@
# Only change if different
if private_comment.value() != attendee_comment.value():
# Remove all property parameters
- private_comment.params().clear()
+ private_comment.removeAllParameters()
# Add default parameters
- private_comment.params()["X-CALENDARSERVER-ATTENDEE-REF"] = [attendee.value()]
- private_comment.params()["X-CALENDARSERVER-DTSTAMP"] = [dateTimeToString(datetime.datetime.now(tz=utc))]
+ private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
+ private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
# Set new value
private_comment.setValue(attendee_comment.value())
@@ -467,11 +459,11 @@
[to_component.replaceProperty(prop) for prop in matched.properties("TRANSP")]
organizer = matched.getProperty("ORGANIZER")
- organizer_schedule_status = organizer.params().get("SCHEDULE-STATUS", None) if organizer else None
+ organizer_schedule_status = organizer.parameterValue("SCHEDULE-STATUS", None) if organizer else None
if organizer_schedule_status:
organizer = to_component.getProperty("ORGANIZER")
if organizer:
- organizer.params()["SCHEDULE-STATUS"] = organizer_schedule_status
+ organizer.setParameter("SCHEDULE-STATUS", organizer_schedule_status)
# Remove the old one
if remove_matched:
@@ -486,7 +478,7 @@
if organizer_schedule_status:
organizer = to_component.getProperty("ORGANIZER")
if organizer:
- organizer.params()["SCHEDULE-STATUS"] = organizer_schedule_status
+ organizer.setParameter("SCHEDULE-STATUS", organizer_schedule_status)
@staticmethod
def fixForiCal3(components, recipient, compatibilityMode):
@@ -498,7 +490,7 @@
continue
attendee = component.getAttendeeProperty((recipient,))
if attendee:
- partstat = attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0]
+ partstat = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
if partstat == "NEEDS-ACTION":
if compatibilityMode:
component.addProperty(Property("X-APPLE-NEEDS-REPLY", "TRUE"))
@@ -523,7 +515,6 @@
# Create a new component matching the type of the original
comp = Component(original.mainType())
- itip.addComponent(comp)
# Use the master component when the instance is None
if not instance_rid:
@@ -535,14 +526,14 @@
assert instance is not None, "Need a master component"
# Add some required properties extracted from the original
- comp.addProperty(Property("DTSTAMP", datetime.datetime.now(tz=utc)))
+ comp.addProperty(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
comp.addProperty(Property("UID", instance.propertyValue("UID")))
seq = instance.propertyValue("SEQUENCE")
- seq = str(int(seq) + 1) if seq else "1"
+ seq = int(seq) + 1 if seq else 1
comp.addProperty(Property("SEQUENCE", seq))
comp.addProperty(instance.getOrganizerProperty())
if instance_rid:
- comp.addProperty(Property("RECURRENCE-ID", asUTC(instance_rid)))
+ comp.addProperty(Property("RECURRENCE-ID", instance_rid.duplicate().adjustToUTC()))
def addProperties(propname):
for property in instance.properties(propname):
@@ -567,6 +558,8 @@
comp.addProperty(attendeeProp)
tzids.update(comp.timezoneIDs())
+
+ itip.addComponent(comp)
# Now include any referenced tzids
for comp in original.subcomponents():
@@ -589,7 +582,7 @@
itip.addProperty(Property("METHOD", "REQUEST"))
# Force update to DTSTAMP everywhere
- itip.replacePropertyInAllComponents(Property("DTSTAMP", datetime.datetime.now(tz=utc)))
+ itip.replacePropertyInAllComponents(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
# Now filter out components that do not contain every attendee
itip.attendeesView(attendees, onlyScheduleAgentServer=True)
@@ -617,7 +610,7 @@
itip.filterComponents(changedRids)
# Force update to DTSTAMP everywhere
- itip.replacePropertyInAllComponents(Property("DTSTAMP", datetime.datetime.now(tz=utc)))
+ itip.replacePropertyInAllComponents(Property("DTSTAMP", PyCalendarDateTime.getNowUTC()))
# Remove all attendees except the one we want
itip.removeAllButOneAttendee(attendee)
@@ -651,11 +644,11 @@
attendeeProps = itip.getAttendeeProperties((attendee,))
assert attendeeProps, "Must have some matching ATTENDEEs"
for attendeeProp in attendeeProps:
- attendeeProp.params().setdefault("PARTSTAT", ["DECLINED",])[0] = "DECLINED"
+ attendeeProp.setParameter("PARTSTAT", "DECLINED")
# Add REQUEST-STATUS to each top-level component
itip.addPropertyToAllComponents(Property("REQUEST-STATUS", ["2.0", "Success",]))
-
+
# Strip out unwanted bits
iTipGenerator.prepareSchedulingMessage(itip, reply=True)
Modified: CalendarServer/trunk/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/processing.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/processing.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -14,12 +14,9 @@
# limitations under the License.
##
-import datetime
import time
from hashlib import md5
-from vobject.icalendar import dateTimeToString, utc
-
from twisted.python.log import err as log_traceback
from twext.python.log import Logger
@@ -37,6 +34,9 @@
from twistedcaldav.scheduling.itip import iTipProcessing, iTIPRequestStatus
from twistedcaldav.scheduling.utils import getCalendarObjectForPrincipals
from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
__all__ = [
"ImplicitProcessor",
@@ -480,8 +480,8 @@
log.debug("ImplicitProcessing - recipient '%s' processing UID: '%s' - checking for auto-reply" % (self.recipient.cuaddr, self.uid))
# First expand current one to get instances (only go 1 year into the future)
- default_future_expansion_duration = datetime.timedelta(days=356*1)
- expand_max = datetime.date.today() + default_future_expansion_duration
+ default_future_expansion_duration = PyCalendarDuration(days=356*1)
+ expand_max = PyCalendarDateTime.getToday() + default_future_expansion_duration
instances = calendar.expandTimeRanges(expand_max, ignoreInvalidInstances=True)
instance_states = dict([(instance, True) for instance in instances.instances.itervalues()])
@@ -501,9 +501,9 @@
has_prop = (yield testcal.hasProperty((caldav_namespace, "calendar-timezone"), self.request))
if has_prop:
tz = (yield testcal.readProperty((caldav_namespace, "calendar-timezone"), self.request))
- tzinfo = tz.calendar().gettzinfo()
+ tzinfo = tz.calendar().gettimezone()
else:
- tzinfo = utc
+ tzinfo = PyCalendarTimezone(utc=True)
# Now do search for overlapping time-range
for instance in instances.instances.itervalues():
@@ -513,15 +513,18 @@
fbinfo = ([], [], [])
def makeTimedUTC(dt):
- if isinstance(dt, datetime.date) and not isinstance(dt, datetime.datetime):
- dt = datetime.datetime.fromordinal(dt.toordinal())
- if dt.tzinfo is None:
- dt = dt.replace(tzinfo=tzinfo).astimezone(utc)
+ dt = dt.duplicate()
+ if dt.isDateOnly():
+ dt.setDateOnly(False)
+ dt.setHHMMSS(0, 0, 0)
+ if dt.floating():
+ dt.setTimezone(tzinfo)
+ dt.adjustToUTC()
return dt
tr = caldavxml.TimeRange(
- start=dateTimeToString(makeTimedUTC(instance.start)),
- end=dateTimeToString(makeTimedUTC(instance.end)),
+ start=str(makeTimedUTC(instance.start)),
+ end=str(makeTimedUTC(instance.end)),
)
yield report_common.generateFreeBusyInfo(self.request, testcal, fbinfo, tr, 0, uid, servertoserver=True)
@@ -692,14 +695,14 @@
madeChanges = False
for attendee in attendees:
- if attendee.params().get("PARTSTAT", ("NEEDS-ACTION",))[0] != partstat:
- attendee.params()["PARTSTAT"] = [partstat]
+ if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") != partstat:
+ attendee.setParameter("PARTSTAT", partstat)
madeChanges = True
# Always remove RSVP - this is only an attendee change so madeChanges
# does not need to be changed
try:
- del attendee.params()["RSVP"]
+ attendee.removeParameter("RSVP")
except KeyError:
pass
Modified: CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -382,7 +382,7 @@
))
# Some clients send floating instead of UTC - coerce to UTC
- if dtstart.tzinfo is None or dtend.tzinfo is None:
+ if not dtstart.utc() or not dtend.utc():
log.err("VFREEBUSY start or end not UTC: %s" % (self.calendar,))
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
Modified: CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -855,6 +855,13 @@
BEGIN:VTIMEZONE
TZID:US-Eastern
LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:19900404T010000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19901026T060000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
@@ -862,13 +869,6 @@
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19900404T010000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:12345-67890
@@ -952,6 +952,13 @@
BEGIN:VTIMEZONE
TZID:US-Eastern
LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:19900404T010000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19901026T060000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
@@ -959,13 +966,6 @@
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19900404T010000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:12345-67890
@@ -1049,6 +1049,13 @@
BEGIN:VTIMEZONE
TZID:US-Eastern
LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:19900404T010000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19901026T060000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
@@ -1056,13 +1063,6 @@
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19900404T010000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:12345-67890
@@ -1146,6 +1146,13 @@
BEGIN:VTIMEZONE
TZID:US-Eastern
LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:19900404T010000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19901026T060000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
@@ -1153,13 +1160,6 @@
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19900404T010000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:12345-67890
@@ -1245,7 +1245,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1325,7 +1325,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test for Attendee
@@ -1392,7 +1392,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1471,7 +1471,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1559,7 +1559,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1647,7 +1647,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1728,7 +1728,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1784,7 +1784,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1858,7 +1858,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -1924,7 +1924,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -2079,7 +2079,7 @@
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
BEGIN:VEVENT
UID:12345-67890
@@ -2147,7 +2147,7 @@
ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
EXDATE:20080604T120000Z
ORGANIZER;CN=User 01:mailto:user1 at example.com
-RRULE:COUNT=400;FREQ=DAILY
+RRULE:FREQ=DAILY;COUNT=400
END:VEVENT
END:VCALENDAR
""")
@@ -2431,8 +2431,8 @@
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
-ATTACH;VALUE=URI:http://localhost/calendars/users/dropbox/6073432E-644B-49
- 65-B6F7-C3F08E70BBF9.dropbox/caldavd.plist
+ATTACH:http://localhost/calendars/users/dropbox/6073432E-644B-4965-B6F7-C3
+ F08E70BBF9.dropbox/caldavd.plist
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
@@ -2533,8 +2533,8 @@
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
-ATTACH;VALUE=URI:http://localhost/calendars/users/dropbox/6073432E-644B-49
- 65-B6F7-C3F08E70BBF9.dropbox/caldavd.plist
+ATTACH:http://localhost/calendars/users/dropbox/6073432E-644B-4965-B6F7-C3
+ F08E70BBF9.dropbox/caldavd.plist
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
@@ -2641,10 +2641,10 @@
UID:12345-67890
DTSTART:20080601T120000Z
DTEND:20080601T130000Z
-ATTACH;VALUE=URI:http://localhost/calendars/users/dropbox/6073432E-644B-49
- 65-B6F7-C3F08E70BBF9.dropbox/caldavd.plist
-ATTACH;VALUE=URI:http://localhost/calendars/users/dropbox/6073432E-644B-49
- 65-B6F7-C3F08E70BBF9.dropbox/caldavd-2.plist
+ATTACH:http://localhost/calendars/users/dropbox/6073432E-644B-4965-B6F7-C3
+ F08E70BBF9.dropbox/caldavd.plist
+ATTACH:http://localhost/calendars/users/dropbox/6073432E-644B-4965-B6F7-C3
+ F08E70BBF9.dropbox/caldavd-2.plist
ATTENDEE:mailto:user1 at example.com
ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
ORGANIZER;CN=User 01:mailto:user1 at example.com
@@ -3595,6 +3595,44 @@
""",
{"":{"ATTENDEE":set(),}},
),
+ (
+ "#2.8 Simple recurring component, property order change",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080602T120000Z
+EXDATE:20080603T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080603T120000Z
+EXDATE:20080602T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ {},
+ ),
)
data3 = (
@@ -3843,7 +3881,7 @@
END:VEVENT
END:VCALENDAR
""",
- {"":{"SUMMARY":set()}, "20080602T120000Z":{"DESCRIPTION":set()}},
+ {"":{"SUMMARY":set()}, "20080602T120000Z":{"Description":set()}},
),
(
"#3.6 Simple component, instance added no change",
Modified: CalendarServer/trunk/twistedcaldav/scheduling/test/test_implicit.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/test/test_implicit.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/test/test_implicit.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -17,8 +17,8 @@
from twistedcaldav.ical import Component
import twistedcaldav.test.util
from twistedcaldav.scheduling.implicit import ImplicitScheduler
-from dateutil.tz import tzutc
-import datetime
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
class Implicit (twistedcaldav.test.util.TestCase):
"""
@@ -28,134 +28,134 @@
def test_removed_attendees(self):
data = (
+# (
+# "#1.1 Simple component, no change",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#ATTENDEE:mailto:user2 at example.com
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#ATTENDEE:mailto:user2 at example.com
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# (),
+# ),
+# (
+# "#1.2 Simple component, one removal",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#ATTENDEE:mailto:user2 at example.com
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# (("mailto:user2 at example.com", None),),
+# ),
+# (
+# "#1.3 Simple component, two removals",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#ATTENDEE:mailto:user2 at example.com
+#ATTENDEE:mailto:user3 at example.com
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# (
+# ("mailto:user2 at example.com", None),
+# ("mailto:user3 at example.com", None),
+# ),
+# ),
+# (
+# "#2.1 Simple recurring component, two removals",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#ATTENDEE:mailto:user2 at example.com
+#ATTENDEE:mailto:user3 at example.com
+#RRULE:FREQ=MONTHLY
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# """BEGIN:VCALENDAR
+#VERSION:2.0
+#PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+#BEGIN:VEVENT
+#UID:12345-67890
+#DTSTART:20080601T120000Z
+#DTEND:20080601T130000Z
+#ORGANIZER;CN="User 01":mailto:user1 at example.com
+#ATTENDEE:mailto:user1 at example.com
+#RRULE:FREQ=MONTHLY
+#END:VEVENT
+#END:VCALENDAR
+#""",
+# (
+# ("mailto:user2 at example.com", None),
+# ("mailto:user3 at example.com", None),
+# ),
+# ),
(
- "#1.1 Simple component, no change",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-END:VEVENT
-END:VCALENDAR
-""",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-END:VEVENT
-END:VCALENDAR
-""",
- (),
- ),
- (
- "#1.2 Simple component, one removal",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-END:VEVENT
-END:VCALENDAR
-""",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-END:VEVENT
-END:VCALENDAR
-""",
- (("mailto:user2 at example.com", None),),
- ),
- (
- "#1.3 Simple component, two removals",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
-END:VEVENT
-END:VCALENDAR
-""",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-END:VEVENT
-END:VCALENDAR
-""",
- (
- ("mailto:user2 at example.com", None),
- ("mailto:user3 at example.com", None),
- ),
- ),
- (
- "#2.1 Simple recurring component, two removals",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
-RRULE:FREQ=MONTHLY
-END:VEVENT
-END:VCALENDAR
-""",
- """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTART:20080601T120000Z
-DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-RRULE:FREQ=MONTHLY
-END:VEVENT
-END:VCALENDAR
-""",
- (
- ("mailto:user2 at example.com", None),
- ("mailto:user3 at example.com", None),
- ),
- ),
- (
"#2.2 Simple recurring component, add exdate",
"""BEGIN:VCALENDAR
VERSION:2.0
@@ -189,9 +189,9 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -228,12 +228,12 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user1 at example.com", datetime.datetime(2008, 9, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 9, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 9, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -271,15 +271,15 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user1 at example.com", datetime.datetime(2008, 9, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 9, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 9, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user1 at example.com", datetime.datetime(2008, 12, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 12, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 12, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 12, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -388,7 +388,7 @@
""",
(
("mailto:user3 at example.com", None),
- ("mailto:user3 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -443,7 +443,7 @@
END:VCALENDAR
""",
(
- ("mailto:user3 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -589,9 +589,9 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -646,7 +646,7 @@
""",
(
("mailto:user3 at example.com", None),
- ("mailto:user4 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user4 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -692,7 +692,7 @@
END:VCALENDAR
""",
(
- ("mailto:user4 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user4 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
(
@@ -739,9 +739,9 @@
END:VCALENDAR
""",
(
- ("mailto:user1 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user2 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
- ("mailto:user4 at example.com", datetime.datetime(2008, 8, 1, 12, 0, 0, tzinfo=tzutc())),
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user4 at example.com", PyCalendarDateTime(2008, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))),
),
),
)
Modified: CalendarServer/trunk/twistedcaldav/scheduling/test/test_itip.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/test/test_itip.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/scheduling/test/test_itip.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -14,10 +14,10 @@
# limitations under the License.
##
-from dateutil.tz import tzutc
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
from twistedcaldav.ical import Component
from twistedcaldav.scheduling.itip import iTipProcessing, iTipGenerator
-import datetime
import os
import twistedcaldav.test.util
@@ -1269,7 +1269,7 @@
END:VCALENDAR
""",
("mailto:user2 at example.com",),
- (datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()), ),
+ (PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), ),
),
# Recurring component with one instance, each with one attendee - cancel instance
@@ -1308,7 +1308,7 @@
END:VCALENDAR
""",
("mailto:user2 at example.com",),
- (datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()), ),
+ (PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), ),
),
# Recurring component with one instance, each with one attendee - cancel master
Modified: CalendarServer/trunk/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/sharing.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/sharing.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -41,10 +41,9 @@
from twistedcaldav.linkresource import LinkFollowerMixIn
from twistedcaldav.sql import AbstractSQLDatabase, db_prefix
-from vobject.icalendar import dateTimeToString, utc
+from pycalendar.datetime import PyCalendarDateTime
from uuid import uuid4
-import datetime
import os
import types
@@ -564,7 +563,7 @@
typeAttr = {'shared-type':self.sharedResourceType()}
xmltype = customxml.InviteNotification(**typeAttr)
xmldata = customxml.Notification(
- customxml.DTStamp.fromString(dateTimeToString(datetime.datetime.now(tz=utc))),
+ customxml.DTStamp.fromString(PyCalendarDateTime.getNowUTC().getText()),
customxml.InviteNotification(
customxml.UID.fromString(record.inviteuid),
davxml.HRef.fromString(record.userid),
@@ -1123,7 +1122,7 @@
notificationUID = "%s-reply" % (replytoUID,)
xmltype = customxml.InviteReply()
xmldata = customxml.Notification(
- customxml.DTStamp.fromString(dateTimeToString(datetime.datetime.now(tz=utc))),
+ customxml.DTStamp.fromString(PyCalendarDateTime.getNowUTC().getText()),
customxml.InviteReply(
*(
(
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -38,7 +38,7 @@
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.config import config
from twistedcaldav.ical import Component as VCalendar, Property as VProperty,\
- InvalidICalendarDataError
+ InvalidICalendarDataError, iCalendarProductID
from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
from twistedcaldav.method.put_addressbook_common import StoreAddressObjectResource
from twistedcaldav.method.put_common import StoreCalendarObjectResource
@@ -47,6 +47,7 @@
from twistedcaldav.schedule import ScheduleInboxResource
from twistedcaldav.scheduling.implicit import ImplicitScheduler
from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError
+
from txdav.base.propertystore.base import PropertyName
from txdav.common.icommondatastore import NoSuchObjectResourceError
from urlparse import urlsplit
@@ -915,6 +916,7 @@
# Generate a monolithic calendar
calendar = VCalendar("VCALENDAR")
calendar.addProperty(VProperty("VERSION", "2.0"))
+ calendar.addProperty(VProperty("PRODID", iCalendarProductID))
# Do some optimisation of access control calculation by determining any
# inherited ACLs outside of the child resource loop and supply those to
@@ -993,6 +995,7 @@
for components in by_uid.values():
newvcal = VCalendar("VCALENDAR")
+ newvcal.addProperty(VProperty("VERSION", "2.0"))
newvcal.addProperty(VProperty("PRODID", vcal.propertyValue("PRODID")))
# Get the set of TZIDs and include them
@@ -1002,7 +1005,7 @@
for tzid in tzids:
try:
tz = by_tzid[tzid]
- newvcal.addComponent(tz)
+ newvcal.addComponent(tz.duplicate())
except KeyError:
# We ignore the error and generate invalid ics which someone will
# complain about at some point
@@ -1010,7 +1013,7 @@
# Now add each component
for component in components:
- newvcal.addComponent(component)
+ newvcal.addComponent(component.duplicate())
results.append(newvcal)
Copied: CalendarServer/trunk/twistedcaldav/test/test_caldavxml.py (from rev 7206, CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_caldavxml.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_caldavxml.py (rev 0)
+++ CalendarServer/trunk/twistedcaldav/test/test_caldavxml.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -0,0 +1,52 @@
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twistedcaldav import caldavxml
+import twistedcaldav.test.util
+
+class CustomXML (twistedcaldav.test.util.TestCase):
+
+
+ def test_TimeRange(self):
+
+ self.assertRaises(ValueError, caldavxml.CalDAVTimeRangeElement)
+
+ tr = caldavxml.CalDAVTimeRangeElement(start="20110201T120000Z")
+ self.assertTrue(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(start="20110201T120000")
+ self.assertFalse(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(start="20110201")
+ self.assertFalse(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(end="20110201T120000Z")
+ self.assertTrue(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(end="20110201T120000")
+ self.assertFalse(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(end="20110201")
+ self.assertFalse(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(start="20110201T120000Z", end="20110202T120000Z")
+ self.assertTrue(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(start="20110201T120000Z", end="20110202T120000")
+ self.assertFalse(tr.valid())
+
+ tr = caldavxml.CalDAVTimeRangeElement(start="20110201T120000Z", end="20110202")
+ self.assertFalse(tr.valid())
Copied: CalendarServer/trunk/twistedcaldav/test/test_customxml.py (from rev 7206, CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_customxml.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_customxml.py (rev 0)
+++ CalendarServer/trunk/twistedcaldav/test/test_customxml.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -0,0 +1,29 @@
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twistedcaldav import customxml
+import time
+import twistedcaldav.test.util
+
+class CustomXML (twistedcaldav.test.util.TestCase):
+
+
+ def test_DTStamp(self):
+
+ dtstamp = customxml.DTStamp()
+ now = time.time()
+ now_tm = time.gmtime( now )
+ self.assertEqual(str(dtstamp)[:4], "%s" % (now_tm.tm_year,))
Modified: CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -15,19 +15,18 @@
##
import os
-import datetime
-from dateutil.tz import tzutc
from difflib import unified_diff
import itertools
from twisted.trial.unittest import SkipTest
-from twistedcaldav.ical import Component, parse_date, parse_datetime,\
- parse_date_or_datetime, parse_duration, Property, InvalidICalendarDataError
+from twistedcaldav.ical import Component, Property, InvalidICalendarDataError
from twistedcaldav.instance import InvalidOverriddenInstanceError
import twistedcaldav.test.util
-from vobject.icalendar import utc
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.duration import PyCalendarDuration
class iCalendar (twistedcaldav.test.util.TestCase):
"""
@@ -52,17 +51,183 @@
SkipTest("test unimplemented")
def test_component_equality(self):
- for filename in (
- os.path.join(self.data_dir, "Holidays", "C318A4BA-1ED0-11D9-A5E0-000A958A3252.ics"),
- os.path.join(self.data_dir, "Holidays.ics"),
- ):
- data = file(filename).read()
+# for filename in (
+# os.path.join(self.data_dir, "Holidays", "C318A4BA-1ED0-11D9-A5E0-000A958A3252.ics"),
+# os.path.join(self.data_dir, "Holidays.ics"),
+# ):
+# data = file(filename).read()
+#
+# calendar1 = Component.fromString(data)
+# calendar2 = Component.fromString(data)
+#
+# self.assertEqual(calendar1, calendar2)
+
+ data1 = (
+ (
+ "1.1 Switch property order",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080602T120000Z
+EXDATE:20080603T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080603T120000Z
+EXDATE:20080602T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ (
+ "1.2 Switch component order",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080602T120000Z
+EXDATE:20080603T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T130000Z
+DTEND:20080602T140000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080603T120000Z
+EXDATE:20080602T120000Z
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ (
+ "1.3 Switch VALARM order",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080602T120000Z
+EXDATE:20080603T120000Z
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test-2
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+SUMMARY:Test
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+EXDATE:20080603T120000Z
+EXDATE:20080602T120000Z
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test-2
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ )
+
+ for description, item1, item2, result in data1:
+ if "1.3" not in description:
+ continue
+ calendar1 = Component.fromString(item1)
+ calendar2 = Component.fromString(item2)
+ (self.assertEqual if result else self.assertNotEqual)(
+ calendar1, calendar2, "%s" % (description,)
+ )
- calendar1 = Component.fromString(data)
- calendar2 = Component.fromString(data)
-
- self.assertEqual(calendar1, calendar2)
-
def test_component_validate(self):
"""
CalDAV resource validation.
@@ -186,13 +351,13 @@
year = 2004
- instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
+ instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
- self.assertEqual(start, datetime.datetime(year, 7, 4))
- self.assertEqual(end , datetime.datetime(year, 7, 5))
+ self.assertEqual(start, PyCalendarDateTime(year, 7, 4))
+ self.assertEqual(end , PyCalendarDateTime(year, 7, 5))
if year == 2050: break
year += 1
@@ -211,14 +376,14 @@
}
year = 2004
- instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
+ instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
if year in results:
- self.assertEqual(start, datetime.datetime(year, results[year][0], results[year][1]))
- self.assertEqual(end , datetime.datetime(year, results[year][0], results[year][2]))
+ self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
+ self.assertEqual(end , PyCalendarDateTime(year, results[year][0], results[year][2]))
if year == 2050: break
year += 1
@@ -237,14 +402,14 @@
}
year = 2002
- instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
+ instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
if year in results:
- self.assertEqual(start, datetime.datetime(year, results[year][0], results[year][1]))
- self.assertEqual(end , datetime.datetime(year, results[year][0], results[year][2]))
+ self.assertEqual(start, PyCalendarDateTime(year, results[year][0], results[year][1]))
+ self.assertEqual(end , PyCalendarDateTime(year, results[year][0], results[year][2]))
if year == 2050: break
year += 1
@@ -256,13 +421,13 @@
"""
calendar = Component.fromStream(file(os.path.join(self.data_dir, "Holidays", "C318ABFE-1ED0-11D9-A5E0-000A958A3252.ics")))
- instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
+ instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
- self.assertEqual(start, datetime.datetime(2004, 11, 25))
- self.assertEqual(end, datetime.datetime(2004, 11, 27))
+ self.assertEqual(start, PyCalendarDateTime(2004, 11, 25))
+ self.assertEqual(end, PyCalendarDateTime(2004, 11, 27))
break;
#test_component_timerange.todo = "recurrence expansion should give us no end date here"
@@ -271,49 +436,41 @@
"""
parse_date()
"""
- self.assertEqual(parse_date("19970714"), datetime.date(1997, 7, 14))
+ self.assertEqual(PyCalendarDateTime.parseText("19970714"), PyCalendarDateTime(1997, 7, 14))
def test_parse_datetime(self):
"""
parse_datetime()
"""
- try: parse_datetime("19980119T2300")
- except ValueError: pass
- else: self.fail("Invalid DATE-TIME should raise ValueError")
+ dt = PyCalendarDateTime.parseText("19980118T230000")
+ self.assertEqual(dt, PyCalendarDateTime(1998, 1, 18, 23, 0, 0))
+ self.assertTrue(dt.floating())
- dt = parse_datetime("19980118T230000")
- self.assertEqual(dt, datetime.datetime(1998, 1, 18, 23, 0))
- self.assertNot(dt.tzinfo)
+ dt = PyCalendarDateTime.parseText("19980119T070000Z")
+ self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
- dt = parse_datetime("19980119T070000Z")
- self.assertEqual(dt, datetime.datetime(1998, 1, 19, 07, 0, tzinfo=utc))
-
def test_parse_date_or_datetime(self):
"""
parse_date_or_datetime()
"""
- self.assertEqual(parse_date_or_datetime("19970714"), datetime.date(1997, 7, 14))
+ self.assertEqual(PyCalendarDateTime.parseText("19970714"), PyCalendarDateTime(1997, 7, 14))
- try: parse_date_or_datetime("19980119T2300")
- except ValueError: pass
- else: self.fail("Invalid DATE-TIME should raise ValueError")
+ dt = PyCalendarDateTime.parseText("19980118T230000")
+ self.assertEqual(dt, PyCalendarDateTime(1998, 1, 18, 23, 0, 0))
+ self.assertTrue(dt.floating())
- dt = parse_date_or_datetime("19980118T230000")
- self.assertEqual(dt, datetime.datetime(1998, 1, 18, 23, 0))
- self.assertNot(dt.tzinfo)
+ dt = PyCalendarDateTime.parseText("19980119T070000Z")
+ self.assertEqual(dt, PyCalendarDateTime(1998, 1, 19, 7, 0, 0, tzid=PyCalendarTimezone(utc=True)))
- dt = parse_date_or_datetime("19980119T070000Z")
- self.assertEqual(dt, datetime.datetime(1998, 1, 19, 07, 0, tzinfo=utc))
-
def test_parse_duration(self):
"""
parse_duration()
"""
- self.assertEqual(parse_duration( "P15DT5H0M20S"), datetime.timedelta(days= 15, hours= 5, minutes=0, seconds= 20))
- self.assertEqual(parse_duration("+P15DT5H0M20S"), datetime.timedelta(days= 15, hours= 5, minutes=0, seconds= 20))
- self.assertEqual(parse_duration("-P15DT5H0M20S"), datetime.timedelta(days=-15, hours=-5, minutes=0, seconds=-20))
+ self.assertEqual(PyCalendarDuration.parseText( "P15DT5H0M20S"), PyCalendarDuration(days= 15, hours= 5, minutes=0, seconds= 20))
+ self.assertEqual(PyCalendarDuration.parseText("+P15DT5H0M20S"), PyCalendarDuration(days= 15, hours= 5, minutes=0, seconds= 20))
+ self.assertEqual(PyCalendarDuration.parseText("-P15DT5H0M20S"), PyCalendarDuration(days=-15, hours=-5, minutes=0, seconds=-20))
- self.assertEqual(parse_duration("P7W"), datetime.timedelta(weeks=7))
+ self.assertEqual(PyCalendarDuration.parseText("P7W"), PyCalendarDuration(weeks=7))
def test_correct_attendee_properties(self):
@@ -413,7 +570,7 @@
""",
(
("mailto:user1 at example.com", None),
- ("mailto:user1 at example.com", datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()))
+ ("mailto:user1 at example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
)
),
(
@@ -437,7 +594,7 @@
""",
(
("mailto:user1 at example.com", None),
- ("mailto:user3 at example.com", datetime.datetime(2009, 11, 14, 0, 0, tzinfo=tzutc()))
+ ("mailto:user3 at example.com", PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
)
),
(
@@ -542,8 +699,8 @@
False,
(
("mailto:user2 at example.com", None),
- ("mailto:user2 at example.com", datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc())),
- ("mailto:user3 at example.com", datetime.datetime(2008, 11, 14, 0, 0, tzinfo=tzutc()))
+ ("mailto:user2 at example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))),
+ ("mailto:user3 at example.com", PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
)
),
(
@@ -804,8 +961,8 @@
def test_attendees_views(self):
data = (
- # Simple component, no Attendees - no filtering
(
+ "1.1 Simple component, no Attendees - no filtering",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -828,8 +985,8 @@
()
),
- # Simple component, no Attendees - filtering
(
+ "1.2 Simple component, no Attendees - filtering",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -848,8 +1005,8 @@
("mailto:user01 at example.com",)
),
- # Simple component, with one attendee - filtering match
(
+ "1.3 Simple component, with one attendee - filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -876,8 +1033,8 @@
("mailto:user2 at example.com",)
),
- # Simple component, with one attendee - no filtering match
(
+ "1.4 Simple component, with one attendee - no filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -898,8 +1055,8 @@
("mailto:user3 at example.com",)
),
- # Recurring component with one instance, each with one attendee - filtering match
(
+ "2.1 Recurring component with one instance, each with one attendee - filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -942,8 +1099,8 @@
("mailto:user2 at example.com",)
),
- # Recurring component with one instance, each with one attendee - no filtering match
(
+ "2.2 Recurring component with one instance, each with one attendee - no filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -972,8 +1129,8 @@
("mailto:user3 at example.com",)
),
- # Recurring component with one instance, master with one attendee, instance without attendee - filtering match
(
+ "2.3 Recurring component with one instance, master with one attendee, instance without attendee - filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1009,8 +1166,8 @@
("mailto:user2 at example.com",)
),
- # Recurring component with one instance, master with one attendee, instance without attendee - no filtering match
(
+ "2.4 Recurring component with one instance, master with one attendee, instance without attendee - no filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1038,8 +1195,8 @@
("mailto:user3 at example.com",)
),
- # Recurring component with one instance, master without attendee, instance with attendee - filtering match
(
+ "2.5 Recurring component with one instance, master without attendee, instance with attendee - filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1074,8 +1231,8 @@
("mailto:user2 at example.com",)
),
- # Recurring component with one instance, master without attendee, instance with attendee - no filtering match
(
+ "2.6 Recurring component with one instance, master without attendee, instance with attendee - no filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1103,8 +1260,8 @@
("mailto:user3 at example.com",)
),
- # Simple component, no Attendees - no filtering
(
+ "3.1 Simple component, no Attendees - no filtering",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1127,8 +1284,8 @@
()
),
- # Simple component, no Attendees - filtering
(
+ "3.2 Simple component, no Attendees - filtering",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1147,8 +1304,8 @@
("mailto:user01 at example.com",)
),
- # Simple component, with one attendee - filtering match
(
+ "3.3 Simple component, with one attendee - filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1175,8 +1332,8 @@
("mailto:user2 at example.com",)
),
- # Simple component, with one attendee - filtering match
(
+ "3.4 Simple component, with one attendee - filtering match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1203,8 +1360,8 @@
("mailto:user2 at example.com",)
),
- # Simple component, with one attendee - filtering match - no schedule-agent match
(
+ "3.5 Simple component, with one attendee - filtering match - no schedule-agent match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1225,8 +1382,8 @@
("mailto:user2 at example.com",)
),
- # Simple component, with one attendee - filtering match - no schedule-agent match
(
+ "3.6 Simple component, with one attendee - filtering match - no schedule-agent match",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//PYVOBJECT//NONSGML Version 1//EN
@@ -1249,10 +1406,10 @@
)
- for original, checkScheduleAgent, filtered, attendees in data:
+ for description, original, checkScheduleAgent, filtered, attendees in data:
component = Component.fromString(original)
component.attendeesView(attendees, onlyScheduleAgentServer=checkScheduleAgent)
- self.assertEqual(filtered, str(component).replace("\r", ""))
+ self.assertEqual(filtered, str(component).replace("\r", ""), "Failed: %s" % (description,))
def test_all_but_one_attendee(self):
@@ -1746,12 +1903,12 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
False,
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()),)
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),)
),
(
"Simple recurring",
@@ -1761,15 +1918,15 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=2
END:VEVENT
END:VCALENDAR
""",
False,
(
- datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
)
),
(
@@ -1780,7 +1937,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=2
RDATE:20071116T010000Z
END:VEVENT
@@ -1788,9 +1945,9 @@
""",
False,
(
- datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2007, 11, 16, 1, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 16, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
)
),
(
@@ -1801,7 +1958,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=3
EXDATE:20071115T000000Z
END:VEVENT
@@ -1809,8 +1966,8 @@
""",
False,
(
- datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2007, 11, 16, 0, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
)
),
(
@@ -1821,7 +1978,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=3
EXDATE:20071114T000000Z
END:VEVENT
@@ -1829,8 +1986,8 @@
""",
False,
(
- datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2007, 11, 16, 0, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 16, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
)
),
(
@@ -1841,21 +1998,21 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=2
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
False,
(
- datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2007, 11, 15, 1, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
)
),
(
@@ -1866,14 +2023,14 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=2
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T010000Z
DTSTART:20071115T000000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -1888,21 +2045,21 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=2
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T010000Z
DTSTART:20071115T000000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
True,
(
- datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
)
),
)
@@ -1910,9 +2067,9 @@
for description, original, ignoreInvalidInstances, results in data:
component = Component.fromString(original)
if results is None:
- self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, datetime.date(2100, 1, 1), ignoreInvalidInstances)
+ self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances)
else:
- instances = component.expandTimeRanges(datetime.date(2100, 1, 1), ignoreInvalidInstances)
+ instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances)
self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
for instance in instances:
self.assertTrue(instances[instance].start in results, "%s: %s missing" % (description, instance,))
@@ -1928,7 +2085,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -1943,7 +2100,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -1958,14 +2115,14 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -1980,14 +2137,14 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -2002,14 +2159,14 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -2024,14 +2181,14 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -2046,14 +2203,14 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -2068,14 +2225,14 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
END:VEVENT
BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
END:VEVENT
END:VCALENDAR
""",
@@ -2099,7 +2256,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:True
END:VEVENT
END:VCALENDAR
@@ -2110,7 +2267,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM2:True
END:VEVENT
END:VCALENDAR
@@ -2121,7 +2278,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:True
X-ITEM2:True
END:VEVENT
@@ -2137,7 +2294,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:True
END:VEVENT
END:VCALENDAR
@@ -2148,7 +2305,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM2:True
X-ITEM3:True
END:VEVENT
@@ -2160,7 +2317,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:True
X-ITEM2:True
X-ITEM3:True
@@ -2177,7 +2334,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:True
END:VEVENT
END:VCALENDAR
@@ -2188,7 +2345,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM2:True
X-ITEM1:False
END:VEVENT
@@ -2200,7 +2357,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:True
X-ITEM2:True
X-ITEM1:False
@@ -2217,7 +2374,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
X-ITEM1:True
END:VEVENT
@@ -2225,7 +2382,7 @@
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:False
END:VEVENT
END:VCALENDAR
@@ -2236,7 +2393,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
X-ITEM2:True
END:VEVENT
@@ -2244,7 +2401,7 @@
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM2:False
END:VEVENT
END:VCALENDAR
@@ -2255,7 +2412,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
X-ITEM1:True
X-ITEM2:True
@@ -2264,7 +2421,7 @@
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:False
X-ITEM2:False
END:VEVENT
@@ -2280,7 +2437,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
X-ITEM1:True
END:VEVENT
@@ -2288,7 +2445,7 @@
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:False
END:VEVENT
END:VCALENDAR
@@ -2299,7 +2456,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
X-ITEM2:True
END:VEVENT
@@ -2311,7 +2468,7 @@
BEGIN:VEVENT
UID:12345-67890-1
DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
RRULE:FREQ=DAILY
X-ITEM1:True
X-ITEM2:True
@@ -2320,7 +2477,7 @@
UID:12345-67890-1
RECURRENCE-ID:20071115T000000Z
DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
X-ITEM1:False
X-ITEM2:True
END:VEVENT
@@ -2428,6 +2585,74 @@
END:VCALENDAR
""",
),
+ (
+ "1.4",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=US/Pacific:20071114T000000
+RRULE:FREQ=WEEKLY;WKST=SU;INTERVAL=1;BYDAY=MO,WE,FR
+TRANSP:OPAQUE
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:user03 at example.com
+ATTENDEE;RSVP=FALSE:mailto:user04 at example.com
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//PYVOBJECT//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;_TZID=US/Pacific:20071114T080000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;RSVP=TRUE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
+ATTENDEE:mailto:user04 at example.com
+RRULE:BYDAY=MO,WE,FR;FREQ=WEEKLY;INTERVAL=1;WKST=SU
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+ ),
)
for title, original, result in data:
@@ -2631,7 +2856,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.datetime(2009, 1, 2, 8, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090102T080000Z
@@ -2654,7 +2879,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.datetime(2009, 1, 2, 18, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090102T180000Z
@@ -2678,7 +2903,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.datetime(2009, 1, 3, 18, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 3, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID:20090103T180000Z
@@ -2700,7 +2925,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.datetime(2009, 1, 2, 9, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)),
None,
),
(
@@ -2717,7 +2942,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.datetime(2009, 1, 2, 19, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
None,
),
(
@@ -2735,7 +2960,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.datetime(2009, 1, 3, 19, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 3, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
None,
),
(
@@ -2751,7 +2976,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.date(2009, 1, 8),
+ PyCalendarDateTime(2009, 1, 8),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID;VALUE=DATE:20090108
@@ -2774,7 +2999,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.date(2009, 1, 3),
+ PyCalendarDateTime(2009, 1, 3),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID;VALUE=DATE:20090103
@@ -2798,7 +3023,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.date(2009, 1, 10),
+ PyCalendarDateTime(2009, 1, 10),
"""BEGIN:VEVENT
UID:12345-67890-1
RECURRENCE-ID;VALUE=DATE:20090110
@@ -2820,7 +3045,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.date(2009, 1, 3),
+ PyCalendarDateTime(2009, 1, 3),
None,
),
(
@@ -2837,7 +3062,7 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.date(2009, 1, 5),
+ PyCalendarDateTime(2009, 1, 5),
None,
),
(
@@ -2855,14 +3080,12 @@
END:VEVENT
END:VCALENDAR
""",
- datetime.datetime(2009, 1, 19),
+ PyCalendarDateTime(2009, 1, 19),
None,
),
)
for title, calendar, rid, result in data:
- if not title.startswith("3"):
- continue
ical = Component.fromString(calendar)
derived = ical.deriveInstance(rid)
derived = str(derived).replace("\r", "") if derived else None
@@ -2885,8 +3108,8 @@
END:VCALENDAR
""",
(
- datetime.datetime(2009, 1, 2, 8, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2009, 1, 4, 8, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2009, 1, 4, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
),
(
"""BEGIN:VEVENT
@@ -2920,8 +3143,8 @@
END:VCALENDAR
""",
(
- datetime.datetime(2009, 1, 2, 18, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2009, 1, 4, 8, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2009, 1, 4, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
),
(
"""BEGIN:VEVENT
@@ -2956,8 +3179,8 @@
END:VCALENDAR
""",
(
- datetime.datetime(2009, 1, 3, 18, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2009, 1, 5, 8, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 3, 18, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2009, 1, 5, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
),
(
"""BEGIN:VEVENT
@@ -2990,8 +3213,8 @@
END:VCALENDAR
""",
(
- datetime.datetime(2009, 1, 2, 9, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2009, 1, 3, 8, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 9, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
),
(
None,
@@ -3019,8 +3242,8 @@
END:VCALENDAR
""",
(
- datetime.datetime(2009, 1, 2, 19, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2009, 1, 3, 8, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 2, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
),
(
None,
@@ -3049,8 +3272,8 @@
END:VCALENDAR
""",
(
- datetime.datetime(2009, 1, 3, 19, 0, 0, tzinfo=tzutc()),
- datetime.datetime(2009, 1, 3, 8, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2009, 1, 3, 19, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2009, 1, 3, 8, 0, 0, tzid=PyCalendarTimezone(utc=True)),
),
(
None,
@@ -3234,8 +3457,8 @@
""",
(
(None, True),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3252,9 +3475,9 @@
""",
(
(None, True),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 5, 0, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 5, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3271,10 +3494,10 @@
""",
(
(None, True),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 1, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3292,11 +3515,11 @@
""",
(
(None, True),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 1, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 2, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3315,12 +3538,12 @@
""",
(
(None, True),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 1, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 2, 0, 0, tzinfo=tzutc()), False),
- (datetime.datetime(2009, 10, 3, 0, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (PyCalendarDateTime(2009, 10, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3342,11 +3565,11 @@
""",
(
(None, True),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 1, 0, 0, tzinfo=tzutc()), False),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 1, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3369,12 +3592,12 @@
""",
(
(None, True),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 1, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2007, 11, 15, 2, 0, 0, tzinfo=tzutc()), False),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 1, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2007, 11, 15, 2, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3391,9 +3614,9 @@
""",
(
(None, False),
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), False),
- (datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()), True),
- (datetime.datetime(2009, 10, 4, 0, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
+ (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
+ (PyCalendarDateTime(2009, 10, 4, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
(
@@ -3423,7 +3646,7 @@
END:VCALENDAR
""",
(
- (datetime.datetime(2007, 11, 14, 0, 0, 0, tzinfo=tzutc()), True),
+ (PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), True),
)
),
(
@@ -3438,7 +3661,7 @@
END:VCALENDAR
""",
(
- (datetime.datetime(2007, 11, 15, 0, 0, 0, tzinfo=tzutc()), False),
+ (PyCalendarDateTime(2007, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), False),
)
),
)
@@ -3727,7 +3950,7 @@
),
),
(
- datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
),
@@ -3771,7 +3994,7 @@
),
),
(
- datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", False,),
@@ -3824,7 +4047,7 @@
),
),
(
- datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", False,),
@@ -3868,19 +4091,19 @@
),
),
(
- datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
),
),
(
- datetime.datetime(2008, 6, 3, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", True,),
),
),
(
- datetime.datetime(2008, 6, 4, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", True,),
),
@@ -3941,21 +4164,21 @@
),
),
(
- datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", True,),
),
),
(
- datetime.datetime(2008, 6, 3, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", True,),
),
),
(
- datetime.datetime(2008, 6, 4, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", False,),
@@ -4038,7 +4261,7 @@
),
),
(
- datetime.datetime(2008, 6, 2, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 2, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", True,),
@@ -4046,7 +4269,7 @@
),
),
(
- datetime.datetime(2008, 6, 3, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 3, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", True,),
@@ -4054,7 +4277,7 @@
),
),
(
- datetime.datetime(2008, 6, 4, 12, 0, 0, tzinfo=tzutc()),
+ PyCalendarDateTime(2008, 6, 4, 12, 0, 0, tzid=PyCalendarTimezone(utc=True)),
(
("", False,),
("user01", False,),
Modified: CalendarServer/trunk/twistedcaldav/test/test_localization.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_localization.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/test/test_localization.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -19,7 +19,8 @@
from twistedcaldav.localization import translationTo
from twistedcaldav.ical import Component
from twistedcaldav.test.util import TestCase
-from datetime import time
+from pycalendar.datetime import PyCalendarDateTime
+
import os
def getComp(str):
@@ -64,21 +65,21 @@
with translationTo('English', localeDir=localeDir) as t:
- self.assertEquals(t.dtTime(time(0,0)), "12:00 AM")
- self.assertEquals(t.dtTime(time(12,0)), "12:00 PM")
- self.assertEquals(t.dtTime(time(23,59)), "11:59 PM")
- self.assertEquals(t.dtTime(time(6,5)), "6:05 AM")
- self.assertEquals(t.dtTime(time(16,5)), "4:05 PM")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 0, 0, 0)), "12:00 AM")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12, 0, 0)), "12:00 PM")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "11:59 PM")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 6, 5, 0)), "6:05 AM")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16, 5, 0)), "4:05 PM")
def test_TimeFormatting24Hour(self):
with translationTo('pig', localeDir=localeDir) as t:
- self.assertEquals(t.dtTime(time(0,0)), "00:00")
- self.assertEquals(t.dtTime(time(12,0)), "12:00")
- self.assertEquals(t.dtTime(time(23,59)), "23:59")
- self.assertEquals(t.dtTime(time(6,5)), "06:05")
- self.assertEquals(t.dtTime(time(16,5)), "16:05")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 0, 0, 0)), "00:00")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12, 0, 0)), "12:00")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "23:59")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 6, 5, 0)), "06:05")
+ self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16, 5, 0)), "16:05")
def test_CalendarFormatting(self):
@@ -87,15 +88,15 @@
comp = data[0][1]
self.assertEquals(t.date(comp), "Saturday, October 25, 2008")
self.assertEquals(t.time(comp),
- (u'9:15 AM to 10:15 AM PDT', u'1 hour 1 second'))
+ (u'9:15 AM to 10:15 AM (PDT)', u'1 hour 1 second'))
comp = data[1][1]
self.assertEquals(t.time(comp),
- (u'1:15 PM to 3:15 PM PDT', u'2 hours 2 seconds'))
+ (u'1:15 PM to 3:15 PM (PDT)', u'2 hours 2 seconds'))
comp = data[2][1]
self.assertEquals(t.time(comp),
- (u'11:05 AM to 2:15 PM PDT', u'3 hours 10 minutes'))
+ (u'11:05 AM to 2:15 PM (PDT)', u'3 hours 10 minutes'))
comp = data[3][1]
self.assertEquals(t.time(comp),
@@ -103,11 +104,11 @@
comp = data[4][1]
self.assertEquals(t.time(comp),
- (u'1:15 PM PDT', ""))
+ (u'1:15 PM (PDT)', ""))
comp = data[5][1]
self.assertEquals(t.time(comp),
- (u'11:05 AM PDT to 6:15 PM EDT', u'4 hours 10 minutes'))
+ (u'11:05 AM (PDT) to 6:15 PM (EDT)', u'4 hours 10 minutes'))
self.assertEquals(t.monthAbbreviation(1), "JAN")
@@ -116,15 +117,15 @@
comp = data[0][1]
self.assertEquals(t.date(comp), 'Aturdaysay, Octoberway 25, 2008')
self.assertEquals(t.time(comp),
- (u'09:15 otay 10:15 PDT', u'1 ourhay 1 econdsay'))
+ (u'09:15 otay 10:15 (PDT)', u'1 ourhay 1 econdsay'))
comp = data[1][1]
self.assertEquals(t.time(comp),
- (u'13:15 otay 15:15 PDT', u'2 ourshay 2 econdsay'))
+ (u'13:15 otay 15:15 (PDT)', u'2 ourshay 2 econdsay'))
comp = data[2][1]
self.assertEquals(t.time(comp),
- (u'11:05 otay 14:15 PDT', u'3 ourshay 10 inutesmay'))
+ (u'11:05 otay 14:15 (PDT)', u'3 ourshay 10 inutesmay'))
comp = data[3][1]
self.assertEquals(t.time(comp),
@@ -132,10 +133,10 @@
comp = data[4][1]
self.assertEquals(t.time(comp),
- (u'13:15 PDT', ""))
+ (u'13:15 (PDT)', ""))
comp = data[5][1]
self.assertEquals(t.time(comp),
- (u'11:05 PDT otay 18:15 EDT', u'4 ourshay 10 inutesmay'))
+ (u'11:05 (PDT) otay 18:15 (EDT)', u'4 ourshay 10 inutesmay'))
self.assertEquals(t.monthAbbreviation(1), "ANJAY")
Modified: CalendarServer/trunk/twistedcaldav/test/test_mail.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_mail.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/test/test_mail.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -65,7 +65,45 @@
def test_processDSN(self):
- template = 'BEGIN:VCALENDAR\nVERSION:2.0\nCALSCALE:GREGORIAN\nMETHOD:REQUEST\nPRODID:-//example Inc.//iCal 3.0//EN\nBEGIN:VTIMEZONE\nTZID:US/Pacific\nBEGIN:STANDARD\nDTSTART:20071104T020000\nRRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\nTZNAME:PST\nTZOFFSETFROM:-0700\nTZOFFSETTO:-0800\nEND:STANDARD\nBEGIN:DAYLIGHT\nDTSTART:20070311T020000\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\nTZNAME:PDT\nTZOFFSETFROM:-0800\nTZOFFSETTO:-0700\nEND:DAYLIGHT\nEND:VTIMEZONE\nBEGIN:VEVENT\nUID:1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C\nDTSTART;TZID=US/Pacific:20080812T094500\nDTEND;TZID=US/Pacific:20080812T104500\nATTENDEE;CUTYPE=INDIVIDUAL;CN=User 01;PARTSTAT=ACCEPTED:mailto:user01 at exam\n ple.com\nATTENDEE;CUTYPE=INDIVIDUAL;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-A\n CTION;CN=nonexistant at example.com:mailto:nonexistant at example.com\nCREATED:20080812T191857Z\nDTSTAMP:20080812T191932Z\nORGANIZER;CN=User 01:mailto:xyzzy+%s at example.com\nSEQUENCE:2\nSUMMARY:New Event\nTRANSP:OPAQUE\nEND:VEVENT\nEND:VCALENDAR\n'
+ template = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+PRODID:-//example Inc.//iCal 3.0//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C
+DTSTART;TZID=US/Pacific:20080812T094500
+DTEND;TZID=US/Pacific:20080812T104500
+ATTENDEE;CUTYPE=INDIVIDUAL;CN=User 01;PARTSTAT=ACCEPTED:mailto:user01 at exam
+ ple.com
+ATTENDEE;CUTYPE=INDIVIDUAL;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-A
+ CTION;CN=nonexistant at example.com:mailto:nonexistant at example.com
+CREATED:20080812T191857Z
+DTSTAMP:20080812T191932Z
+ORGANIZER;CN=User 01:mailto:xyzzy+%s at example.com
+SEQUENCE:2
+SUMMARY:New Event
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+"""
# Make sure an unknown token is not processed
calBody = template % "bogus_token"
@@ -80,7 +118,42 @@
"xyzzy", echo)
self.assertEquals(organizer, 'mailto:user01 at example.com')
self.assertEquals(attendee, 'mailto:user02 at example.com')
- self.assertEquals(str(calendar), 'BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nMETHOD:REQUEST\r\nPRODID:-//example Inc.//iCal 3.0//EN\r\nBEGIN:VTIMEZONE\r\nTZID:US/Pacific\r\nBEGIN:STANDARD\r\nDTSTART:20071104T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\nTZNAME:PST\r\nTZOFFSETFROM:-0700\r\nTZOFFSETTO:-0800\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nDTSTART:20070311T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\nTZNAME:PDT\r\nTZOFFSETFROM:-0800\r\nTZOFFSETTO:-0700\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C\r\nDTSTART;TZID=US/Pacific:20080812T094500\r\nDTEND;TZID=US/Pacific:20080812T104500\r\nCREATED:20080812T191857Z\r\nDTSTAMP:20080812T191932Z\r\nORGANIZER;CN=User 01:mailto:user01 at example.com\r\nREQUEST-STATUS:5.1;Service unavailable\r\nSEQUENCE:2\r\nSUMMARY:New Event\r\nTRANSP:OPAQUE\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n')
+ self.assertEquals(str(calendar), """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+PRODID:-//example Inc.//iCal 3.0//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C
+DTSTART;TZID=US/Pacific:20080812T094500
+DTEND;TZID=US/Pacific:20080812T104500
+CREATED:20080812T191857Z
+DTSTAMP:20080812T191932Z
+ORGANIZER;CN=User 01:mailto:user01 at example.com
+REQUEST-STATUS:5.1;Service unavailable
+SEQUENCE:2
+SUMMARY:New Event
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"))
self.assertEquals(msgId, 'xyzzy')
@@ -129,8 +202,7 @@
# have added an attendee back in with a "5.1;Service unavailable"
# schedule-status
attendeeProp = calendar.mainComponent().getAttendeeProperty([attendee])
- self.assertEquals(attendeeProp.paramValue("SCHEDULE-STATUS"),
- iTIPRequestStatus.SERVICE_UNAVAILABLE)
+ self.assertEquals(attendeeProp.parameterValue("SCHEDULE-STATUS"), iTIPRequestStatus.SERVICE_UNAVAILABLE)
@inlineCallbacks
Modified: CalendarServer/trunk/twistedcaldav/test/test_multiget.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_multiget.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/test/test_multiget.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -214,7 +214,7 @@
self.fail("Got calendar for unexpected UID %r" % (uid,))
if data:
- original_calendar = ical.Component.fromStream(data[uid])
+ original_calendar = ical.Component.fromString(data[uid])
else:
original_filename = file(os.path.join(self.holidays_dir, uid + ".ics"))
original_calendar = ical.Component.fromStream(original_filename)
Modified: CalendarServer/trunk/twistedcaldav/test/test_timezones.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_timezones.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/test/test_timezones.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -16,11 +16,11 @@
import twistedcaldav.test.util
from twistedcaldav.ical import Component
-from vobject.icalendar import utc, getTzid
-from vobject.icalendar import registerTzid
from twistedcaldav.timezones import TimezoneCache, TimezoneException
from twistedcaldav.timezones import readTZ, listTZs
-import datetime
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
+
import os
class TimezoneProblemTest (twistedcaldav.test.util.TestCase):
@@ -30,6 +30,10 @@
data_dir = os.path.join(os.path.dirname(__file__), "data")
+ def tearDown(self):
+ TimezoneCache.clear()
+ TimezoneCache.create()
+
def doTest(self, filename, dtstart, dtend, testEqual=True):
if testEqual:
@@ -40,7 +44,7 @@
calendar = Component.fromStream(file(os.path.join(self.data_dir, filename)))
if calendar.name() != "VCALENDAR": self.fail("Calendar is not a VCALENDAR")
- instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
+ instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
@@ -54,83 +58,83 @@
Properties in components
"""
- oldtzid = getTzid("America/New_York")
- try:
- registerTzid("America/New_York", None)
- self.doTest("TruncatedApr01.ics", datetime.datetime(2007, 04, 01, 16, 0, 0, tzinfo=utc), datetime.datetime(2007, 04, 01, 17, 0, 0, tzinfo=utc))
- finally:
- registerTzid("America/New_York", oldtzid)
+ TimezoneCache.create("")
+ TimezoneCache.clear()
+ self.doTest(
+ "TruncatedApr01.ics",
+ PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True))
+ )
+
def test_truncatedDec(self):
"""
Properties in components
"""
- oldtzid = getTzid("America/New_York")
- try:
- registerTzid("America/New_York", None)
- self.doTest("TruncatedDec10.ics", datetime.datetime(2007, 12, 10, 17, 0, 0, tzinfo=utc), datetime.datetime(2007, 12, 10, 18, 0, 0, tzinfo=utc))
- finally:
- registerTzid("America/New_York", oldtzid)
+ TimezoneCache.create("")
+ TimezoneCache.clear()
+ self.doTest(
+ "TruncatedDec10.ics",
+ PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True))
+ )
+
def test_truncatedAprThenDecFail(self):
"""
Properties in components
"""
- if TimezoneCache.activeCache:
- TimezoneCache.activeCache.unregister()
- oldtzid = getTzid("America/New_York")
- try:
- registerTzid("America/New_York", None)
- self.doTest(
- "TruncatedApr01.ics",
- datetime.datetime(2007, 04, 01, 16, 0, 0, tzinfo=utc),
- datetime.datetime(2007, 04, 01, 17, 0, 0, tzinfo=utc),
- )
- self.doTest(
- "TruncatedDec10.ics",
- datetime.datetime(2007, 12, 10, 17, 0, 0, tzinfo=utc),
- datetime.datetime(2007, 12, 10, 18, 0, 0, tzinfo=utc),
- testEqual=False
- )
- finally:
- registerTzid("America/New_York", oldtzid)
+ TimezoneCache.create("")
+ TimezoneCache.clear()
+ self.doTest(
+ "TruncatedApr01.ics",
+ PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ )
+ self.doTest(
+ "TruncatedDec10.ics",
+ PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True)),
+ testEqual=False
+ )
+
def test_truncatedAprThenDecOK(self):
"""
Properties in components
"""
- oldtzid = getTzid("America/New_York")
- try:
- registerTzid("America/New_York", None)
- tzcache = TimezoneCache()
- tzcache.register()
- self.doTest(
- "TruncatedApr01.ics",
- datetime.datetime(2007, 04, 01, 16, 0, 0, tzinfo=utc),
- datetime.datetime(2007, 04, 01, 17, 0, 0, tzinfo=utc),
- )
- self.doTest(
- "TruncatedDec10.ics",
- datetime.datetime(2007, 12, 10, 17, 0, 0, tzinfo=utc),
- datetime.datetime(2007, 12, 10, 18, 0, 0, tzinfo=utc),
- )
- tzcache.unregister()
- finally:
- registerTzid("America/New_York", oldtzid)
+ TimezoneCache.create()
+ self.doTest(
+ "TruncatedApr01.ics",
+ PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ )
+ self.doTest(
+ "TruncatedDec10.ics",
+ PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True)),
+ )
+
def test_truncatedDecThenApr(self):
"""
Properties in components
"""
- oldtzid = getTzid("America/New_York")
- try:
- registerTzid("America/New_York", None)
- self.doTest("TruncatedDec10.ics", datetime.datetime(2007, 12, 10, 17, 0, 0, tzinfo=utc), datetime.datetime(2007, 12, 10, 18, 0, 0, tzinfo=utc))
- self.doTest("TruncatedApr01.ics", datetime.datetime(2007, 04, 01, 16, 0, 0, tzinfo=utc), datetime.datetime(2007, 04, 01, 17, 0, 0, tzinfo=utc))
- finally:
- registerTzid("America/New_York", oldtzid)
+ TimezoneCache.create("")
+ TimezoneCache.clear()
+ self.doTest(
+ "TruncatedDec10.ics",
+ PyCalendarDateTime(2007, 12, 10, 17, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 12, 10, 18, 0, 0, PyCalendarTimezone(utc=True))
+ )
+ self.doTest(
+ "TruncatedApr01.ics",
+ PyCalendarDateTime(2007, 04, 01, 16, 0, 0, PyCalendarTimezone(utc=True)),
+ PyCalendarDateTime(2007, 04, 01, 17, 0, 0, PyCalendarTimezone(utc=True))
+ )
+
class TimezoneCacheTest (twistedcaldav.test.util.TestCase):
"""
Timezone support tests
@@ -140,19 +144,13 @@
def test_basic(self):
- registerTzid("America/New_York", None)
- registerTzid("US/Eastern", None)
+ TimezoneCache.create()
+ self.assertTrue(readTZ("America/New_York"))
+ self.assertTrue(readTZ("US/Eastern"))
- tzcache = TimezoneCache()
- tzcache.register()
- self.assertTrue(tzcache.loadTimezone("America/New_York"))
- self.assertTrue(tzcache.loadTimezone("US/Eastern"))
- tzcache.unregister()
-
def test_not_in_cache(self):
- tzcache = TimezoneCache()
- tzcache.register()
+ TimezoneCache.create()
data = """BEGIN:VCALENDAR
VERSION:2.0
@@ -186,21 +184,24 @@
calendar = Component.fromString(data)
if calendar.name() != "VCALENDAR": self.fail("Calendar is not a VCALENDAR")
- instances = calendar.expandTimeRanges(datetime.date(2100, 1, 1))
+ instances = calendar.expandTimeRanges(PyCalendarDateTime(2100, 1, 1))
for key in instances:
instance = instances[key]
start = instance.start
end = instance.end
- self.assertEqual(start, datetime.datetime(2007, 12, 25, 05, 0, 0, tzinfo=utc))
- self.assertEqual(end, datetime.datetime(2007, 12, 25, 06, 0, 0, tzinfo=utc))
+ self.assertEqual(start, PyCalendarDateTime(2007, 12, 25, 05, 0, 0, PyCalendarTimezone(utc=True)))
+ self.assertEqual(end, PyCalendarDateTime(2007, 12, 25, 06, 0, 0, PyCalendarTimezone(utc=True)))
break;
- tzcache.unregister()
class TimezonePackageTest (twistedcaldav.test.util.TestCase):
"""
Timezone support tests
"""
+ def setUp(self):
+ TimezoneCache.clear()
+ TimezoneCache.create()
+
def test_ReadTZ(self):
self.assertTrue(readTZ("America/New_York").find("TZID:America/New_York") != -1)
@@ -223,7 +224,6 @@
def test_ListTZsCached(self):
results = listTZs()
- results = listTZs()
self.assertTrue("America/New_York" in results)
self.assertTrue("Europe/London" in results)
self.assertTrue("GB" in results)
Modified: CalendarServer/trunk/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_upgrade.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/test/test_upgrade.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -1304,20 +1304,20 @@
PRODID:-//Apple Inc.//iCal 3.0//EN
BEGIN:VTIMEZONE
TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:1E238CA1-3C95-4468-B8CD-C8A399F78C71
Modified: CalendarServer/trunk/twistedcaldav/test/test_validation.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_validation.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/test/test_validation.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -44,8 +44,9 @@
self.destination.name = lambda : '1'
self.destinationParent = CalDAVResource()
self.destinationParent.name = lambda : '2'
- self.sampleCalendar = Component.fromString("""
-BEGIN:VCALENDAR
+
+ def _getSampleCalendar(self):
+ return Component.fromString("""BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
UID:12345-67890
@@ -56,6 +57,9 @@
END:VEVENT
END:VCALENDAR
""")
+
+ def _getStorer(self, calendar):
+ self.sampleCalendar = calendar
req = SimpleRequest(None, "COPY", "http://example.com/foo/bar")
self.storer = StoreCalendarObjectResource(
req,
@@ -64,8 +68,8 @@
destination_uri="http://example.com/foo/baz",
calendar=self.sampleCalendar
)
-
-
+ return self.storer
+
@inlineCallbacks
def test_simpleValidRequest(self):
"""
@@ -73,7 +77,7 @@
L{StoreCalendarObjectResource.fullValidation} results in a L{Deferred}
which fires with C{None} (and raises no exception).
"""
- self.assertEquals((yield self.storer.fullValidation()), None)
+ self.assertEquals((yield self._getStorer(self._getSampleCalendar()).fullValidation()), None)
@inlineCallbacks
@@ -87,13 +91,14 @@
"""
# Get the event, and add too many attendees to it.
+ self.sampleCalendar = self._getSampleCalendar()
eventComponent = list(self.sampleCalendar.subcomponents())[0]
for x in xrange(config.MaxAttendeesPerInstance):
eventComponent.addProperty(
- Property(u"ATTENDEE", u"mailto:user%d at example.com" % (x+3,)))
+ Property("ATTENDEE", "mailto:user%d at example.com" % (x+3,)))
try:
- yield self.storer.fullValidation()
+ yield self._getStorer(self.sampleCalendar).fullValidation()
except HTTPError, err:
element = XML(err.response.stream.mem)[0]
self.assertEquals(
Modified: CalendarServer/trunk/twistedcaldav/timezones.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/timezones.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/timezones.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -16,13 +16,9 @@
import os
-import vobject
-from vobject.icalendar import getTzid
-from vobject.icalendar import registerTzid
-
from twext.python.log import Logger
-from twistedcaldav.ical import Component
+from pycalendar.timezonedb import PyCalendarTimezoneDatabase
log = Logger()
@@ -51,114 +47,55 @@
class TimezoneCache(object):
- activeCache = None
+ dirName = None
@staticmethod
- def create():
- if TimezoneCache.activeCache is None:
- TimezoneCache.activeCache = TimezoneCache()
- TimezoneCache.activeCache.register()
+ def _getDBPath():
+ if TimezoneCache.dirName is None:
+ try:
+ import pkg_resources
+ except ImportError:
+ TimezoneCache.dirName = os.path.join(os.path.dirname(__file__), "zoneinfo")
+ else:
+ TimezoneCache.dirName = pkg_resources.resource_filename("twistedcaldav", "zoneinfo")
- def __init__(self):
- self._caching = False
+ return TimezoneCache.dirName
- def register(self):
- self.vobjectRegisterTzid = registerTzid
- vobject.icalendar.registerTzid = self.registerTzidFromCache
+ @staticmethod
+ def create(dbpath=None):
+ PyCalendarTimezoneDatabase.createTimezoneDatabase(TimezoneCache._getDBPath() if dbpath is None else dbpath)
- def unregister(self):
- vobject.icalendar.registerTzid = self.vobjectRegisterTzid
+ @staticmethod
+ def clear():
+ PyCalendarTimezoneDatabase.clearTimezoneDatabase()
- def loadTimezone(self, tzid):
- # Make sure it is not already loaded
- if getTzid(tzid) != None:
- return False
+# zoneinfo never changes in a running instance so cache all this data as we use it
+cachedTZs = {}
+cachedTZIDs = []
- tzData = readTZ(tzid)
- calendar = Component.fromString(tzData)
+def readTZ(tzid):
- if calendar.name() != "VCALENDAR":
- raise TimezoneException("%s does not contain valid iCalendar data." % (tzid,))
-
- # Check that we now have it cached
- if getTzid(tzid) == None:
- raise TimezoneException("Could not read timezone %s from timezone cache." % (tzid,))
+ if tzid not in cachedTZs:
- return True
-
- def registerTzidFromCache(self, tzid, tzinfo):
- if not self._caching:
- self._caching = True
- try:
- self.loadTimezone(tzid)
- except TimezoneException:
- # Fallback to vobject processing the actual tzdata
- log.err("Cannot load timezone data for %s from timezone cache" % (tzid,))
- self.vobjectRegisterTzid(tzid, tzinfo)
- self._caching = False
+ tzcal = PyCalendarTimezoneDatabase.getTimezoneInCalendar(tzid)
+ if tzcal:
+ cachedTZs[tzid] = str(tzcal)
else:
- self.vobjectRegisterTzid(tzid, tzinfo)
-
-try:
- # zoneinfo never changes in a running instance so cache all this data as we use it
- cachedTZs = {}
- cachedTZIDs = []
-
- import pkg_resources
-except ImportError:
- #
- # We don't have pkg_resources, so assume file paths work, since that's all we have
- #
-
- dirname = os.path.join(os.path.dirname(__file__), "zoneinfo")
- def readTZ(tzid):
-
- if tzid not in cachedTZs:
- tzpath = os.path.join(*tzid.split("/")) # Don't assume "/" from tzid is a path separator
- tzpath = os.path.join(dirname, tzpath + ".ics")
- try:
- cachedTZs[tzid] = file(tzpath).read()
- except IOError:
- raise TimezoneException("Unknown time zone: %s" % (tzid,))
-
- return cachedTZs[tzid]
+ raise TimezoneException("Unknown time zone: %s" % (tzid,))
- def listTZs(path=""):
- if not path and cachedTZIDs:
- return cachedTZIDs
+ return cachedTZs[tzid]
- result = []
- for item in os.listdir(os.path.join(dirname, path)):
- if item.find('.') == -1:
- result.extend(listTZs(os.path.join(path, item)))
- elif item.endswith(".ics"):
- result.append(os.path.join(path, item[:-4]))
-
- if not path:
- cachedTZIDs.extend(result)
- return result
-else:
- def readTZ(tzid):
- if tzid not in cachedTZs:
- # Here, "/" is always the path separator
- try:
- cachedTZs[tzid] = pkg_resources.resource_stream("twistedcaldav", "zoneinfo/%s.ics" % (tzid,)).read()
- except IOError:
- raise TimezoneException("Unknown time zone: %s" % (tzid,))
-
- return cachedTZs[tzid]
+def listTZs(path=""):
+ if not path and cachedTZIDs:
+ return cachedTZIDs
- def listTZs(path=""):
- if not path and cachedTZIDs:
- return cachedTZIDs
-
- result = []
- for item in pkg_resources.resource_listdir("twistedcaldav", os.path.join("zoneinfo", path)):
- if item.find('.') == -1:
- result.extend(listTZs(os.path.join(path, item)))
- elif item.endswith(".ics"):
- result.append(os.path.join(path, item[:-4]))
-
- if not path:
- cachedTZIDs.extend(result)
- return result
+ result = []
+ for item in os.listdir(os.path.join(TimezoneCache._getDBPath(), path)):
+ if item.find('.') == -1:
+ result.extend(listTZs(os.path.join(path, item)))
+ elif item.endswith(".ics"):
+ result.append(os.path.join(path, item[:-4]))
+
+ if not path:
+ cachedTZIDs.extend(result)
+ return result
Modified: CalendarServer/trunk/twistedcaldav/timezoneservice.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/timezoneservice.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/twistedcaldav/timezoneservice.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -40,13 +40,14 @@
from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.extensions import DAVResource,\
DAVResourceWithoutChildrenMixin
-from twistedcaldav.ical import parse_date_or_datetime
from twistedcaldav.ical import tzexpand
from twistedcaldav.resource import ReadOnlyNoCopyResourceMixIn
from twistedcaldav.timezones import TimezoneException
from twistedcaldav.timezones import listTZs
from twistedcaldav.timezones import readTZ
+from pycalendar.datetime import PyCalendarDateTime
+
class TimezoneServiceResource (ReadOnlyNoCopyResourceMixIn, DAVResourceWithoutChildrenMixin, DAVResource):
"""
Timezone Service resource.
@@ -234,7 +235,7 @@
start = request.args.get("start", ())
if len(start) != 1:
raise ValueError()
- start = parse_date_or_datetime(start[0])
+ start = PyCalendarDateTime.parseText(start[0])
except ValueError:
raise HTTPError(ErrorResponse(
responsecode.BAD_REQUEST,
@@ -246,7 +247,7 @@
end = request.args.get("end", ())
if len(end) != 1:
raise ValueError()
- end = parse_date_or_datetime(end[0])
+ end = PyCalendarDateTime.parseText(end[0])
if end <= start:
raise ValueError()
except ValueError:
Modified: CalendarServer/trunk/txdav/caldav/datastore/index_file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/index_file.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/caldav/datastore/index_file.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -39,8 +39,6 @@
except ImportError:
from pysqlite2 import dbapi2 as sqlite
-from vobject.icalendar import utc
-
from twisted.internet.defer import maybeDeferred, succeed
from twext.python.log import Logger, LoggingMixIn
@@ -48,6 +46,7 @@
from txdav.common.icommondatastore import SyncTokenValidException,\
ReservationError, IndexedSearchException
+from twistedcaldav.dateops import pyCalendarTodatetime
from twistedcaldav.ical import Component
from twistedcaldav.query import calendarquery, calendarqueryfilter
from twistedcaldav.sql import AbstractSQLDatabase
@@ -56,6 +55,10 @@
from twistedcaldav.config import config
from twistedcaldav.memcachepool import CachePoolUserMixIn
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.timezone import PyCalendarTimezone
+
log = Logger()
db_basename = db_prefix + "sqlite"
@@ -311,9 +314,10 @@
# will have been indexed with an "infinite" value always included.
maxDate, isStartDate = filter.getmaxtimerange()
if maxDate:
- maxDate = maxDate.date()
+ maxDate = maxDate.duplicate()
+ maxDate.setDateOnly(True)
if isStartDate:
- maxDate += datetime.timedelta(days=365)
+ maxDate += PyCalendarDuration(days=365)
self.testAndUpdateIndex(maxDate)
else:
# We cannot handle this filter in an indexed search
@@ -605,7 +609,7 @@
Gives all resources which have not been expanded beyond a given date
in the index
"""
- return self._db_values_for_sql("select NAME from RESOURCE where RECURRANCE_MAX < :1", minDate)
+ return self._db_values_for_sql("select NAME from RESOURCE where RECURRANCE_MAX < :1", pyCalendarTodatetime(minDate))
def reExpandResource(self, name, expand_until):
"""
@@ -635,11 +639,10 @@
# Decide how far to expand based on the component
doInstanceIndexing = False
master = calendar.masterComponent()
- if master is None or not calendar.isRecurring() and not calendar.isRecurringUnbounded():
+ if master is None or not calendar.isRecurring():
# When there is no master we have a set of overridden components - index them all.
# When there is one instance - index it.
- # When bounded - index all.
- expand = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
+ expand = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
doInstanceIndexing = True
else:
# If migrating or re-creating or config option for delayed indexing is off, always index
@@ -650,8 +653,8 @@
# by default. This is a caching parameter which affects the size of the index;
# it does not affect search results beyond this period, but it may affect
# performance of such a search.
- expand = (datetime.date.today() +
- datetime.timedelta(days=config.FreeBusyIndexExpandAheadDays))
+ expand = (PyCalendarDateTime.getToday() +
+ PyCalendarDuration(days=config.FreeBusyIndexExpandAheadDays))
if expand_until and expand_until > expand:
expand = expand_until
@@ -668,8 +671,8 @@
# occurrences into some obscenely far-in-the-future date, so we cap the caching
# period. Searches beyond this period will always be relatively expensive for
# resources with occurrences beyond this period.
- if expand > (datetime.date.today() +
- datetime.timedelta(days=config.FreeBusyIndexExpandMaxDays)):
+ if expand > (PyCalendarDateTime.getToday() +
+ PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)):
raise IndexedSearchException()
# Always do recurrence expansion even if we do not intend to index - we need this to double-check the
@@ -684,7 +687,7 @@
# Now coerce indexing to off if needed
if not doInstanceIndexing:
instances = None
- recurrenceLimit = datetime.datetime(1900, 1, 1, 0, 0, 0, tzinfo=utc)
+ recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self._delete_from_db(name, uid, False)
@@ -693,7 +696,7 @@
"""
insert into RESOURCE (NAME, UID, TYPE, RECURRANCE_MAX, ORGANIZER)
values (:1, :2, :3, :4, :5)
- """, name, uid, calendar.resourceType(), recurrenceLimit, organizer
+ """, name, uid, calendar.resourceType(), pyCalendarTodatetime(recurrenceLimit) if recurrenceLimit else None, organizer
)
resourceid = self.lastrowid
@@ -720,9 +723,9 @@
if doInstanceIndexing:
for key in instances:
instance = instances[key]
- start = instance.start.replace(tzinfo=utc)
- end = instance.end.replace(tzinfo=utc)
- float = 'Y' if instance.start.tzinfo is None else 'N'
+ start = instance.start
+ end = instance.end
+ float = 'Y' if instance.start.floating() else 'N'
transp = 'T' if instance.component.propertyValue("TRANSP") == "TRANSPARENT" else 'F'
self._db_execute(
"""
@@ -731,8 +734,8 @@
""",
resourceid,
float,
- start,
- end,
+ pyCalendarTodatetime(start),
+ pyCalendarTodatetime(end),
icalfbtype_to_indexfbtype.get(instance.component.getFBType(), 'F'),
transp
)
@@ -751,14 +754,14 @@
# Special - for unbounded recurrence we insert a value for "infinity"
# that will allow an open-ended time-range to always match it.
if calendar.isRecurringUnbounded():
- start = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
- end = datetime.datetime(2100, 1, 1, 1, 0, 0, tzinfo=utc)
+ start = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ end = PyCalendarDateTime(2100, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
float = 'N'
self._db_execute(
"""
insert into TIMESPAN (RESOURCEID, FLOAT, START, END, FBTYPE, TRANSPARENT)
values (:1, :2, :3, :4, :5, :6)
- """, resourceid, float, start, end, '?', '?'
+ """, resourceid, float, pyCalendarTodatetime(start), pyCalendarTodatetime(end), '?', '?'
)
instanceid = self.lastrowid
peruserdata = calendar.perUserTransparency(None)
Property changes on: CalendarServer/trunk/txdav/caldav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -39,7 +39,7 @@
from twistedcaldav.caldavxml import ScheduleCalendarTransp, Opaque
from twistedcaldav.config import config
from twistedcaldav.dateops import normalizeForIndex, datetimeMktime,\
- parseSQLTimestamp
+ parseSQLTimestamp, pyCalendarTodatetime
from twistedcaldav.ical import Component
from twistedcaldav.instance import InvalidOverriddenInstanceError
from twistedcaldav.memcacher import Memcacher
@@ -69,10 +69,10 @@
from twext.enterprise.dal.syntax import Len
from txdav.common.icommondatastore import IndexedSearchException
-from vobject.icalendar import utc
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.timezone import PyCalendarTimezone
-import datetime
-
from zope.interface.declarations import implements
class CalendarHome(CommonHome):
@@ -399,13 +399,11 @@
# Decide how far to expand based on the component
doInstanceIndexing = False
master = component.masterComponent()
- if ( master is None or not component.isRecurring()
- and not component.isRecurringUnbounded() ):
+ if ( master is None or not component.isRecurring() ):
# When there is no master we have a set of overridden components -
# index them all.
# When there is one instance - index it.
- # When bounded - index all.
- expand = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
+ expand = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
doInstanceIndexing = True
else:
@@ -417,8 +415,8 @@
# by default. This is a caching parameter which affects the size of the index;
# it does not affect search results beyond this period, but it may affect
# performance of such a search.
- expand = (datetime.date.today() +
- datetime.timedelta(days=config.FreeBusyIndexExpandAheadDays))
+ expand = (PyCalendarDateTime.getToday() +
+ PyCalendarDuration(days=config.FreeBusyIndexExpandAheadDays))
if expand_until and expand_until > expand:
expand = expand_until
@@ -435,8 +433,8 @@
# occurrences into some obscenely far-in-the-future date, so we cap the caching
# period. Searches beyond this period will always be relatively expensive for
# resources with occurrences beyond this period.
- if expand > (datetime.date.today() +
- datetime.timedelta(days=config.FreeBusyIndexExpandMaxDays)):
+ if expand > (PyCalendarDateTime.getToday() +
+ PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)):
raise IndexedSearchException
# Always do recurrence expansion even if we do not intend to index - we need this to double-check the
@@ -458,7 +456,7 @@
# Now coerce indexing to off if needed
if not doInstanceIndexing:
instances = None
- recurrenceLimit = datetime.datetime(1900, 1, 1, 0, 0, 0, tzinfo=utc)
+ recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
co = schema.CALENDAR_OBJECT
tr = schema.TIME_RANGE
@@ -503,7 +501,7 @@
co.DROPBOX_ID : self._dropboxID,
co.ORGANIZER : organizer,
co.RECURRANCE_MAX :
- normalizeForIndex(recurrenceLimit) if recurrenceLimit else None,
+ pyCalendarTodatetime(normalizeForIndex(recurrenceLimit)) if recurrenceLimit else None,
co.ACCESS : self._access,
co.SCHEDULE_OBJECT : self._schedule_object,
co.SCHEDULE_TAG : self._schedule_tag,
@@ -535,7 +533,7 @@
else:
values = {
co.RECURRANCE_MAX :
- normalizeForIndex(recurrenceLimit) if recurrenceLimit else None,
+ pyCalendarTodatetime(normalizeForIndex(recurrenceLimit)) if recurrenceLimit else None,
}
yield Update(
@@ -553,16 +551,18 @@
# TIME_RANGE table update
for key in instances:
instance = instances[key]
- start = instance.start.replace(tzinfo=utc)
- end = instance.end.replace(tzinfo=utc)
- float = instance.start.tzinfo is None
+ start = instance.start
+ end = instance.end
+ float = instance.start.floating()
+ start.setTimezoneUTC(True)
+ end.setTimezoneUTC(True)
transp = instance.component.propertyValue("TRANSP") == "TRANSPARENT"
instanceid = (yield Insert({
tr.CALENDAR_RESOURCE_ID : self._calendar._resourceID,
tr.CALENDAR_OBJECT_RESOURCE_ID : self._resourceID,
tr.FLOATING : float,
- tr.START_DATE : start,
- tr.END_DATE : end,
+ tr.START_DATE : pyCalendarTodatetime(start),
+ tr.END_DATE : pyCalendarTodatetime(end),
tr.FBTYPE :
icalfbtype_to_indexfbtype.get(
instance.component.getFBType(),
@@ -580,16 +580,16 @@
# Special - for unbounded recurrence we insert a value for "infinity"
# that will allow an open-ended time-range to always match it.
if component.isRecurringUnbounded():
- start = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
- end = datetime.datetime(2100, 1, 1, 1, 0, 0, tzinfo=utc)
+ start = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+ end = PyCalendarDateTime(2100, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
float = False
transp = True
instanceid = (yield Insert({
tr.CALENDAR_RESOURCE_ID : self._calendar._resourceID,
tr.CALENDAR_OBJECT_RESOURCE_ID : self._resourceID,
tr.FLOATING : float,
- tr.START_DATE : start,
- tr.END_DATE : end,
+ tr.START_DATE : pyCalendarTodatetime(start),
+ tr.END_DATE : pyCalendarTodatetime(end),
tr.FBTYPE :
icalfbtype_to_indexfbtype["UNKNOWN"],
tr.TRANSPARENT : transp,
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -104,7 +104,7 @@
"BEGIN:VALARM\r\n"
"X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
"TRIGGER:-PT20M\r\n"
- "ATTACH;VALUE=URI:Basso\r\n"
+ "ATTACH:Basso\r\n"
"ACTION:AUDIO\r\n"
"END:VALARM\r\n"
"END:VEVENT\r\n"
@@ -130,7 +130,7 @@
"BEGIN:VALARM\r\n"
"X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
"TRIGGER:-PT20M\r\n"
- "ATTACH;VALUE=URI:Basso\r\n"
+ "ATTACH:Basso\r\n"
"ACTION:AUDIO\r\n"
"END:VALARM\r\n"
"END:VEVENT\r\n"
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -15,6 +15,7 @@
##
from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks
from twisted.internet.task import deferLater
from txdav.caldav.datastore.index_file import Index, MemcachedUIDReserver
@@ -29,11 +30,10 @@
from twistedcaldav.test.util import InMemoryMemcacheProtocol
import twistedcaldav.test.util
-import datetime
+from pycalendar.datetime import PyCalendarDateTime
+
import os
-from twisted.internet.defer import inlineCallbacks
-
class MinimalCalendarObjectReplacement(object):
"""
Provide the minimal set of attributes and methods from CalDAVFile required
@@ -301,7 +301,7 @@
else:
self.assertFalse(self.db.resourceExists(name), msg=description)
- self.db.testAndUpdateIndex(datetime.date(2020, 1, 1))
+ self.db.testAndUpdateIndex(PyCalendarDateTime(2020, 1, 1))
for description, name, calendar_txt, reCreate, ok in data:
if ok:
self.assertTrue(self.db.resourceExists(name), msg=description)
Property changes on: CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
Modified: CalendarServer/trunk/txdav/caldav/datastore/util.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/util.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/caldav/datastore/util.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -89,16 +89,15 @@
# Now look at each ATTACH property and see if it might be a dropbox item
# and if so extract the id from that
- attachments = (yield calendarObject.component()
- ).getAllPropertiesInAnyComponent(
+ attachments = (yield calendarObject.component()).getAllPropertiesInAnyComponent(
"ATTACH",
depth=1,
)
for attachment in attachments:
# Make sure the value type is URI and http(s) and it is in a dropbox
- valueType = attachment.params().get("VALUE", ("TEXT",))
- if valueType[0] == "URI" and attachment.value().startswith("http"):
+ valueType = attachment.parameterValue("VALUE", "URI")
+ if valueType == "URI" and attachment.value().startswith("http"):
segments = attachment.value().split("/")
try:
if segments[-3] == "dropbox":
Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -35,16 +35,6 @@
]
-# The following imports are used by the L{} links below, but shouldn't actually
-# be imported.as they're not really needed.
-
-# from datetime import datetime, date, tzinfo
-
-# from twext.python.vcomponent import VComponent
-
-# from txdav.idav import IPropertyStore
-# from txdav.idav import ITransaction
-
class ICalendarTransaction(ICommonTransaction):
"""
Transaction functionality required to be implemented by calendar stores.
@@ -288,9 +278,9 @@
instances that occur within the time range that begins at
C{start} and ends at C{end}.
- @param start: a L{datetime} or L{date}.
- @param end: a L{datetime} or L{date}.
- @param timeZone: a L{tzinfo}.
+ @param start: a L{PyCalendarDateTime}.
+ @param end: a L{PyCalendarDateTime}.
+ @param timeZone: a L{PyCalendarTimezone}.
@return: an iterable of L{ICalendarObject}s.
"""
Property changes on: CalendarServer/trunk/txdav/carddav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
Property changes on: CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -73,7 +73,8 @@
from txdav.base.propertystore.sql import PropertyStore
from twistedcaldav.customxml import NotificationType
-from twistedcaldav.dateops import datetimeMktime, parseSQLTimestamp
+from twistedcaldav.dateops import datetimeMktime, parseSQLTimestamp,\
+ pyCalendarTodatetime
v1_schema = getModule(__name__).filePath.sibling("sql_schema_v1.sql").getContent()
@@ -302,12 +303,12 @@
def eventsOlderThan(self, cutoff, batchSize=None):
"""
Return up to the oldest batchSize events which exist completely earlier
- than "cutoff" (datetime)
+ than "cutoff" (PyCalendarDateTime)
Returns a deferred to a list of (uid, calendarName, eventName, maxDate)
tuples.
"""
- kwds = { "CutOff" : cutoff }
+ kwds = { "CutOff" : pyCalendarTodatetime(cutoff) }
if batchSize is not None:
kwds["batchSize"] = batchSize
query = self._oldEventsLimited
Modified: CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_legacy.py 2011-03-17 18:53:26 UTC (rev 7207)
+++ CalendarServer/trunk/txdav/common/datastore/sql_legacy.py 2011-03-17 18:54:11 UTC (rev 7208)
@@ -20,7 +20,6 @@
PostgreSQL data store.
"""
-import datetime
import StringIO
from twistedcaldav.sharing import SharedCollectionRecord
@@ -33,7 +32,7 @@
from twistedcaldav import carddavxml
from twistedcaldav.config import config
-from twistedcaldav.dateops import normalizeForIndex
+from twistedcaldav.dateops import normalizeForIndex, pyCalendarTodatetime
from twistedcaldav.memcachepool import CachePoolUserMixIn
from twistedcaldav.notifications import NotificationRecord
from twistedcaldav.query import (
@@ -56,6 +55,8 @@
ADDRESSBOOK_HOME_TABLE, ADDRESSBOOK_BIND_TABLE, schema)
+from pycalendar.duration import PyCalendarDuration
+
log = Logger()
indexfbtype_to_icalfbtype = {
@@ -1073,7 +1074,7 @@
"""
returnValue([row[0] for row in (
yield self._notExpandedBeyondQuery.on(
- self._txn, minDate=normalizeForIndex(minDate),
+ self._txn, minDate=pyCalendarTodatetime(normalizeForIndex(minDate)),
resourceID=self.calendar._resourceID))]
)
@@ -1138,9 +1139,10 @@
# "infinite" value always included.
maxDate, isStartDate = filter.getmaxtimerange()
if maxDate:
- maxDate = maxDate.date()
+ maxDate = maxDate.duplicate()
+ maxDate.setDateOnly(True)
if isStartDate:
- maxDate += datetime.timedelta(days=365)
+ maxDate += PyCalendarDuration(days=365)
yield self.testAndUpdateIndex(maxDate)
else:
# We cannot handler this filter in an indexed search
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110317/1ebc37c2/attachment-0001.html>
More information about the calendarserver-changes
mailing list