[CalendarServer-changes] [7162] CalendarServer/branches/users/cdaboo/pycalendar

source_changes at macosforge.org source_changes at macosforge.org
Tue Mar 8 19:41:35 PST 2011


Revision: 7162
          http://trac.macosforge.org/projects/calendarserver/changeset/7162
Author:   cdaboo at apple.com
Date:     2011-03-08 19:41:33 -0800 (Tue, 08 Mar 2011)
Log Message:
-----------
Snapshot of pycalendar work - passes all unit tests and CDT (on branch).

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/purge.py
    CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge.py
    CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge_old_events.py
    CalendarServer/branches/users/cdaboo/pycalendar/contrib/tools/dtraceanalyze.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/caldavxml.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/customxml.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/calendardata.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_calendardata.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_privateevents.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/dateops.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/freebusyurl.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/ical.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/instance.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/localization.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/mail.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/method/put_common.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/method/report_common.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarquery.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarqueryfilter.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/expression.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/sqlgenerator.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_calendarquery.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_queryfilter.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/icaldiff.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/implicit.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/itip.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/processing.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/scheduler.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_icaldiff.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_implicit.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_itip.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/sharing.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_icalendar.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_localization.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_mail.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_multiget.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_timezones.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_upgrade.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_validation.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/timezones.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/timezoneservice.py
    CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py
    CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/sql.py
    CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py
    CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/util.py
    CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/icalendarstore.py
    CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql.py
    CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql_legacy.py

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_caldavxml.py
    CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_customxml.py

Modified: CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/purge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/purge.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/purge.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -19,11 +19,8 @@
 import os
 import sys
 
-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
@@ -44,6 +41,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()
 
@@ -270,7 +268,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
@@ -531,7 +532,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
@@ -542,7 +543,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()
 
@@ -552,14 +554,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()
 
@@ -579,19 +576,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
@@ -600,18 +593,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
@@ -619,12 +608,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
@@ -640,8 +625,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)
@@ -671,7 +655,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/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge_old_events.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge_old_events.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/calendarserver/tools/test/test_purge_old_events.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/contrib/tools/dtraceanalyze.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/contrib/tools/dtraceanalyze.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/contrib/tools/dtraceanalyze.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/caldavxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/caldavxml.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/caldavxml.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
-from pycalendar.datetime import PyCalendarDateTime
 
 """
 CalDAV XML Support.
@@ -26,10 +25,8 @@
 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
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/customxml.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/customxml.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/calendardata.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/calendardata.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/calendardata.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_calendardata.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_calendardata.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_calendardata.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_privateevents.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_privateevents.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/datafilters/test/test_privateevents.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/dateops.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/dateops.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/dateops.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -44,13 +44,13 @@
     @return: the normalized PyCalendarDateTime
     """
     if not isinstance(dt, PyCalendarDateTime):
-        raise TypeError("%r is not a removeParameterValue instance" % (dt,))
+        raise TypeError("%r is not a PyCalendarDateTime instance" % (dt,))
     
     dt = dt.duplicate()
     if dt.isDateOnly():
         dt.setDateOnly(False)
         dt.setHHMMSS(0, 0, 0)
-        dt.setTimezoneUTC(True)
+        dt.setTimezoneID(None)  # Keep it floating
         return dt
     elif dt.floating():
         return dt
@@ -123,10 +123,10 @@
 def timeRangesOverlap(start1, end1, start2, end2, defaulttz = None):
     # Can't compare date-time and date only, so normalize
     # to date only if they are mixed.
-    if not start1.isDateOnly() and (start2 is not None) and start2.isDateOnly(): start1 = start1.setDateOnly(True)
-    if not start2.isDateOnly() and (start1 is not None) and start1.isDateOnly(): start2 = start2.setDateOnly(True)
-    if not end1.isDateOnly() and (end2 is not None) and end2.isDateOnly(): end1 = end1.setDateOnly(True)
-    if not end2.isDateOnly() and (end1 is not None) and end1.isDateOnly(): end2 = end2.setDateOnly(True)
+    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:
@@ -149,15 +149,14 @@
     """
     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
@@ -176,6 +175,8 @@
         
         return compareDateTime(cmp1, cmp2)
 
+    for period in periods:
+        period.adjustToUTC()
     periods.sort(cmp=sortPeriods)
     
     # Now merge overlaps and consecutive periods
@@ -191,7 +192,7 @@
         ie = periods[i].getEnd()
         if (pe >= periods[i].getStart()):
             if ie > pe:
-                periods[index] = (periods[index].getStart(), ie)
+                periods[index] = PyCalendarPeriod(periods[index].getStart(), ie)
                 pe = ie
             periods[i] = None
         else:
@@ -227,6 +228,21 @@
         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
+        )
+
 def parseSQLTimestamp(ts):
     
     # Handle case where fraction seconds may not be present
@@ -234,6 +250,18 @@
         ts += ".0"
     return datetime.datetime.strptime(ts, "%Y-%m-%d %H:%M:%S.%f")
 
+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/branches/users/cdaboo/pycalendar/twistedcaldav/freebusyurl.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/freebusyurl.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/freebusyurl.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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()
 
 
@@ -179,15 +176,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,
@@ -204,7 +201,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")
@@ -212,12 +209,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/branches/users/cdaboo/pycalendar/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/ical.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/ical.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -14,15 +14,6 @@
 # limitations under the License.
 ##
 
-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.period import PyCalendarPeriod
-from pycalendar.property import PyCalendarProperty
-from pycalendar.timezone import PyCalendarTimezone
 
 """
 iCalendar Utilities
@@ -38,6 +29,7 @@
 ]
 
 import cStringIO as StringIO
+import codecs
 import heapq
 import itertools
 
@@ -49,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()
 
@@ -188,23 +191,23 @@
     def name  (self): return self._pycalendar.getName()
 
     def value (self): return self._pycalendar.getValue().getValue()
+
+    def strvalue (self): return str(self._pycalendar.getValue())
+
     def setValue(self, 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.
         """
-        result = {}
+        result = set()
         for pyattrlist in self._pycalendar.getAttributes().values():
             for pyattr in pyattrlist:
-                result.setdefault(pyattr.getName(), []).extend(pyattr.getValues())
+                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.
@@ -212,14 +215,20 @@
         try:
             return self._pycalendar.getAttributeValue(name)
         except KeyError:
-            return None
+            return default
 
+    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()):
@@ -235,11 +244,9 @@
         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 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.
@@ -251,7 +258,7 @@
             return False
         
         # get date/date-time value
-        dt = self.getDateTimeValue().getValue()
+        dt = self._pycalendar.getValue().getValue()
         assert isinstance(dt, PyCalendarDateTime), "Not a date/date-time value: %r" % (self,)
         
         return timeRangesOverlap(dt, None, start, end, defaulttz)
@@ -296,6 +303,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
@@ -307,7 +322,10 @@
             C{stream}.
         """
         cal = PyCalendar()
-        result = cal.parse(stream)
+        try:
+            result = cal.parse(stream)
+        except PyCalendarInvalidData:
+            result = None
         if not result:
             stream.seek(0)
             raise InvalidICalendarDataError("%s" % (stream.read(),))
@@ -364,8 +382,7 @@
                 self._parent = None
         else:
             # FIXME: figure out creating an arbitrary component
-            raise NotImplementedError
-            self._pycalendar = PyCalendar.makeComponent(name)
+            self._pycalendar = PyCalendar(False) if name == "VCALENDAR" else PyCalendar.makeComponent(name, None)
             self._parent = None
 
     def __str__ (self):
@@ -456,7 +473,7 @@
         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.
         """
@@ -551,7 +568,7 @@
         if name is None:
             [properties.extend(i) for i in self._pycalendar.getProperties().values()]
         elif self._pycalendar.countProperty(name) > 0:
-            properties = self._pycalendar.getProperties()[name]
+            properties = self._pycalendar.getProperties(name)
 
         return (
             Property(None, None, None, pycalendar=p)
@@ -572,7 +589,7 @@
         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.propertyValue("DTSTART")
         return dtstart.duplicateAsUTC() if dtstart is not None else None
@@ -583,7 +600,7 @@
         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.propertyValue("DTEND")
         if dtend is None:
@@ -599,7 +616,7 @@
         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.propertyValue("DUE")
         if due is None:
@@ -614,7 +631,7 @@
         """
         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.propertyValue("RECURRENCE-ID")
         return rid.duplicateAsUTC() if rid is not None else None
@@ -627,7 +644,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")
 
@@ -638,7 +655,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
@@ -651,7 +668,7 @@
             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:
@@ -709,6 +726,7 @@
         @param property: the L{Property} to add to this component.
         """
         self._pycalendar.addProperty(property._pycalendar)
+        self._pycalendar.finalise()
 
     def removeProperty(self, property):
         """
@@ -716,6 +734,7 @@
         @param property: the L{Property} to remove from this component.
         """
         self._pycalendar.removeProperty(property._pycalendar)
+        self._pycalendar.finalise()
 
     def replaceProperty(self, property):
         """
@@ -736,7 +755,7 @@
         result = set()
 
         for property in self.properties():
-            tzid = property.paramValue("TZID")
+            tzid = property.parameterValue("TZID")
             if tzid is not None:
                 result.add(tzid)
                 break
@@ -818,13 +837,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.
         """
         
-        pytz = PyCalendarTimezone(timezone.getID()) 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
@@ -878,7 +897,6 @@
         # Add RECURRENCE-ID if not first instance
         if not first:
             newcomp.addProperty(Property("RECURRENCE-ID", instance.rid))
-            newcomp.transformAllToNative()
 
         return newcomp
 
@@ -888,7 +906,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
@@ -908,7 +926,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.
         """
@@ -922,7 +940,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.
@@ -989,7 +1007,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}
         
@@ -1006,8 +1024,7 @@
         didCancel = False
         for exdate in tuple(master.properties("EXDATE")):
             for exdateValue in exdate.value():
-                exdateValue = exdateValue.getValue()
-                if exdateValue == rid:
+                if exdateValue.getValue() == rid:
                     if allowCancelled:
                         exdate.value().remove(exdateValue)
                         if len(exdate.value()) == 0:
@@ -1058,19 +1075,24 @@
             oldduration = dtend.value() - dtstart.value()
         
         newdtstartValue = rid.duplicate()
-        if dtstart.value().local():
-            newdtstartValue.adjustTimezone(dtstart.value().getTimezone())
+        if not dtstart.value().isDateOnly():
+            if dtstart.value().local():
+                newdtstartValue.adjustTimezone(dtstart.value().getTimezone())
+        else:
+            newdtstartValue.setDateOnly(True)
             
         dtstart.setValue(newdtstartValue)
         if newcomp.hasProperty("DTEND"):
             dtend.setValue(newdtstartValue + oldduration)
 
-        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):
@@ -1098,7 +1120,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}
         """
@@ -1304,7 +1326,9 @@
                                 if fix:
                                     log.debug("Fixing mismatch")
                                     rrule.getUntil().setDateOnly(dtValue.isDateOnly())
-                                    rrule.getUntil().setHHMMSS(dtutc.getHours(), dtutc.getMinutes(), dtutc.getSeconds())
+                                    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)
@@ -1370,6 +1394,23 @@
 
         return foundOrganizer
 
+    def gettimezone(self):
+        """
+        Get the PyCalendarTimezone for a Timezone component.
+
+        @return: L{PyCalendarTimezone} if this is a VTIMEZONE, otherwise None.
+        """
+        if self.name() == "VTIMEZONE":
+            return PyCalendarTimezone(tzid=self._pycalendar.getID())
+        elif self.name() == "VCALENDAR":
+            for component in self.subcomponents():
+                if component.name() == "VTIMEZONE":
+                    return component.gettimezone()
+            else:
+                return None
+        else:
+            return None
+
     ##
     # iTIP stuff
     ##
@@ -1492,8 +1533,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
@@ -1544,8 +1585,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()
@@ -1833,8 +1874,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:
@@ -1946,7 +1987,7 @@
                 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-"):
                             p.removeParameter(paramname)
             
@@ -1995,13 +2036,14 @@
                 default_params = {"VALUE": "TEXT"}
             
             # Remove any default parameters
-            for name, value in prop.params().items():
-                if value == [default_params.get(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
 
@@ -2042,22 +2084,41 @@
                 duration = duration.value() if duration is not None else None,
             )
 
-            dtstart.setValue(timeRange.getStart().duplicateAsUTC())
+            # 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.getEnd().duplicateAsUTC())
+                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.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([value.duplicateAsUTC() for value in exdate.value()])
-                exdate.removeParameter("TZID")
+                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.removeParameter("TZID")
                 rid.setValue(rid.value().duplicateAsUTC())
-                rid.removeProperty("TZID")
 
             # Recurrence rules - we need to normalize the order of the value parts
 #            for rrule in self._pycalendar.getRecurrenceSet().getRules():
@@ -2082,7 +2143,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):
         """
@@ -2099,7 +2160,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:
@@ -2128,7 +2189,7 @@
                     continue
 
                 # Get any EMAIL parameter
-                oldemail = prop.paramsValue("EMAIL")
+                oldemail = prop.parameterValue("EMAIL")
                 if oldemail:
                     oldemail = "mailto:%s" % (oldemail,)
 
@@ -2254,8 +2315,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():
@@ -2265,52 +2324,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/branches/users/cdaboo/pycalendar/twistedcaldav/instance.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/instance.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/instance.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -13,10 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.timezone import PyCalendarTimezone
 
 """
 iCalendar Recurrence Expansion Utilities
@@ -24,6 +20,11 @@
 
 from twistedcaldav.dateops import normalizeForIndex, differenceDateTime
 
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.duration import PyCalendarDuration
+from pycalendar.period import PyCalendarPeriod
+from pycalendar.timezone import PyCalendarTimezone
+
 # The maximum number of instances we will expand out to.
 # Raise a TooManyInstancesError exception if we exceed this.
 max_allowed_instances = 1000
@@ -75,8 +76,10 @@
             
             # 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
     
@@ -104,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
@@ -167,11 +170,12 @@
         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
@@ -186,7 +190,7 @@
         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):
         """
@@ -220,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()
@@ -228,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):
         """
@@ -259,13 +265,13 @@
 
         self._addOverrideComponent(component, limit, start, due, got_master)
 
-    def _addMasterComponent(self, component, limit, start, end, duration):
+    def _addMasterComponent(self, component, limit, rulestart, start, end, duration):
         
         rrules = component.getRecurrenceSet()
         if rrules is not None:
             # Do recurrence set expansion
             expanded = []
-            limited = rrules.expand(start, PyCalendarPeriod(start, limit), expanded)
+            limited = rrules.expand(rulestart, PyCalendarPeriod(start, limit), expanded)
             for startDate in expanded:
                 startDate = normalizeForIndex(startDate)
                 endDate = startDate + duration
@@ -343,7 +349,7 @@
         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()
@@ -361,6 +367,7 @@
             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
+                period = period.getValue()
                 if period.getStart() >= limit:
                     continue
                 start = normalizeForIndex(period.getStart())
@@ -375,7 +382,7 @@
         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()

Modified: CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/localization.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/localization.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/localization.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/mail.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/mail.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/mail.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/method/put_common.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/method/put_common.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -620,7 +620,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/branches/users/cdaboo/pycalendar/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/method/report_common.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/method/report_common.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarquery.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarquery.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarquery.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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 (
-        str(start) if start else None,
-        str(end) if end else None,
-        str(startfloat) if startfloat else None,
-        str(endfloat) if endfloat else None,
+        str(pyCalendarTodatetime(start)) if start else None,
+        str(pyCalendarTodatetime(end)) if end else None,
+        str(pyCalendarTodatetime(startfloat)) if startfloat else None,
+        str(pyCalendarTodatetime(endfloat)) if endfloat else None,
     )
 
 def sqlcalendarquery(filter, calendarid=None, userid=None, generator=sqlgenerator.sqlgenerator):

Modified: CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarqueryfilter.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarqueryfilter.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/calendarqueryfilter.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
-from pycalendar.datetime import PyCalendarDateTime
 
 """
 Object model of CALDAV:filter element used in a calendar-query.
@@ -28,9 +27,10 @@
 from twistedcaldav.caldavxml import caldav_namespace, CalDAVTimeZoneElement
 from twistedcaldav.dateops import timeRangesOverlap
 from twistedcaldav.ical import Component, Property
-from vobject.icalendar import utc
-import datetime
 
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.timezone import PyCalendarTimezone
+
 log = Logger()
 
 class FilterBase(object):
@@ -83,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:
@@ -109,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)
 
@@ -119,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
 
@@ -320,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
@@ -336,7 +337,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
@@ -406,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
@@ -418,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
@@ -439,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):
@@ -508,7 +498,7 @@
         if item is None: return False
 
         if isinstance(item, Property):
-            values = [item.value()]
+            values = [item.strvalue()]
         else:
             values = item
 
@@ -527,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)
@@ -560,7 +550,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
@@ -573,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/branches/users/cdaboo/pycalendar/twistedcaldav/query/expression.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/expression.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/expression.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/query/sqlgenerator.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/sqlgenerator.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/sqlgenerator.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -266,13 +266,13 @@
     e3 = expression.notcontainsExpression("SUMMARY", "help", True)
     e5 = expression.andExpression([e1, e2, e3])
     print e5
-    sql = sqlgenerator(e5)
+    sql = sqlgenerator(e5, None, None)
     print sql.generate()
     e6 = expression.inExpression("TYPE", ("VEVENT", "VTODO",), False)
     print e6
-    sql = sqlgenerator(e6)
+    sql = sqlgenerator(e6, None, None)
     print sql.generate()
     e7 = expression.notinExpression("TYPE", ("VEVENT", "VTODO",), False)
     print e7
-    sql = sqlgenerator(e7)
+    sql = sqlgenerator(e7, None, None)
     print sql.generate()

Modified: CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_calendarquery.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_calendarquery.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_calendarquery.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_queryfilter.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_queryfilter.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/query/test/test_queryfilter.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/icaldiff.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/icaldiff.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/implicit.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/implicit.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -276,7 +276,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"):
@@ -485,7 +485,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():
@@ -494,7 +494,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:
@@ -505,15 +505,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
 
@@ -564,7 +564,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
@@ -602,10 +602,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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/itip.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/itip.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/processing.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/processing.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/scheduler.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/scheduler.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -374,7 +374,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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_icaldiff.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_icaldiff.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_implicit.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_implicit.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_implicit.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_itip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_itip.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/scheduling/test/test_itip.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/sharing.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/sharing.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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),
@@ -1125,7 +1124,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(
                 *(
                     (

Added: CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_caldavxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_caldavxml.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_caldavxml.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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())

Added: CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_customxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_customxml.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_customxml.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_icalendar.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_icalendar.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -51,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.
@@ -789,14 +955,14 @@
 
         for original, result in data:
             component = Component.fromString(original)
-            component.addPropertyToAllComponents(Property("REQUEST-STATUS", "2.0;Success"))
+            component.addPropertyToAllComponents(Property("REQUEST-STATUS", ["2.0", "Success"]))
             self.assertEqual(result, str(component).replace("\r", ""))        
 
     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
@@ -819,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
@@ -839,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
@@ -867,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
@@ -889,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
@@ -933,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
@@ -963,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
@@ -1000,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
@@ -1029,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
@@ -1065,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
@@ -1094,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
@@ -1118,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
@@ -1138,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
@@ -1166,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
@@ -1194,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
@@ -1216,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
@@ -1240,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):
         
@@ -1737,7 +1903,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 END:VEVENT
 END:VCALENDAR
 """,
@@ -1752,7 +1918,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY;COUNT=2
 END:VEVENT
 END:VCALENDAR
@@ -1771,7 +1937,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY;COUNT=2
 RDATE:20071116T010000Z
 END:VEVENT
@@ -1792,7 +1958,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY;COUNT=3
 EXDATE:20071115T000000Z
 END:VEVENT
@@ -1812,7 +1978,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY;COUNT=3
 EXDATE:20071114T000000Z
 END:VEVENT
@@ -1832,14 +1998,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:20071115T000000Z
 DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
 END:VEVENT
 END:VCALENDAR
 """,
@@ -1857,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
 """,
@@ -1879,14 +2045,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
 """,
@@ -1919,7 +2085,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 END:VEVENT
 END:VCALENDAR
 """,
@@ -1934,7 +2100,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 END:VEVENT
 END:VCALENDAR
 """,
@@ -1949,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
 """,
@@ -1971,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
 """,
@@ -1993,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
 """,
@@ -2015,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
 """,
@@ -2037,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
 """,
@@ -2059,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
 """,
@@ -2090,7 +2256,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:True
 END:VEVENT
 END:VCALENDAR
@@ -2101,7 +2267,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM2:True
 END:VEVENT
 END:VCALENDAR
@@ -2112,7 +2278,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:True
 X-ITEM2:True
 END:VEVENT
@@ -2128,7 +2294,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:True
 END:VEVENT
 END:VCALENDAR
@@ -2139,7 +2305,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM2:True
 X-ITEM3:True
 END:VEVENT
@@ -2151,7 +2317,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:True
 X-ITEM2:True
 X-ITEM3:True
@@ -2168,7 +2334,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:True
 END:VEVENT
 END:VCALENDAR
@@ -2179,7 +2345,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM2:True
 X-ITEM1:False
 END:VEVENT
@@ -2191,7 +2357,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:True
 X-ITEM2:True
 X-ITEM1:False
@@ -2208,7 +2374,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY
 X-ITEM1:True
 END:VEVENT
@@ -2216,7 +2382,7 @@
 UID:12345-67890-1
 RECURRENCE-ID:20071115T000000Z
 DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:False
 END:VEVENT
 END:VCALENDAR
@@ -2227,7 +2393,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY
 X-ITEM2:True
 END:VEVENT
@@ -2235,7 +2401,7 @@
 UID:12345-67890-1
 RECURRENCE-ID:20071115T000000Z
 DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM2:False
 END:VEVENT
 END:VCALENDAR
@@ -2246,7 +2412,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY
 X-ITEM1:True
 X-ITEM2:True
@@ -2255,7 +2421,7 @@
 UID:12345-67890-1
 RECURRENCE-ID:20071115T000000Z
 DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:False
 X-ITEM2:False
 END:VEVENT
@@ -2271,7 +2437,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY
 X-ITEM1:True
 END:VEVENT
@@ -2279,7 +2445,7 @@
 UID:12345-67890-1
 RECURRENCE-ID:20071115T000000Z
 DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:False
 END:VEVENT
 END:VCALENDAR
@@ -2290,7 +2456,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY
 X-ITEM2:True
 END:VEVENT
@@ -2302,7 +2468,7 @@
 BEGIN:VEVENT
 UID:12345-67890-1
 DTSTART:20071114T000000Z
-DURATION:P1H
+DURATION:PT1H
 RRULE:FREQ=DAILY
 X-ITEM1:True
 X-ITEM2:True
@@ -2311,7 +2477,7 @@
 UID:12345-67890-1
 RECURRENCE-ID:20071115T000000Z
 DTSTART:20071115T010000Z
-DURATION:P1H
+DURATION:PT1H
 X-ITEM1:False
 X-ITEM2:True
 END:VEVENT
@@ -2419,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:
@@ -2852,8 +3086,6 @@
         )
         
         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

Modified: CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_localization.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_localization.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_localization.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_mail.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_mail.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_mail.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_multiget.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_multiget.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_multiget.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_timezones.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_timezones.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_timezones.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_upgrade.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_upgrade.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_validation.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_validation.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/test/test_validation.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/timezones.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/timezones.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/timezones.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/twistedcaldav/timezoneservice.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/timezoneservice.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/twistedcaldav/timezoneservice.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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.
@@ -228,7 +229,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,
@@ -239,7 +240,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/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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,9 @@
                 # will have been indexed with an "infinite" value always included.
                 maxDate, isStartDate = filter.getmaxtimerange()
                 if maxDate:
-                    maxDate = maxDate.date()
+                    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 +608,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):
         """
@@ -639,7 +642,7 @@
             # 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)

Modified: CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/sql.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/sql.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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):
@@ -421,12 +421,12 @@
         doInstanceIndexing = False
         master = component.masterComponent()
         if ( master is None or not component.isRecurring()
-             and not component.isRecurringUnbounded() ):
+             or not component.isRecurringUnbounded() ):
             # 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:
             
@@ -438,8 +438,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
@@ -456,8 +456,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
@@ -479,7 +479,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
@@ -524,7 +524,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,
@@ -556,7 +556,7 @@
         else:
             values = {
                 co.RECURRANCE_MAX :
-                    normalizeForIndex(recurrenceLimit) if recurrenceLimit else None,
+                    pyCalendarTodatetime(normalizeForIndex(recurrenceLimit)) if recurrenceLimit else None,
             }
     
             yield Update(
@@ -574,16 +574,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(),
@@ -601,16 +603,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/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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)

Modified: CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/util.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/util.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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/branches/users/cdaboo/pycalendar/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/icalendarstore.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/icalendarstore.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -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.
         """
 

Modified: CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -75,7 +75,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()
 
@@ -303,12 +304,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/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql_legacy.py	2011-03-09 03:29:17 UTC (rev 7161)
+++ CalendarServer/branches/users/cdaboo/pycalendar/txdav/common/datastore/sql_legacy.py	2011-03-09 03:41:33 UTC (rev 7162)
@@ -20,7 +20,6 @@
 PostgreSQL data store.
 """
 
-import datetime
 import StringIO
 
 from twistedcaldav.sharing import SharedCollectionRecord
@@ -32,7 +31,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 calendarqueryfilter, calendarquery, \
@@ -48,6 +47,8 @@
     CALENDAR_BIND_TABLE, CALENDAR_HOME_TABLE, ADDRESSBOOK_HOME_TABLE, \
     ADDRESSBOOK_BIND_TABLE
 
+from pycalendar.duration import PyCalendarDuration
+
 log = Logger()
 
 indexfbtype_to_icalfbtype = {
@@ -948,7 +949,7 @@
         returnValue([row[0] for row in (yield self._txn.execSQL(
             "select RESOURCE_NAME from CALENDAR_OBJECT "
             "where RECURRANCE_MAX < %s and CALENDAR_RESOURCE_ID = %s",
-            [normalizeForIndex(minDate), self.calendar._resourceID]
+            [pyCalendarTodatetime(normalizeForIndex(minDate)), self.calendar._resourceID]
         ))])
 
 
@@ -1002,9 +1003,9 @@
                 # "infinite" value always included.
                 maxDate, isStartDate = filter.getmaxtimerange()
                 if maxDate:
-                    maxDate = maxDate.date()
+                    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/20110308/6a52be3e/attachment-0001.html>


More information about the calendarserver-changes mailing list