[CalendarServer-changes] [9383] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Jun 25 09:27:34 PDT 2012


Revision: 9383
          http://trac.macosforge.org/projects/calendarserver/changeset/9383
Author:   cdaboo at apple.com
Date:     2012-06-25 09:27:34 -0700 (Mon, 25 Jun 2012)
Log Message:
-----------
Remove COUNT=400 truncation behavior. Added upper/lower time range limits. Fix all tests to use current time-relative
iCalendar data.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tools/calverify.py
    CalendarServer/trunk/calendarserver/tools/test/test_calverify.py
    CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py
    CalendarServer/trunk/calendarserver/tools/validcalendardata.py
    CalendarServer/trunk/conf/caldavd-apple.plist
    CalendarServer/trunk/conf/caldavd-test.plist
    CalendarServer/trunk/conf/caldavd.plist
    CalendarServer/trunk/twistedcaldav/ical.py
    CalendarServer/trunk/twistedcaldav/instance.py
    CalendarServer/trunk/twistedcaldav/method/put_common.py
    CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
    CalendarServer/trunk/twistedcaldav/method/report_common.py
    CalendarServer/trunk/twistedcaldav/method/report_freebusy.py
    CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
    CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
    CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py
    CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/2.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/3.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/4.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/24204e8682b99527cbda64d7423acda7.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/61038c41bd02ae5daf9f7fe9d54199fd.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/84be58ced1f1bb34057e1bd7e602c9c8.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/acc1015b7dc300c1b5665f6833960994.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b0d5785f275c064117ffd1fc20f4ed40.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b495c5dd5aa53392078eb43b1f906a80.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b88dd50941e4a31520ee396fd7894c96.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/1.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/2.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/3.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/1.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/2.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/1.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/2.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/1.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/2.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/3.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/1.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/2.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/3.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/1.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/2.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/3.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/4.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/5.ics
    CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/caldav/icalendarstore.py
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
    CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
    CalendarServer/trunk/txdav/common/datastore/test/test_sql_tables.py
    CalendarServer/trunk/txdav/common/datastore/test/util.py

Added Paths:
-----------
    CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v9.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v9.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_9_to_10.sql
    CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_9_to_10.sql

Modified: CalendarServer/trunk/calendarserver/tools/calverify.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/calverify.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/calendarserver/tools/calverify.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -719,7 +719,7 @@
                 returnValue((True, "Nothing to scan"))
 
         component = Component(None, pycalendar=caldata)
-        if self.config.MaxInstancesForRRULE:
+        if getattr(self.config, "MaxInstancesForRRULE", 0):
             component.truncateRecurrence(self.config.MaxInstancesForRRULE)
         result = True
         message = ""

Modified: CalendarServer/trunk/calendarserver/tools/test/test_calverify.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_calverify.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/calendarserver/tools/test/test_calverify.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -963,6 +963,7 @@
             (yield self.calendarUnderTest(home_name, calendar_name, txn)).calendarObjectWithName(name))
         )
 
+now = PyCalendarDateTime.getToday().getYear()
 
 class CalVerifyMismatchTestsNonRecurring(CalVerifyMismatchTestsBase):
     """
@@ -977,10 +978,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISSING_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -989,7 +990,7 @@
 ATTENDEE:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     # Attendees have event, organizer does not
     MISSING_ORGANIZER_2_ICS = """BEGIN:VCALENDAR
@@ -999,10 +1000,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISSING_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1011,7 +1012,7 @@
 ATTENDEE:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISSING_ORGANIZER_3_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1020,10 +1021,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISSING_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1032,7 +1033,7 @@
 ATTENDEE:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     # Attendee partstat mismatch
     MISMATCH_ATTENDEE_1_ICS = """BEGIN:VCALENDAR
@@ -1042,10 +1043,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1054,7 +1055,7 @@
 ATTENDEE;PARTSTAT=NEEDS-ACTION:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH_ATTENDEE_2_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1063,10 +1064,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1075,7 +1076,7 @@
 ATTENDEE;PARTSTAT=NEEDS-ACTION:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH_ATTENDEE_3_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1084,10 +1085,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1096,7 +1097,7 @@
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     # Attendee events outside time range
     MISMATCH2_ATTENDEE_1_ICS = """BEGIN:VCALENDAR
@@ -1106,10 +1107,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH2_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1118,7 +1119,7 @@
 ATTENDEE;PARTSTAT=NEEDS-ACTION:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH2_ATTENDEE_2_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1127,10 +1128,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH2_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:19990307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1139,7 +1140,7 @@
 ATTENDEE;PARTSTAT=NEEDS-ACTION:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH2_ATTENDEE_3_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1148,10 +1149,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH2_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:19990307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1160,7 +1161,7 @@
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     # Organizer event outside time range
     MISMATCH_ORGANIZER_1_ICS = """BEGIN:VCALENDAR
@@ -1170,10 +1171,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:19990307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1182,7 +1183,7 @@
 ATTENDEE;PARTSTAT=NEEDS-ACTION:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now-2}
 
     MISMATCH_ORGANIZER_2_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1191,10 +1192,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1203,7 +1204,7 @@
 ATTENDEE;PARTSTAT=NEEDS-ACTION:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     # Attendee uuid3 has event with different organizer
     MISMATCH3_ATTENDEE_1_ICS = """BEGIN:VCALENDAR
@@ -1213,10 +1214,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH3_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1225,7 +1226,7 @@
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH3_ATTENDEE_2_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1234,10 +1235,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH3_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1245,7 +1246,7 @@
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:47B16BB4-DB5F-4BF6-85FE-A7DA54230F92
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH3_ATTENDEE_3_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1254,17 +1255,17 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH3_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:47B16BB4-DB5F-4BF6-85FE-A7DA54230F92
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:47B16BB4-DB5F-4BF6-85FE-A7DA54230F92
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH_ORGANIZER_3_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1273,10 +1274,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1285,7 +1286,7 @@
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     # Attendee uuid3 has event they are not invited to
     MISMATCH2_ORGANIZER_1_ICS = """BEGIN:VCALENDAR
@@ -1295,10 +1296,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH2_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1306,7 +1307,7 @@
 ATTENDEE;PARTSTAT=DECLINED:urn:uuid:47B16BB4-DB5F-4BF6-85FE-A7DA54230F92
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH2_ORGANIZER_2_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1315,10 +1316,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH2_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1326,7 +1327,7 @@
 ATTENDEE;PARTSTAT=DECLINED:urn:uuid:47B16BB4-DB5F-4BF6-85FE-A7DA54230F92
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH2_ORGANIZER_3_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1335,10 +1336,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH2_ORGANIZER_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1347,7 +1348,7 @@
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:AC478592-7783-44D1-B2AE-52359B4E8415
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
 
     requirements = {
@@ -1413,9 +1414,9 @@
         }
         output = StringIO()
         calverify = CalVerifyService(self._sqlCalendarStore, options, output, reactor, config)
-        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
+        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(now, 1, 1, 0, 0, 0))
 
-        self.assertEqual(calverify.results["Number of events to process"], 15)
+        self.assertEqual(calverify.results["Number of events to process"], 17)
         self.assertEqual(calverify.results["Missing Attendee"], set((
             ("MISSING_ATTENDEE_ICS", self.uuid1, self.uuid2,),
             ("MISSING_ATTENDEE_ICS", self.uuid1, self.uuid3,),
@@ -1478,9 +1479,9 @@
         }
         output = StringIO()
         calverify = CalVerifyService(self._sqlCalendarStore, options, output, reactor, config)
-        yield calverify.doScan(False, True, True, start=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
+        yield calverify.doScan(False, True, True, start=PyCalendarDateTime(now, 1, 1, 0, 0, 0))
 
-        self.assertEqual(calverify.results["Number of events to process"], 15)
+        self.assertEqual(calverify.results["Number of events to process"], 17)
         self.assertEqual(calverify.results["Missing Attendee"], set((
             ("MISSING_ATTENDEE_ICS", self.uuid1, self.uuid2,),
             ("MISSING_ATTENDEE_ICS", self.uuid1, self.uuid3,),
@@ -1554,7 +1555,7 @@
         # Re-scan after changes to make sure there are no errors
         self.commit()
         calverify = CalVerifyService(self._sqlCalendarStore, options, output, reactor, config)
-        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
+        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(now, 1, 1, 0, 0, 0))
 
         self.assertEqual(calverify.results["Number of events to process"], 14)
         self.assertTrue("Missing Attendee" not in calverify.results)
@@ -1580,10 +1581,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISSING_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1591,7 +1592,7 @@
 ATTENDEE:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     # Attendee partstat mismatch
     MISMATCH_ATTENDEE_1_ICS = """BEGIN:VCALENDAR
@@ -1601,10 +1602,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1612,7 +1613,7 @@
 ATTENDEE;PARTSTAT=NEEDS-ACTION:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
     MISMATCH_ATTENDEE_L1_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -1621,10 +1622,10 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:MISMATCH_ATTENDEE_ICS
-DTEND:20100307T151500Z
+DTEND:%(year)s0307T151500Z
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART:20100307T111500Z
+DTSTART:%(year)s0307T111500Z
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
@@ -1632,7 +1633,7 @@
 ATTENDEE;PARTSTAT=ACCEPTED:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
 
     requirements = {
@@ -1683,7 +1684,7 @@
         }
         output = StringIO()
         calverify = CalVerifyService(self._sqlCalendarStore, options, output, reactor, config)
-        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
+        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(now, 1, 1, 0, 0, 0))
 
         self.assertEqual(calverify.results["Number of events to process"], 3)
         self.assertEqual(calverify.results["Missing Attendee"], set((
@@ -1732,7 +1733,7 @@
         }
         output = StringIO()
         calverify = CalVerifyService(self._sqlCalendarStore, options, output, reactor, config)
-        yield calverify.doScan(False, True, True, start=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
+        yield calverify.doScan(False, True, True, start=PyCalendarDateTime(now, 1, 1, 0, 0, 0))
 
         self.assertEqual(calverify.results["Number of events to process"], 3)
         self.assertEqual(calverify.results["Missing Attendee"], set((
@@ -1763,9 +1764,9 @@
         testResults = sorted(calverify.results["Fixed Auto-Accepts"], key=lambda x:x["uid"])
         self.assertEqual(testResults[0]["path"], "/calendars/__uids__/%s/calendar/mismatched_attendee.ics" % self.uuidl1)
         self.assertEqual(testResults[0]["uid"], "MISMATCH_ATTENDEE_ICS")
-        self.assertEqual(testResults[0]["start"].getText(), "20100307T031500")
+        self.assertEqual(testResults[0]["start"].getText(), "%s0307T031500" % (now,))
         self.assertEqual(testResults[1]["uid"], "MISSING_ATTENDEE_ICS")
-        self.assertEqual(testResults[1]["start"].getText(), "20100307T031500")
+        self.assertEqual(testResults[1]["start"].getText(), "%s0307T031500" % (now,))
 
         sync_token_new1 = (yield (yield self.calendarUnderTest(self.uuid1)).syncToken())
         sync_token_newl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
@@ -1775,7 +1776,7 @@
         # Re-scan after changes to make sure there are no errors
         self.commit()
         calverify = CalVerifyService(self._sqlCalendarStore, options, output, reactor, config)
-        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(2010, 1, 1, 0, 0, 0))
+        yield calverify.doScan(False, True, False, start=PyCalendarDateTime(now, 1, 1, 0, 0, 0))
 
         self.assertEqual(calverify.results["Number of events to process"], 4)
         self.assertTrue("Missing Attendee" not in calverify.results)

Modified: CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -36,6 +36,7 @@
 import os
 
 
+now = PyCalendarDateTime.getToday().getYear()
 
 OLD_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -75,15 +76,15 @@
 BEGIN:VEVENT
 CREATED:20100303T181216Z
 UID:685BC3A1-195A-49B3-926D-388DDACA78A6
-DTEND;TZID=US/Pacific:20000307T151500
+DTEND;TZID=US/Pacific:%(year)s0307T151500
 TRANSP:OPAQUE
 SUMMARY:Ancient event
-DTSTART;TZID=US/Pacific:20000307T111500
+DTSTART;TZID=US/Pacific:%(year)s0307T111500
 DTSTAMP:20100303T181220Z
 SEQUENCE:2
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now-5}
 
 OLD_ATTACHMENT_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -125,15 +126,15 @@
 UID:57A5D1F6-9A57-4F74-9520-25C617F54B88
 TRANSP:OPAQUE
 SUMMARY:Ancient event with attachment
-DTSTART;TZID=US/Pacific:20000308T111500
-DTEND;TZID=US/Pacific:20000308T151500
+DTSTART;TZID=US/Pacific:%(year)s0308T111500
+DTEND;TZID=US/Pacific:%(year)s0308T151500
 DTSTAMP:20100303T181220Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/57A5D1F6-9A57-4F74-95
  20-25C617F54B88.dropbox
 SEQUENCE:2
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now-5}
 
 ENDLESS_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -173,16 +174,16 @@
 BEGIN:VEVENT
 CREATED:20100303T194654Z
 UID:9FDE0E4C-1495-4CAF-863B-F7F0FB15FE8C
-DTEND;TZID=US/Pacific:20000308T151500
+DTEND;TZID=US/Pacific:%(year)s0308T151500
 RRULE:FREQ=YEARLY;INTERVAL=1
 TRANSP:OPAQUE
 SUMMARY:Ancient Repeating Endless
-DTSTART;TZID=US/Pacific:20000308T111500
+DTSTART;TZID=US/Pacific:%(year)s0308T111500
 DTSTAMP:20100303T194710Z
 SEQUENCE:4
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now-5}
 
 REPEATING_AWHILE_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -222,16 +223,16 @@
 BEGIN:VEVENT
 CREATED:20100303T194716Z
 UID:76236B32-2BC4-4D78-956B-8D42D4086200
-DTEND;TZID=US/Pacific:20000309T151500
+DTEND;TZID=US/Pacific:%(year)s0309T151500
 RRULE:FREQ=YEARLY;INTERVAL=1;COUNT=3
 TRANSP:OPAQUE
 SUMMARY:Ancient Repeat Awhile
-DTSTART;TZID=US/Pacific:20000309T111500
+DTSTART;TZID=US/Pacific:%(year)s0309T111500
 DTSTAMP:20100303T194747Z
 SEQUENCE:6
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now-5}
 
 STRADDLING_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -257,16 +258,16 @@
 BEGIN:VEVENT
 CREATED:20100303T213643Z
 UID:1C219DAD-D374-4822-8C98-ADBA85E253AB
-DTEND;TZID=US/Pacific:20090508T121500
-RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=20100509T065959Z
+DTEND;TZID=US/Pacific:%(year)s0508T121500
+RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=%(until)s0509T065959Z
 TRANSP:OPAQUE
 SUMMARY:Straddling cut-off
-DTSTART;TZID=US/Pacific:20090508T111500
+DTSTART;TZID=US/Pacific:%(year)s0508T111500
 DTSTAMP:20100303T213704Z
 SEQUENCE:5
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now-2, "until":now+1}
 
 RECENT_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
@@ -292,15 +293,15 @@
 BEGIN:VEVENT
 CREATED:20100303T195159Z
 UID:F2F14D94-B944-43D9-8F6F-97F95B2764CA
-DTEND;TZID=US/Pacific:20100304T141500
+DTEND;TZID=US/Pacific:%(year)s0304T141500
 TRANSP:OPAQUE
 SUMMARY:Recent
-DTSTART;TZID=US/Pacific:20100304T120000
+DTSTART;TZID=US/Pacific:%(year)s0304T120000
 DTSTAMP:20100303T195203Z
 SEQUENCE:2
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % {"year":now}
 
 
 VCARD_1 = """BEGIN:VCARD
@@ -347,6 +348,9 @@
 
     @inlineCallbacks
     def setUp(self):
+        # Turn off delayed indexing option so we can have some useful tests
+        self.patch(config, "FreeBusyIndexDelayedExpand", False)
+
         yield super(PurgeOldEventsTests, self).setUp()
         self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
         yield self.populate()
@@ -380,45 +384,44 @@
 
     @inlineCallbacks
     def test_eventsOlderThan(self):
-        cutoff = PyCalendarDateTime(2010, 4, 1, 0, 0, 0)
+        cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
         txn = self._sqlCalendarStore.newTransaction()
 
         # Query for all old events
         results = (yield txn.eventsOlderThan(cutoff))
-        self.assertEquals(results,
-            [
-                ['home1', 'calendar1', 'old.ics', '2000-03-07 23:15:00'],
-                ['home1', 'calendar1', 'oldattachment.ics', '2000-03-08 23:15:00'],
-                ['home2', 'calendar3', 'repeating_awhile.ics', '2002-03-09 23:15:00'],
-                ['home2', 'calendar2', 'recent.ics', '2010-03-04 22:15:00'],
-            ]
+        self.assertEquals(sorted(results),
+            sorted([
+                ['home1', 'calendar1', 'old.ics', '1901-01-01 01:00:00'],
+                ['home1', 'calendar1', 'oldattachment.ics', '1901-01-01 01:00:00'],
+                ['home2', 'calendar3', 'repeating_awhile.ics', '1901-01-01 01:00:00'],
+                ['home2', 'calendar2', 'recent.ics', '%s-03-04 22:15:00' % (now,)],
+            ])
         )
 
         # Query for oldest event
         results = (yield txn.eventsOlderThan(cutoff, batchSize=1))
         self.assertEquals(results,
             [
-                ['home1', 'calendar1', 'old.ics', '2000-03-07 23:15:00'],
+                ['home1', 'calendar1', 'old.ics', '1901-01-01 01:00:00'],
             ]
         )
-    
-    test_eventsOlderThan.todo = "New lazy indexing broke this"
 
+
     @inlineCallbacks
     def test_removeOldEvents(self):
-        cutoff = PyCalendarDateTime(2010, 4, 1, 0, 0, 0)
+        cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
         txn = self._sqlCalendarStore.newTransaction()
 
         # Remove oldest event
         count = (yield txn.removeOldEvents(cutoff, batchSize=1))
         self.assertEquals(count, 1)
         results = (yield txn.eventsOlderThan(cutoff))
-        self.assertEquals(results,
-            [
-                ['home1', 'calendar1', 'oldattachment.ics', '2000-03-08 23:15:00'],
-                ['home2', 'calendar3', 'repeating_awhile.ics', '2002-03-09 23:15:00'],
-                ['home2', 'calendar2', 'recent.ics', '2010-03-04 22:15:00'],
-            ]
+        self.assertEquals(sorted(results),
+            sorted([
+                ['home1', 'calendar1', 'oldattachment.ics', '1901-01-01 01:00:00'],
+                ['home2', 'calendar3', 'repeating_awhile.ics', '1901-01-01 01:00:00'],
+                ['home2', 'calendar2', 'recent.ics', '%s-03-04 22:15:00' % (now,)],
+            ])
         )
 
         # Remove remaining oldest events
@@ -430,8 +433,6 @@
         # Remove oldest events (none left)
         count = (yield txn.removeOldEvents(cutoff))
         self.assertEquals(count, 0)
-    
-    test_removeOldEvents.todo = "New lazy indexing broke this"
 
 
     @inlineCallbacks
@@ -452,6 +453,7 @@
 
         returnValue(attachment)
 
+
     @inlineCallbacks
     def test_removeOrphanedAttachments(self):
         attachment = (yield self._addAttachment())
@@ -469,7 +471,7 @@
         self.assertTrue(os.path.exists(attachmentPath))
 
         # Delete all old events (including the event containing the attachment)
-        cutoff = PyCalendarDateTime(2010, 4, 1, 0, 0, 0)
+        cutoff = PyCalendarDateTime(now, 4, 1, 0, 0, 0)
         count = (yield txn.removeOldEvents(cutoff))
 
         # Just look for orphaned attachments but don't delete
@@ -490,26 +492,26 @@
         # Verify the file itself is gone
         self.assertFalse(os.path.exists(attachmentPath))
 
+
     @inlineCallbacks
     def test_purgeOldEvents(self):
 
         # Dry run
         total = (yield purgeOldEvents(self._sqlCalendarStore, self.directory,
-            self.rootResource, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, dryrun=True,
+            self.rootResource, PyCalendarDateTime(now, 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, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, verbose=False))
+            self.rootResource, PyCalendarDateTime(now, 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, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, verbose=False))
+            self.rootResource, PyCalendarDateTime(now, 4, 1, 0, 0, 0), 2, verbose=False))
         self.assertEquals(total, 0)
 
-    test_purgeOldEvents.todo = "New lazy indexing broke this"
 
     @inlineCallbacks
     def test_purgeUID(self):
@@ -533,7 +535,7 @@
         # Purge home1
         total, ignored = (yield purgeUID(self._sqlCalendarStore, "home1", self.directory,
             self.rootResource, verbose=False, proxies=False,
-            when=PyCalendarDateTime(2010, 4, 1, 12, 0, 0, 0, PyCalendarTimezone(utc=True))))
+            when=PyCalendarDateTime(now, 4, 1, 12, 0, 0, 0, PyCalendarTimezone(utc=True))))
 
         # 2 items deleted: 1 event and 1 vcard
         self.assertEquals(total, 2)
@@ -589,7 +591,7 @@
 
         # Remove old events first
         total = (yield purgeOldEvents(self._sqlCalendarStore, self.directory,
-            self.rootResource, PyCalendarDateTime(2010, 4, 1, 0, 0, 0), 2, verbose=False))
+            self.rootResource, PyCalendarDateTime(now, 4, 1, 0, 0, 0), 2, verbose=False))
         self.assertEquals(total, 4)
 
         # Dry run
@@ -607,4 +609,3 @@
             dryrun=False, verbose=False))
         self.assertEquals(total, 0)
 
-    test_purgeOrphanedAttachments.todo = "New lazy indexing broke this"

Modified: CalendarServer/trunk/calendarserver/tools/validcalendardata.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/validcalendardata.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/calendarserver/tools/validcalendardata.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -155,7 +155,7 @@
         truncated = False
         try:
             component = Component.fromString(self.input.read())
-            if config.MaxInstancesForRRULE != 0:
+            if getattr(self.config, "MaxInstancesForRRULE", 0) != 0:
                 truncated = component.truncateRecurrence(config.MaxInstancesForRRULE)
             component.validCalendarData(doFix=False, validateRecurrences=True)
             component.validCalendarForCalDAV(methodAllowed=True)

Modified: CalendarServer/trunk/conf/caldavd-apple.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-apple.plist	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/conf/caldavd-apple.plist	2012-06-25 16:27:34 UTC (rev 9383)
@@ -161,12 +161,7 @@
     <key>MaxAllowedInstances</key>
     <integer>3000</integer>
 
-    <!-- Maximum number of instances allowed for a single RRULE -->
-    <!-- 0 for no limit -->
-    <key>MaxInstancesForRRULE</key>
-    <integer>400</integer>
 
-
     <!--
         Directory service
 

Modified: CalendarServer/trunk/conf/caldavd-test.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-test.plist	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/conf/caldavd-test.plist	2012-06-25 16:27:34 UTC (rev 9383)
@@ -167,11 +167,6 @@
     <key>MaxAllowedInstances</key>
     <integer>3000</integer>
 
-    <!-- Maximum number of instances allowed for a single RRULE -->
-    <!-- 0 for no limit -->
-    <key>MaxInstancesForRRULE</key>
-    <integer>400</integer>
-
     <!--
         Directory service
 

Modified: CalendarServer/trunk/conf/caldavd.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd.plist	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/conf/caldavd.plist	2012-06-25 16:27:34 UTC (rev 9383)
@@ -149,12 +149,7 @@
     <key>MaxAllowedInstances</key>
     <integer>3000</integer>
 
-    <!-- Maximum number of instances allowed for a single RRULE -->
-    <!-- 0 for no limit -->
-    <key>MaxInstancesForRRULE</key>
-    <integer>400</integer>
 
-
     <!--
         Directory service
 

Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/ical.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -978,7 +978,7 @@
         # so that all-day date/times remain that way. However, when doing the timeRangesOverlap test below, we
         # Need to convert the all-days to floating (T000000) so that the timezone overlap calculation can be done
         # properly.
-        instances = self.expandTimeRanges(end, normalizeFunction=normalizeForExpand)
+        instances = self.expandTimeRanges(end, lowerLimit=start, normalizeFunction=normalizeForExpand)
         first = True
         for key in instances:
             instance = instances[key]
@@ -1051,7 +1051,7 @@
         )
         return self.cachedInstances
 
-    def expandTimeRanges(self, limit, ignoreInvalidInstances=False, normalizeFunction=normalizeForIndex):
+    def expandTimeRanges(self, limit, lowerLimit=None, ignoreInvalidInstances=False, normalizeFunction=normalizeForIndex):
         """
         Expand the set of recurrence instances for the components
         contained within this VCALENDAR component. We will assume
@@ -1063,9 +1063,9 @@
         """
         
         componentSet = self.subcomponents()
-        return self.expandSetTimeRanges(componentSet, limit, ignoreInvalidInstances, normalizeFunction=normalizeFunction)
+        return self.expandSetTimeRanges(componentSet, limit, lowerLimit=lowerLimit, ignoreInvalidInstances=ignoreInvalidInstances, normalizeFunction=normalizeFunction)
     
-    def expandSetTimeRanges(self, componentSet, limit, ignoreInvalidInstances=False, normalizeFunction=normalizeForIndex):
+    def expandSetTimeRanges(self, componentSet, limit, lowerLimit=None, ignoreInvalidInstances=False, normalizeFunction=normalizeForIndex):
         """
         Expand the set of recurrence instances up to the specified date limit.
         What we do is first expand the master instance into the set of generate
@@ -1092,7 +1092,7 @@
         
         # Set of instances to return
         instances = InstanceList(ignoreInvalidInstances=ignoreInvalidInstances, normalizeFunction=normalizeFunction)
-        instances.expandTimeRanges(componentSet, limit)
+        instances.expandTimeRanges(componentSet, limit, lowerLimit=lowerLimit)
         return instances
 
     def getComponentInstances(self):

Modified: CalendarServer/trunk/twistedcaldav/instance.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/instance.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/instance.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -88,6 +88,7 @@
     def __init__(self, ignoreInvalidInstances=False, normalizeFunction=normalizeForIndex):
         self.instances = {}
         self.limit = None
+        self.lowerLimit = None
         self.ignoreInvalidInstances = ignoreInvalidInstances
         self.normalizeFunction = normalizeFunction
         
@@ -99,7 +100,7 @@
     def __getitem__(self, key):
         return self.instances[key]
 
-    def expandTimeRanges(self, componentSet, limit):
+    def expandTimeRanges(self, componentSet, limit, lowerLimit=None):
         """
         Expand the set of recurrence instances up to the specified date limit.
         What we do is first expand the master instance into the set of generate
@@ -119,40 +120,40 @@
                 if component.hasProperty("RECURRENCE-ID"):
                     overrides.append(component)
                 else:
-                    self._addMasterEventComponent(component, limit)
+                    self._addMasterEventComponent(component, lowerLimit, limit)
                     got_master = True
             elif component.name() == "VTODO":
                 if component.hasProperty("RECURRENCE-ID"):
                     overrides.append(component)
                 else:
-                    self._addMasterToDoComponent(component, limit)
+                    self._addMasterToDoComponent(component, lowerLimit, limit)
                     got_master = True
             elif component.name() == "VJOURNAL":
                 #TODO: VJOURNAL
                 raise NotImplementedError("VJOURNAL recurrence expansion not supported yet")
             elif component.name() == "VFREEBUSY":
-                self._addFreeBusyComponent(component, limit)
+                self._addFreeBusyComponent(component, lowerLimit, limit)
             elif component.name() == "VAVAILABILITY":
-                self._addAvailabilityComponent(component, limit)
+                self._addAvailabilityComponent(component, lowerLimit, limit)
             elif component.name() == "AVAILABLE":
                 if component.hasProperty("RECURRENCE-ID"):
                     overrides.append(component)
                 else:
                     # AVAILABLE components are just like VEVENT components
-                    self._addMasterEventComponent(component, limit)
+                    self._addMasterEventComponent(component, lowerLimit, limit)
                     got_master = True
             
         for component in overrides:
             if component.name() == "VEVENT":
-                self._addOverrideEventComponent(component, limit, got_master)
+                self._addOverrideEventComponent(component, lowerLimit, limit, got_master)
             elif component.name() == "VTODO":
-                self._addOverrideToDoComponent(component, limit, got_master)
+                self._addOverrideToDoComponent(component, lowerLimit, limit, got_master)
             elif component.name() == "VJOURNAL":
                 #TODO: VJOURNAL
                 raise NotImplementedError("VJOURNAL recurrence expansion not supported yet")
             elif component.name() == "AVAILABLE":
                 # AVAILABLE components are just like VEVENT components
-                self._addOverrideEventComponent(component, limit, got_master)
+                self._addOverrideEventComponent(component, lowerLimit, limit, got_master)
 
     def addInstance(self, instance):
         """
@@ -191,7 +192,7 @@
         
         return (rulestart, start, end, duration,)
 
-    def _addMasterEventComponent(self, component, limit):
+    def _addMasterEventComponent(self, component, lowerLimit, upperlimit):
         """
         Add the specified master VEVENT Component to the instance list, expanding it
         within the supplied time range.
@@ -204,9 +205,9 @@
             return
         rulestart, start, end, duration = details
 
-        self._addMasterComponent(component, limit, rulestart, start, end, duration)
+        self._addMasterComponent(component, lowerLimit, upperlimit, rulestart, start, end, duration)
 
-    def _addOverrideEventComponent(self, component, limit, got_master):
+    def _addOverrideEventComponent(self, component, lowerLimit, upperlimit, got_master):
         """
         Add the specified overridden VEVENT Component to the instance list, replacing 
         the one generated by the master component.
@@ -221,7 +222,7 @@
             return
         _ignore_rulestart, start, end, _ignore_duration = details
 
-        self._addOverrideComponent(component, limit, start, end, got_master)
+        self._addOverrideComponent(component, lowerLimit, upperlimit, start, end, got_master)
 
     def _getMasterToDoDetails(self, component):
         """
@@ -268,7 +269,7 @@
 
         return (rulestart, start, end, duration,)
 
-    def _addMasterToDoComponent(self, component, limit):
+    def _addMasterToDoComponent(self, component, lowerLimit, upperlimit):
         """
         Add the specified master VTODO Component to the instance list, expanding it
         within the supplied time range.
@@ -280,9 +281,9 @@
             return
         rulestart, start, end, duration = details
 
-        self._addMasterComponent(component, limit, rulestart, start, end, duration)
+        self._addMasterComponent(component, lowerLimit, upperlimit, rulestart, start, end, duration)
 
-    def _addOverrideToDoComponent(self, component, limit, got_master):
+    def _addOverrideToDoComponent(self, component, lowerLimit, upperlimit, got_master):
         """
         Add the specified overridden VTODO Component to the instance list, replacing 
         the one generated by the master component.
@@ -297,9 +298,9 @@
             return
         _ignore_rulestart, start, end, _ignore_duration = details
 
-        self._addOverrideComponent(component, limit, start, end, got_master)
+        self._addOverrideComponent(component, lowerLimit, upperlimit, start, end, got_master)
 
-    def _addMasterComponent(self, component, limit, rulestart, start, end, duration):
+    def _addMasterComponent(self, component, lowerLimit, upperlimit, rulestart, start, end, duration):
         
         rrules = component.getRecurrenceSet()
         if rrules is not None and rulestart is not None:
@@ -309,23 +310,29 @@
             # than the master DTSTART, and if we exclude those, the associated
             # overridden instances will cause an InvalidOverriddenInstance.
             limited = rrules.expand(rulestart,
-                PyCalendarPeriod(PyCalendarDateTime(1900,1,1), limit), expanded)
+                PyCalendarPeriod(PyCalendarDateTime(1900,1,1), upperlimit), expanded)
             for startDate in expanded:
                 startDate = self.normalizeFunction(startDate)
                 endDate = startDate + duration
-                self.addInstance(Instance(component, startDate, endDate))
+                if lowerLimit is None or endDate >= lowerLimit:
+                    self.addInstance(Instance(component, startDate, endDate))
+                else:
+                    self.lowerLimit = lowerLimit
             if limited:
-                self.limit = limit
+                self.limit = upperlimit
         else:
             # Always add main instance if included in range.
-            if start < limit:
-                start = self.normalizeFunction(start)
-                end = self.normalizeFunction(end)
-                self.addInstance(Instance(component, start, end))
+            if start < upperlimit:
+                if lowerLimit is None or end >= lowerLimit:
+                    start = self.normalizeFunction(start)
+                    end = self.normalizeFunction(end)
+                    self.addInstance(Instance(component, start, end))
+                else:
+                    self.lowerLimit = lowerLimit
             else:
-                self.limit = limit
+                self.limit = upperlimit
     
-    def _addOverrideComponent(self, component, limit, start, end, got_master):
+    def _addOverrideComponent(self, component, lowerLimit, upperlimit, start, end, got_master):
 
         # Get the recurrence override info
         rid = component.getRecurrenceIDUTC()
@@ -337,12 +344,14 @@
         rid = self.normalizeFunction(rid)
 
         # Make sure start is within the limit
-        if start > limit and rid > limit:
+        if start > upperlimit and rid > upperlimit:
             return
+        if lowerLimit is not None and end < lowerLimit and rid < lowerLimit:
+            return
 
         # Make sure override RECURRENCE-ID is a valid instance of the master
         if got_master:
-            if str(rid) not in self.instances and rid < limit:
+            if str(rid) not in self.instances and rid < upperlimit:
                 if self.ignoreInvalidInstances:
                     return
                 else:
@@ -382,7 +391,7 @@
                 # Now replacing existing entry with the new one
                 self.addInstance(Instance(component, start, end, originalStart, False, False))
 
-    def _addFreeBusyComponent(self, component, limit):
+    def _addFreeBusyComponent(self, component, lowerLimit, upperlimit):
         """
         Add the specified master VFREEBUSY Component to the instance list, expanding it
         within the supplied time range.
@@ -391,14 +400,18 @@
         """
 
         start = component.getStartDateUTC()
-        if start is not None and start >= limit:
-            # If the free busy is beyond the end of the range we want, ignore it
-            return
-
         end = component.getEndDateUTC()
         if end is None and start is not None:
             raise ValueError("VFREEBUSY component must have both DTSTART and DTEND: %r" % (component, ))
 
+        # If the free busy is beyond the end of the range we want, ignore it
+        if start is not None and start >= upperlimit:
+            return
+
+        # If the free busy is before the start of the range we want, ignore it
+        if lowerLimit is not None and end is not None and end < lowerLimit:
+            return            
+
         # Now look at each FREEBUSY property
         for fb in component.properties("FREEBUSY"):
             # Look at each period in the property
@@ -406,13 +419,13 @@
             for period in fb.value():
                 # Ignore if period starts after limit
                 period = period.getValue()
-                if period.getStart() >= limit:
+                if period.getStart() >= upperlimit:
                     continue
                 start = self.normalizeFunction(period.getStart())
                 end = self.normalizeFunction(period.getEnd())
                 self.addInstance(Instance(component, start, end))
 
-    def _addAvailabilityComponent(self, component, limit):
+    def _addAvailabilityComponent(self, component, lowerLimit, upperlimit):
         """
         Add the specified master VAVAILABILITY Component to the instance list, expanding it
         within the supplied time range. VAVAILABILITY components are not recurring, they have an
@@ -424,14 +437,17 @@
         """
 
         start = component.getStartDateUTC()
-        if start is not None and start >= limit:
-            # If the free busy is beyond the end of the range we want, ignore it
+        if start is not None and start >= upperlimit:
+            # If the availability is beyond the end of the range we want, ignore it
             return
         if start is None:
             start = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
         start = self.normalizeFunction(start)
 
         end = component.getEndDateUTC()
+        if lowerLimit is not None and end is not None and end < lowerLimit:
+            # If the availability is before the start of the range we want, ignore it
+            return            
         if end is None:
             end = PyCalendarDateTime(2100, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
         end = self.normalizeFunction(end)

Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -650,24 +650,6 @@
         return succeed(None)
 
 
-    def truncateRecurrence(self):
-        
-        if config.MaxInstancesForRRULE != 0:
-            try:
-                result = self.calendar.truncateRecurrence(config.MaxInstancesForRRULE)
-            except (ValueError, TypeError), ex:
-                log.err("Cannot truncate calendar resource: %s" % (ex,))
-                raise HTTPError(ErrorResponse(
-                    responsecode.FORBIDDEN,
-                    (caldav_namespace, "valid-calendar-data"),
-                   "Cannot truncate recurrences",
-                ))
-            if result:
-                self.calendardata = None
-            return result
-        else:
-            return False
-
     @inlineCallbacks
     def preservePrivateComments(self):
         # Check for private comments on the old resource and the new resource and re-insert
@@ -1198,9 +1180,6 @@
                         ))
 
 
-            # Handle RRULE truncation
-            rruleChanged = self.truncateRecurrence()
-
             # Preserve private comments
             yield self.preservePrivateComments()
     
@@ -1255,7 +1234,7 @@
             response = (yield self.doStore(data_changed))
 
             # Must not set ETag in response if data changed
-            if did_implicit_action or rruleChanged or dropboxChanged or alarmChanged:
+            if did_implicit_action or dropboxChanged or alarmChanged:
                 def _removeEtag(request, response):
                     response.headers.removeHeader('etag')
                     return response

Modified: CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,18 +20,18 @@
 
 __all__ = ["report_urn_ietf_params_xml_ns_caldav_calendar_query"]
 
-from twext.python.log import Logger
-from twext.web2.dav.http import ErrorResponse
-
 from twisted.internet.defer import inlineCallbacks, returnValue,\
     maybeDeferred
+
+from twext.python.log import Logger
 from twext.web2 import responsecode
-from txdav.xml import element as davxml
 from twext.web2.dav.http import MultiStatusResponse
+from twext.web2.dav.http import ErrorResponse
 from twext.web2.dav.method.report import NumberOfMatchesWithinLimits
 from twext.web2.dav.util import joinURL
 from twext.web2.http import HTTPError, StatusResponse
 
+from twistedcaldav import caldavxml
 from twistedcaldav.caldavxml import caldav_namespace, MaxInstances
 from twistedcaldav.config import config
 from txdav.common.icommondatastore import IndexedSearchException,\
@@ -40,6 +40,9 @@
 from twistedcaldav.method import report_common
 from twistedcaldav.query import calendarqueryfilter
 
+from txdav.caldav.icalendarstore import TimeRangeLowerLimit, TimeRangeUpperLimit
+from txdav.xml import element as davxml
+
 log = Logger()
 
 @inlineCallbacks
@@ -260,6 +263,18 @@
             davxml.NumberOfMatchesWithinLimits(),
             "Too many components",
         ))
+    except TimeRangeLowerLimit, e:
+        raise HTTPError(ErrorResponse(
+            responsecode.FORBIDDEN,
+            caldavxml.MinDateTime(),
+            "Time-range value too far in the past. Must be on or after %s." % (str(e.limit),)
+        ))
+    except TimeRangeUpperLimit, e:
+        raise HTTPError(ErrorResponse(
+            responsecode.FORBIDDEN,
+            caldavxml.MaxDateTime(),
+            "Time-range value too far in the future. Must be on or before %s." % (str(e.limit),)
+        ))
     
     if not hasattr(request, "extendedLogItems"):
         request.extendedLogItems = {}

Modified: CalendarServer/trunk/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_common.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/method/report_common.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -665,7 +665,7 @@
     """
     
     # Expand out the set of instances for the event with in the required range
-    instances = calendar.expandTimeRanges(timerange.end, ignoreInvalidInstances=True)
+    instances = calendar.expandTimeRanges(timerange.end, lowerLimit=timerange.start, ignoreInvalidInstances=True)
     
     # Can only do timed events
     for key in instances:

Modified: CalendarServer/trunk/twistedcaldav/method/report_freebusy.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_freebusy.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/method/report_freebusy.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,12 +20,11 @@
 
 __all__ = ["report_urn_ietf_params_xml_ns_caldav_free_busy_query"]
 
+from twisted.internet.defer import inlineCallbacks, returnValue
+
 from twext.python.log import Logger
+from twext.web2 import responsecode
 from twext.web2.dav.http import ErrorResponse
-
-from twisted.internet.defer import inlineCallbacks, returnValue
-from twext.web2 import responsecode
-from txdav.xml import element as davxml
 from twext.web2.dav.method.report import NumberOfMatchesWithinLimits
 from twext.web2.http import HTTPError, Response, StatusResponse
 from twext.web2.http_headers import MimeType
@@ -34,6 +33,9 @@
 from twistedcaldav import caldavxml
 from twistedcaldav.method import report_common
 
+from txdav.caldav.icalendarstore import TimeRangeLowerLimit, TimeRangeUpperLimit
+from txdav.xml import element as davxml
+
 log = Logger()
 
 @inlineCallbacks
@@ -85,6 +87,18 @@
             davxml.NumberOfMatchesWithinLimits(),
             "Too many components"
         ))
+    except TimeRangeLowerLimit, e:
+        raise HTTPError(ErrorResponse(
+            responsecode.FORBIDDEN,
+            caldavxml.MinDateTime(),
+            "Time-range value too far in the past. Must be on or after %s." % (str(e.limit),)
+        ))
+    except TimeRangeUpperLimit, e:
+        raise HTTPError(ErrorResponse(
+            responsecode.FORBIDDEN,
+            caldavxml.MaxDateTime(),
+            "Time-range value too far in the future. Must be on or before %s." % (str(e.limit),)
+        ))
     
     # Now build a new calendar object with the free busy info we have
     fbcalendar = report_common.buildFreeBusyResult(fbinfo, timerange)

Modified: CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/query/calendarqueryfilter.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2009-2011 Apple Inc. All rights reserved.
+# Copyright (c) 2009-2012 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.
@@ -33,6 +33,7 @@
 
 log = Logger()
 
+
 class FilterBase(object):
     """
     Determines which matching components are returned.
@@ -47,6 +48,7 @@
     def valid(self, level=0):
         raise NotImplementedError
 
+
 class Filter(FilterBase):
     """
     Determines which matching components are returned.
@@ -59,7 +61,7 @@
         # One comp-filter element must be present
         if len(xml_element.children) != 1 or xml_element.children[0].qname() != (caldav_namespace, "comp-filter"):
             raise ValueError("Invalid CALDAV:filter element: %s" % (xml_element,))
-        
+
         self.child = ComponentFilter(xml_element.children[0])
 
     def match(self, component, access=None):
@@ -67,7 +69,7 @@
         Returns True if the given calendar component matches this filter, False
         otherwise.
         """
-        
+
         # We only care about certain access restrictions.
         if access not in (Component.ACCESS_CONFIDENTIAL, Component.ACCESS_RESTRICTED):
             access = None
@@ -97,10 +99,10 @@
         """
         Indicate whether this filter element's structure is valid wrt iCalendar
         data object model.
-        
+
         @return: True if valid, False otherwise
         """
-        
+
         # Must have one child element for VCALENDAR
         return self.child.valid(0)
         
@@ -132,9 +134,18 @@
         """
         Get the date farthest into the future in any time-range elements
         """
-        
+
         return self.child.getmaxtimerange(None, False)
 
+    def getmintimerange(self):
+        """
+        Get the date farthest into the past in any time-range elements. That is either
+        the start date, or if start is not present, the end date.
+        """
+
+        return self.child.getmintimerange(None, False)
+
+
 class FilterChildBase(FilterBase):
     """
     CalDAV filter element.
@@ -149,7 +160,7 @@
 
         for child in xml_element.children:
             qname = child.qname()
-            
+
             if qname in (
                 (caldav_namespace, "is-not-defined"),
                 (caldav_namespace, "time-range"),
@@ -157,7 +168,7 @@
             ):
                 if qualifier is not None:
                     raise ValueError("Only one of CalDAV:time-range, CalDAV:text-match allowed")
-                
+
                 if qname == (caldav_namespace, "is-not-defined"):
                     qualifier = IsNotDefined(child)
                 elif qname == (caldav_namespace, "time-range"):
@@ -176,7 +187,7 @@
 
         if qualifier and isinstance(qualifier, IsNotDefined) and (len(filters) != 0):
             raise ValueError("No other tests allowed when CalDAV:is-not-defined is present")
-            
+
         self.qualifier = qualifier
         self.filters = filters
         self.filter_name = xml_element.attributes["name"]
@@ -194,7 +205,7 @@
         Returns True if the given calendar item (either a component, property or parameter value)
         matches this filter, False otherwise.
         """
-        
+
         # Always return True for the is-not-defined case as the result of this will
         # be negated by the caller
         if not self.defined: return True
@@ -210,6 +221,7 @@
         else:
             return True
 
+
 class ComponentFilter (FilterChildBase):
     """
     Limits a search to only the chosen component types.
@@ -245,7 +257,7 @@
             # In particular do not match VALARM.
             if access and subcomponent.name() not in ("VEVENT", "VTODO", "VJOURNAL", "VFREEBUSY", "VTIMEZONE",):
                 continue
-            
+
             # Try to match the component name
             if isinstance(self.filter_name, str):
                 if subcomponent.name() != self.filter_name: continue
@@ -255,7 +267,7 @@
         else:
             return not self.defined
         return self.defined
-        
+
     def setInstances(self, instances):
         """
         Give the list of instances to each comp-filter element.
@@ -264,7 +276,7 @@
         self.instances = instances
         for compfilter in [x for x in self.filters if isinstance(x, ComponentFilter)]:
             compfilter.setInstances(instances)
-        
+
     def valid(self, level):
         """
         Indicate whether this filter element's structure is valid wrt iCalendar
@@ -273,7 +285,7 @@
         @param level: the nesting level of this filter element, 0 being the top comp-filter.
         @return:      True if valid, False otherwise
         """
-        
+
         # Check for time-range
         timerange = self.qualifier and isinstance(self.qualifier, TimeRange)
 
@@ -287,7 +299,7 @@
             if self.filter_name in ("VCALENDAR", "VALARM", "STANDARD", "DAYLIGHT", "AVAILABLE"):
                 log.msg("comp-filter wrong component type: %s" % (self.filter_name,))
                 return False
-            
+
             # time-range only on VEVENT, VTODO, VJOURNAL, VFREEBUSY, VAVAILABILITY
             if timerange and self.filter_name not in ("VEVENT", "VTODO", "VJOURNAL", "VFREEBUSY", "VAVAILABILITY"):
                 log.msg("time-range cannot be used with component %s" % (self.filter_name,))
@@ -297,7 +309,7 @@
             if (self.filter_name in ("VCALENDAR", "VTIMEZONE", "VEVENT", "VTODO", "VJOURNAL", "VFREEBUSY", "VAVAILABILITY")):
                 log.msg("comp-filter wrong sub-component type: %s" % (self.filter_name,))
                 return False
-            
+
             # time-range only on VALARM, AVAILABLE
             if timerange and self.filter_name not in ("VALARM", "AVAILABLE",):
                 log.msg("time-range cannot be used with sub-component %s" % (self.filter_name,))
@@ -307,7 +319,7 @@
             if (self.filter_name in ("VCALENDAR", "VTIMEZONE", "VEVENT", "VTODO", "VJOURNAL", "VFREEBUSY", "VALARM", "STANDARD", "DAYLIGHT", "AVAILABLE")) or timerange:
                 log.msg("comp-filter wrong standard component type: %s" % (self.filter_name,))
                 return False
-        
+
         # Test each property
         for propfilter in [x for x in self.filters if isinstance(x, PropertyFilter)]:
             if not propfilter.valid():
@@ -330,11 +342,11 @@
         Set the default timezone to use with this query.
         @param tzinfo: a L{PyCalendarTimezone} to use.
         """
-        
+
         # Give tzinfo to any TimeRange we have
         if isinstance(self.qualifier, TimeRange):
             self.qualifier.settzinfo(tzinfo)
-        
+
         # Pass down to sub components/properties
         for x in self.filters:
             x.settzinfo(tzinfo)
@@ -346,7 +358,7 @@
         @param currentMaximum: current future value to compare with
         @type currentMaximum: L{PyCalendarDateTime}
         """
-        
+
         # Give tzinfo to any TimeRange we have
         isStartTime = False
         if isinstance(self.qualifier, TimeRange):
@@ -355,13 +367,35 @@
             if currentMaximum is None or currentMaximum < compareWith:
                 currentMaximum = compareWith
                 currentIsStartTime = isStartTime
-        
+
         # Pass down to sub components/properties
         for x in self.filters:
             currentMaximum, currentIsStartTime = x.getmaxtimerange(currentMaximum, currentIsStartTime)
 
         return currentMaximum, currentIsStartTime
 
+    def getmintimerange(self, currentMinimum, currentIsEndTime):
+        """
+        Get the date farthest into the past in any time-range elements. That is either
+        the start date, or if start is not present, the end date.
+        """
+
+        # Give tzinfo to any TimeRange we have
+        isEndTime = False
+        if isinstance(self.qualifier, TimeRange):
+            isEndTime = self.qualifier.start is None
+            compareWith = self.qualifier.end if isEndTime else self.qualifier.start
+            if currentMinimum is None or currentMinimum > compareWith:
+                currentMinimum = compareWith
+                currentIsEndTime = isEndTime
+
+        # Pass down to sub components/properties
+        for x in self.filters:
+            currentMinimum, currentIsEndTime = x.getmintimerange(currentMinimum, currentIsEndTime)
+
+        return currentMinimum, currentIsEndTime
+
+
 class PropertyFilter (FilterChildBase):
     """
     Limits a search to specific properties.
@@ -394,10 +428,10 @@
         
         @return:      True if valid, False otherwise
         """
-        
+
         # Check for time-range
         timerange = self.qualifier and isinstance(self.qualifier, TimeRange)
-        
+
         # time-range only on COMPLETED, CREATED, DTSTAMP, LAST-MODIFIED
         if timerange and self.filter_name.upper() not in ("COMPLETED", "CREATED", "DTSTAMP", "LAST-MODIFIED"):
             log.msg("time-range cannot be used with property %s" % (self.filter_name,))
@@ -416,7 +450,7 @@
         Set the default timezone to use with this query.
         @param tzinfo: a L{PyCalendarTimezone} to use.
         """
-        
+
         # Give tzinfo to any TimeRange we have
         if isinstance(self.qualifier, TimeRange):
             self.qualifier.settzinfo(tzinfo)
@@ -428,7 +462,7 @@
         @param currentMaximum: current future value to compare with
         @type currentMaximum: L{PyCalendarDateTime}
         """
-        
+
         # Give tzinfo to any TimeRange we have
         isStartTime = False
         if isinstance(self.qualifier, TimeRange):
@@ -440,6 +474,24 @@
 
         return currentMaximum, currentIsStartTime
 
+    def getmintimerange(self, currentMinimum, currentIsEndTime):
+        """
+        Get the date farthest into the past in any time-range elements. That is either
+        the start date, or if start is not present, the end date.
+        """
+
+        # Give tzinfo to any TimeRange we have
+        isEndTime = False
+        if isinstance(self.qualifier, TimeRange):
+            isEndTime = self.qualifier.start is None
+            compareWith = self.qualifier.end if isEndTime else self.qualifier.start
+            if currentMinimum is None or currentMinimum > compareWith:
+                currentMinimum = compareWith
+                currentIsEndTime = isEndTime
+
+        return currentMinimum, currentIsEndTime
+
+
 class ParameterFilter (FilterChildBase):
     """
     Limits a search to specific parameters.
@@ -456,6 +508,7 @@
 
         return result
 
+
 class IsNotDefined (FilterBase):
     """
     Specifies that the named iCalendar item does not exist.
@@ -468,6 +521,7 @@
         # is-not-defined option.
         return True
 
+
 class TextMatch (FilterBase):
     """
     Specifies a substring match on a property or parameter value.
@@ -532,7 +586,7 @@
             if self.match_type == "equals":
                 return s == test
             elif self.match_type == "contains":
-                return s.find(test) != -1 
+                return s.find(test) != -1
             elif self.match_type == "starts-with":
                 return s.startswith(test)
             elif self.match_type == "ends-with":
@@ -550,9 +604,10 @@
             else:
                 if _textCompare(unicode(value, "utf-8")):
                     return not self.negate
-        
+
         return self.negate
 
+
 class TimeRange (FilterBase):
     """
     Specifies a time for testing components against.
@@ -565,7 +620,7 @@
         # One of start or end must be present
         if "start" not in xml_element.attributes and "end" not in xml_element.attributes:
             raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range")
-        
+
         self.start = PyCalendarDateTime.parseText(xml_element.attributes["start"]) if "start" in xml_element.attributes else None
         self.end = PyCalendarDateTime.parseText(xml_element.attributes["end"]) if "end" in xml_element.attributes else None
         self.tzinfo = None
@@ -575,7 +630,7 @@
         Set the default timezone to use with this query.
         @param tzinfo: a L{PyCalendarTimezone} to use.
         """
-        
+
         # Give tzinfo to any TimeRange we have
         self.tzinfo = tzinfo
 
@@ -585,7 +640,7 @@
         
         @return:      True if valid, False otherwise
         """
-        
+
         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
@@ -621,9 +676,9 @@
         """
         if component is None:
             return False
-        
+
         assert instances is not None or self.end is None, "Failure to expand instance for time-range filter: %r" % (self,)
-        
+
         # Special case open-ended unbounded
         if instances is None:
             if component.getRecurrenceIDUTC() is None:
@@ -642,10 +697,10 @@
             testcomponent = component._parent
         else:
             testcomponent = component
-            
+
         for key in instances:
             instance = instances[key]
-            
+
             # First make sure components match
             if not testcomponent.same(instance.component):
                 continue

Modified: CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -16,7 +16,6 @@
 
 from twext.python.log import Logger
 
-from twistedcaldav.config import config
 from twistedcaldav.ical import Component, Property
 from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
 from twistedcaldav.scheduling.itip import iTipGenerator
@@ -248,12 +247,6 @@
         
         self.attendee = normalizeCUAddr(attendee)
 
-        if config.MaxInstancesForRRULE != 0:
-            try:
-                self.oldcalendar.truncateRecurrence(config.MaxInstancesForRRULE)
-            except (ValueError, TypeError), ex:
-                log.err("Cannot truncate calendar resource: %s" % (ex,))
-
         returnCalendar = self.oldcalendar.duplicate()
         returnMaster = returnCalendar.masterComponent()
 

Modified: CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -908,20 +908,12 @@
         For data coming in from outside we need to normalize the calendar user addresses so that later iTIP
         processing will match calendar users against those in stored calendar data. Only do that for invites
         not freebusy.
-        
-        We also need to apply recurrence truncation here so the incoming iTIP's RRULE matches the truncated
-        version in any attendee calendar. This avoids a problem where the iTIP message appears to represent
-        a significant change as reported in schedule-changes property.
         """
 
         if not self.checkForFreeBusy():
             self.calendar.normalizeCalendarUserAddresses(normalizationLookup,
                 self.resource.principalForCalendarUserAddress)
 
-        # Apply recurrence truncation at this point
-        if config.MaxInstancesForRRULE != 0:
-            self.calendar.truncateRecurrence(config.MaxInstancesForRRULE)        
-
     def checkAuthorization(self):
         # Must have an unauthenticated user
         if self.resource.currentPrincipal(self.request) != davxml.Principal(davxml.Unauthenticated()):

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -328,7 +328,6 @@
     "MaxResourceSize"           : 1048576, # Maximum resource size (in bytes)
     "MaxAttendeesPerInstance"   :     100, # Maximum number of unique attendees
     "MaxAllowedInstances"       :    3000, # Maximum number of instances the server will index
-    "MaxInstancesForRRULE"      :     400, # Maximum number of instances for an RRULE
 
     # Set to URL path of wiki authentication service, e.g. "/auth", in order
     # to use javascript authentication dialog.  Empty string indicates standard
@@ -922,6 +921,7 @@
     "FreeBusyCacheDaysBack":        7,
     "FreeBusyCacheDaysForward":     12 * 7,
 
+    "FreeBusyIndexLowerLimitDays":  365,
     "FreeBusyIndexExpandAheadDays": 365,
     "FreeBusyIndexExpandMaxDays":   5 * 365,
     "FreeBusyIndexDelayedExpand":   True,

Modified: CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/test/test_calendarquery.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -36,7 +36,9 @@
 from twisted.internet.defer import inlineCallbacks, returnValue
 from txdav.common.datastore.test.util import buildStore, StubNotifierFactory
 
+from pycalendar.datetime import PyCalendarDateTime
 
+
 @inlineCallbacks
 def addEventsDir(testCase, eventsDir, uri):
     """
@@ -109,8 +111,8 @@
         )
 
         query_timerange = caldavxml.TimeRange(
-            start="20021001T000000Z",
-            end="20021101T000000Z",
+            start="%04d1001T000000Z" % (PyCalendarDateTime.getToday().getYear(),),
+            end="%04d1101T000000Z" % (PyCalendarDateTime.getToday().getYear(),),
         )
 
         query = caldavxml.CalendarQuery(

Modified: CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_icalendar.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/twistedcaldav/test/test_icalendar.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -2434,9 +2434,9 @@
         for description, original, ignoreInvalidInstances, results in data:
             component = Component.fromString(original)
             if results is None:
-                self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances)
+                self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
             else:
-                instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances)
+                instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
                 self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
                 periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
                 self.assertEqual(periods, results)
@@ -2776,9 +2776,9 @@
         for description, original, ignoreInvalidInstances, results in data:
             component = Component.fromString(original)
             if results is None:
-                self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances)
+                self.assertRaises(InvalidOverriddenInstanceError, component.expandTimeRanges, PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances)
             else:
-                instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances, normalizeFunction=normalizeForExpand)
+                instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), ignoreInvalidInstances=ignoreInvalidInstances, normalizeFunction=normalizeForExpand)
                 self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
                 periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
                 self.assertEqual(periods, results)
@@ -2786,6 +2786,361 @@
                     self.assertEqual(start.isDateOnly(), results[0][0].isDateOnly(), "%s: %s wrong date/time start state" % (description, start,))
                     self.assertEqual(end.isDateOnly(), results[0][1].isDateOnly(), "%s: %s wrong date/time end state" % (description, end,))
        
+    def test_expand_instances_lowerlimit(self):
+        
+        data = (
+            (
+                "Non recurring - no limit",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+                None,
+                (
+                    (
+                        PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                None,
+            ),
+            (
+                "Non recurring - limit not effective",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2007, 1, 1),
+                (
+                    (
+                        PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                None,
+            ),
+            (
+                "Non recurring - limit effective",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2010, 1, 1),
+                (),
+                PyCalendarDateTime(2010, 1, 1),
+            ),
+            (
+                "Simple recurring - no limit",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+END:VCALENDAR
+""",
+                None,
+                (
+                    (
+                        PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2008, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                None,
+            ),
+            (
+                "Simple recurring - limit not effective",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2007, 1, 1),
+                (
+                    (
+                        PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2008, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2008, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                None,
+            ),
+            (
+                "Simple recurring - limit effective partial",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2010, 1, 1),
+                (
+                    (
+                        PyCalendarDateTime(2010, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2010, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                PyCalendarDateTime(2010, 1, 1),
+            ),
+            (
+                "Simple recurring - limit effective full",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2012, 1, 1),
+                (),
+                PyCalendarDateTime(2012, 1, 1),
+            ),
+            (
+                "Complex recurring - no limit",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20081115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20101114T000000Z
+DTSTART:20101115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+                None,
+                (
+                    (
+                        PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2008, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2008, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                None,
+            ),
+            (
+                "Complex recurring - limit not effective",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20081115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20101114T000000Z
+DTSTART:20101115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2007, 1, 1),
+                (
+                    (
+                        PyCalendarDateTime(2007, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2007, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2008, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2008, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2009, 11, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2009, 11, 14, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                    (
+                        PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                None,
+            ),
+            (
+                "Complex recurring - limit effective partial",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20081115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20101114T000000Z
+DTSTART:20101115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2010, 1, 1),
+                (
+                    (
+                        PyCalendarDateTime(2010, 11, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                        PyCalendarDateTime(2010, 11, 15, 1, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                    ),
+                ),
+                PyCalendarDateTime(2010, 1, 1),
+            ),
+            (
+                "Complex recurring - limit effective full",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+RRULE:FREQ=YEARLY;COUNT=4
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20081114T000000Z
+DTSTART:20081115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20101114T000000Z
+DTSTART:20101115T000000Z
+DTSTAMP:20080601T120000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""",
+                PyCalendarDateTime(2012, 1, 1),
+                (),
+                PyCalendarDateTime(2012, 1, 1),
+            ),
+        )
+        
+        for description, original, lowerLimit, results, limited in data:
+            component = Component.fromString(original)
+            instances = component.expandTimeRanges(PyCalendarDateTime(2100, 1, 1), lowerLimit=lowerLimit)
+            self.assertTrue(len(instances.instances) == len(results), "%s: wrong number of instances" % (description,))
+            periods = tuple([(instance.start, instance.end) for instance in sorted(instances.instances.values(), key=lambda x:x.start)])
+            self.assertEqual(periods, results)
+            for start, end in periods:
+                self.assertEqual(start.isDateOnly(), results[0][0].isDateOnly(), "%s: %s wrong date/time start state" % (description, start,))
+                self.assertEqual(end.isDateOnly(), results[0][1].isDateOnly(), "%s: %s wrong date/time end state" % (description, end,))
+            self.assertEqual(instances.lowerLimit, limited)
+       
     def test_has_property_in_any_component(self):
         
         data = (

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -858,6 +858,7 @@
         instanceIndexingRequired = not hasattr(component, "noInstanceIndexing") or inserting or reCreate
         
         if instanceIndexingRequired:
+
             # Decide how far to expand based on the component. doInstanceIndexing will indicate whether we
             # store expanded instance data immediately, or wait until a re-expand is triggered by some later
             # operation.
@@ -901,25 +902,34 @@
                              PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)):
                     raise IndexedSearchException
     
+            if config.FreeBusyIndexLowerLimitDays:
+                truncateLowerLimit = PyCalendarDateTime.getToday()
+                truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
+            else:
+                truncateLowerLimit = None
+
             # Always do recurrence expansion even if we do not intend to index - we need this to double-check the
             # validity of the iCalendar recurrence data.
             try:
-                instances = component.expandTimeRanges(expand, ignoreInvalidInstances=reCreate)
+                instances = component.expandTimeRanges(expand, lowerLimit=truncateLowerLimit, ignoreInvalidInstances=reCreate)
                 recurrenceLimit = instances.limit
+                recurrenceLowerLimit = instances.lowerLimit
             except InvalidOverriddenInstanceError, e:
                 self.log_error("Invalid instance %s when indexing %s in %s" %
                                (e.rid, self._name, self._calendar,))
     
                 if txn._migrating:
                     # TODO: fix the data here by re-writing component then re-index
-                    instances = component.expandTimeRanges(expand, ignoreInvalidInstances=True)
+                    instances = component.expandTimeRanges(expand, lowerLimit=truncateLowerLimit, ignoreInvalidInstances=True)
                     recurrenceLimit = instances.limit
+                    recurrenceLowerLimit = instances.lowerLimit
                 else:
                     raise
     
             # Now coerce indexing to off if needed
             if not doInstanceIndexing:
                 instances = None
+                recurrenceLowerLimit = None
                 recurrenceLimit = PyCalendarDateTime(1900, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
 
         co = schema.CALENDAR_OBJECT
@@ -977,6 +987,7 @@
 
             # Only needed if indexing being changed
             if instanceIndexingRequired:
+                values[co.RECURRANCE_MIN] = pyCalendarTodatetime(normalizeForIndex(recurrenceLowerLimit)) if recurrenceLowerLimit else None
                 values[co.RECURRANCE_MAX] = pyCalendarTodatetime(normalizeForIndex(recurrenceLimit)) if recurrenceLimit else None
 
             if inserting:
@@ -1003,8 +1014,8 @@
                     ).on(txn)
         else:
             values = {
-                co.RECURRANCE_MAX :
-                    pyCalendarTodatetime(normalizeForIndex(recurrenceLimit)) if recurrenceLimit else None,
+                co.RECURRANCE_MIN : pyCalendarTodatetime(normalizeForIndex(recurrenceLowerLimit)) if recurrenceLowerLimit else None,
+                co.RECURRANCE_MAX : pyCalendarTodatetime(normalizeForIndex(recurrenceLimit)) if recurrenceLimit else None,
             }
 
             yield Update(
@@ -1019,11 +1030,11 @@
             ).on(txn)
 
         if instanceIndexingRequired and doInstanceIndexing:
-            yield self._addInstances(component, instances, txn)
+            yield self._addInstances(component, instances, truncateLowerLimit, txn)
     
     
     @inlineCallbacks
-    def _addInstances(self, component, instances, txn):
+    def _addInstances(self, component, instances, truncateLowerLimit, txn):
         """
         Add the set of supplied instances to the store.
 
@@ -1031,69 +1042,73 @@
         @type component: L{Component}
         @param instances: the set of instances to add
         @type instances: L{InstanceList}
+        @param truncateLowerLimit: the lower limit for instances
+        @type truncateLowerLimit: L{PyCalendarDateTime}
         @param txn: transaction to use
         @type txn: L{Transaction}
         """
 
-        tr = schema.TIME_RANGE
-        tpy = schema.TRANSPARENCY
-
         # TIME_RANGE table update
+        lowerLimitApplied = False
         for key in instances:
             instance = instances[key]
             start = instance.start
             end = instance.end
             float = instance.start.floating()
+            transp = instance.component.propertyValue("TRANSP") == "TRANSPARENT"
+            fbtype = instance.component.getFBType()
             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                  : pyCalendarTodatetime(start),
-                tr.END_DATE                    : pyCalendarTodatetime(end),
-                tr.FBTYPE                      :
-                    icalfbtype_to_indexfbtype.get(
-                        instance.component.getFBType(),
-                        icalfbtype_to_indexfbtype["FREE"]),
-                tr.TRANSPARENT                 : transp,
-            }, Return=tr.INSTANCE_ID).on(txn))[0][0]
-            peruserdata = component.perUserTransparency(instance.rid)
-            for useruid, transp in peruserdata:
-                (yield Insert({
-                    tpy.TIME_RANGE_INSTANCE_ID : instanceid,
-                    tpy.USER_ID                : useruid,
-                    tpy.TRANSPARENT            : transp,
-                }).on(txn))
 
+            # Ignore if below the lower limit            
+            if truncateLowerLimit and end < truncateLowerLimit:
+                lowerLimitApplied = True
+                continue
+
+            yield self._addInstanceDetails(component, instance.rid, start, end, float, transp, fbtype, txn)
+
+        # For truncated items we insert a tomb stone lower bound so that a time-range
+        # query with just an end bound will match
+        if lowerLimitApplied or instances.lowerLimit and len(instances.instances) == 0:
+            start = PyCalendarDateTime(1901, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+            end = PyCalendarDateTime(1901, 1, 1, 1, 0, 0, tzid=PyCalendarTimezone(utc=True))
+            yield self._addInstanceDetails(component, None, start, end, False, True, "UNKNOWN", txn)
+
         # 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():
+        # We also need to add the "infinity" value if the event was bounded but
+        # starts after the future expansion cut-off limit. 
+        if component.isRecurringUnbounded() or instances.limit and len(instances.instances) == 0:
             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                  : pyCalendarTodatetime(start),
-                tr.END_DATE                    : pyCalendarTodatetime(end),
-                tr.FBTYPE                      :
-                    icalfbtype_to_indexfbtype["UNKNOWN"],
-                tr.TRANSPARENT                 : transp,
-            }, Return=tr.INSTANCE_ID).on(txn))[0][0]
-            peruserdata = component.perUserTransparency(None)
-            for useruid, transp in peruserdata:
-                (yield Insert({
-                    tpy.TIME_RANGE_INSTANCE_ID : instanceid,
-                    tpy.USER_ID                : useruid,
-                    tpy.TRANSPARENT            : transp,
-                }).on(txn))
+            yield self._addInstanceDetails(component, None, start, end, False, True, "UNKNOWN", txn)
 
 
     @inlineCallbacks
+    def _addInstanceDetails(self, component, rid, start, end, float, transp, fbtype, txn):
+
+        tr = schema.TIME_RANGE
+        tpy = schema.TRANSPARENCY
+
+        instanceid = (yield Insert({
+            tr.CALENDAR_RESOURCE_ID        : self._calendar._resourceID,
+            tr.CALENDAR_OBJECT_RESOURCE_ID : self._resourceID,
+            tr.FLOATING                    : float,
+            tr.START_DATE                  : pyCalendarTodatetime(start),
+            tr.END_DATE                    : pyCalendarTodatetime(end),
+            tr.FBTYPE                      : icalfbtype_to_indexfbtype.get(fbtype, icalfbtype_to_indexfbtype["FREE"]),
+            tr.TRANSPARENT                 : transp,
+        }, Return=tr.INSTANCE_ID).on(txn))[0][0]
+        peruserdata = component.perUserTransparency(rid)
+        for useruid, transp in peruserdata:
+            (yield Insert({
+                tpy.TIME_RANGE_INSTANCE_ID : instanceid,
+                tpy.USER_ID                : useruid,
+                tpy.TRANSPARENT            : transp,
+            }).on(txn))
+
+
+    @inlineCallbacks
     def component(self):
         """
         Read calendar data and validate/fix it. Do not raise a store error here
@@ -1128,19 +1143,22 @@
 
 
     @classproperty
-    def _recurrenceMaxByIDQuery(cls): #@NoSelf
+    def _recurrenceMinMaxByIDQuery(cls): #@NoSelf
         """
-        DAL query to load RECURRANCE_MAX via an object's resource ID.
+        DAL query to load RECURRANCE_MIN, RECURRANCE_MAX via an object's resource ID.
         """
         co = schema.CALENDAR_OBJECT
-        return Select([co.RECURRANCE_MAX], From=co,
-                      Where=co.RESOURCE_ID == Parameter("resourceID"))
+        return Select(
+            [co.RECURRANCE_MIN, co.RECURRANCE_MAX,],
+            From=co,
+            Where=co.RESOURCE_ID == Parameter("resourceID"),
+        )
 
 
     @inlineCallbacks
-    def recurrenceMax(self, txn=None):
+    def recurrenceMinMax(self, txn=None):
         """
-        Get the RECURRANCE_MAX value from the database. Occasionally we might need to do an
+        Get the RECURRANCE_MIN, RECURRANCE_MAX value from the database. Occasionally we might need to do an
         update to time-range data via a separate transaction, so we allow that to be passed in.
     
         @return: L{PyCalendarDateTime} result
@@ -1148,11 +1166,14 @@
         # Setup appropriate txn
         txn = txn if txn is not None else self._txn
 
-        rMax = (
-            yield self._recurrenceMaxByIDQuery.on(txn,
+        rMin, rMax = (
+            yield self._recurrenceMinMaxByIDQuery.on(txn,
                                          resourceID=self._resourceID)
-        )[0][0]
-        returnValue(parseSQLDateToPyCalendar(rMax) if rMax is not None else None)
+        )[0]
+        returnValue((
+            parseSQLDateToPyCalendar(rMin) if rMin is not None else None,
+            parseSQLDateToPyCalendar(rMax) if rMax is not None else None,
+        ))
 
 
     @classproperty

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -24,7 +24,7 @@
  o:wsanchez at example.com
 ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:cda
  boo at example.com
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 TRANSP:OPAQUE
 ORGANIZER;CN="Wilfredo Sanchez":mailto:wsanchez at example.com
 UID:uid1
@@ -35,11 +35,11 @@
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/FE5CDC6F-7776-4607-83
  A9-B90FF7ACC8D0.dropbox
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 BEGIN:VALARM
 X-WR-ALARMUID:DB39AB67-449C-441C-89D2-D740B5F41A73
-TRIGGER;VALUE=DATE-TIME:20090324T180009Z
+TRIGGER;VALUE=DATE-TIME:%(now)s0324T180009Z
 ACTION:AUDIO
 END:VALARM
 END:VEVENT

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/2.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/2.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/2.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -22,22 +22,22 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid2
-DTSTART;TZID=US/Eastern:20060102T140000
+DTSTART;TZID=US/Eastern:%(now)s0102T140000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DTSTAMP:20051222T210507Z
 RRULE:FREQ=DAILY;COUNT=5
-SUMMARY:event 6-%ctr
+SUMMARY:event 6-ctr
 END:VEVENT
 BEGIN:VEVENT
 UID:uid2
-RECURRENCE-ID;TZID=US/Eastern:20060104T140000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T140000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 BEGIN:VALARM
 ACTION:AUDIO
 TRIGGER;RELATED=START:-PT10M

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/3.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/3.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/3.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -22,12 +22,12 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid3
-DTSTART;TZID=US/Pacific:20060101T130000
+DTSTART;TZID=US/Pacific:%(now)s0101T130000
 DURATION:PT1H
 CREATED:20060101T210000Z
 DTSTAMP:20051222T210146Z
 LAST-MODIFIED:20051222T210203Z
 SEQUENCE:1
-SUMMARY:event 3-%ctr
+SUMMARY:event 3-ctr
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/4.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/4.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/4.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -4,7 +4,7 @@
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
 BEGIN:VEVENT
 UID:uid4
-DTSTART;VALUE=DATE:20060201
+DTSTART;VALUE=DATE:%(now)s0201
 DURATION:P1D
 CREATED:20060101T210000Z
 DTSTAMP:20051222T210146Z

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/24204e8682b99527cbda64d7423acda7.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/24204e8682b99527cbda64d7423acda7.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/24204e8682b99527cbda64d7423acda7.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -21,12 +21,12 @@
 END:DAYLIGHT
 END:VTIMEZONE
 BEGIN:VEVENT
-UID:9A6519F71822CD45840C3440-%ctr at ninevah.local
-DTSTART;TZID=US/Mountain:20060101T110000
+UID:9A6519F71822CD45840C3440-ctr at ninevah.local
+DTSTART;TZID=US/Mountain:%(now)s0101T110000
 DURATION:PT1H
 CREATED:20060101T160000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210052Z
-SUMMARY:event 2-%ctr
+SUMMARY:event 2-ctr
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/61038c41bd02ae5daf9f7fe9d54199fd.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/61038c41bd02ae5daf9f7fe9d54199fd.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/61038c41bd02ae5daf9f7fe9d54199fd.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -21,11 +21,11 @@
 END:DAYLIGHT
 END:VTIMEZONE
 BEGIN:VEVENT
-UID:54E181BC7CCC373042B28842-%ctr at ninevah.local
-DTSTART;TZID=US/Eastern:20060101T100000
+UID:54E181BC7CCC373042B28842-ctr at ninevah.local
+DTSTART;TZID=US/Eastern:%(now)s0101T100000
 DURATION:PT1H
 CREATED:20060101T150000Z
 DTSTAMP:20051222T205953Z
-SUMMARY:event 1-%ctr
+SUMMARY:event 1-ctr
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/84be58ced1f1bb34057e1bd7e602c9c8.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/84be58ced1f1bb34057e1bd7e602c9c8.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/84be58ced1f1bb34057e1bd7e602c9c8.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -21,11 +21,11 @@
 END:DAYLIGHT
 END:VTIMEZONE
 BEGIN:VEVENT
-UID:54E181BC7CCC373042B28842-8-%ctr at ninevah.local
-DTSTART;TZID=US/Eastern:20060107T100000
+UID:54E181BC7CCC373042B28842-8-ctr at ninevah.local
+DTSTART;TZID=US/Eastern:%(now)s0107T100000
 DURATION:PT1H
 CREATED:20060101T150000Z
 DTSTAMP:20051222T205953Z
-SUMMARY:event 8-%ctr
+SUMMARY:event 8-ctr
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/acc1015b7dc300c1b5665f6833960994.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/acc1015b7dc300c1b5665f6833960994.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/acc1015b7dc300c1b5665f6833960994.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -21,11 +21,11 @@
 END:DAYLIGHT
 END:VTIMEZONE
 BEGIN:VEVENT
-UID:54E181BC7CCC373042B28842-9-%ctr at ninevah.local
-DTSTART;TZID=US/Eastern:20060107T103000
+UID:54E181BC7CCC373042B28842-9-ctr at ninevah.local
+DTSTART;TZID=US/Eastern:%(now)s0107T103000
 DURATION:PT1H
 CREATED:20060101T150000Z
 DTSTAMP:20051222T205953Z
-SUMMARY:event 9-%ctr
+SUMMARY:event 9-ctr
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b0d5785f275c064117ffd1fc20f4ed40.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b0d5785f275c064117ffd1fc20f4ed40.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b0d5785f275c064117ffd1fc20f4ed40.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -21,12 +21,12 @@
 END:DAYLIGHT
 END:VTIMEZONE
 BEGIN:VEVENT
-UID:A3217B429B4D2FF2DC2EEE66-%ctr at ninevah.local
-DTSTART;TZID=US/Eastern:20060101T180000
+UID:A3217B429B4D2FF2DC2EEE66-ctr at ninevah.local
+DTSTART;TZID=US/Eastern:%(now)s0101T180000
 DURATION:PT1H
 CREATED:20060101T230000Z
 DTSTAMP:20051222T210310Z
-SUMMARY:event 4-%ctr
+SUMMARY:event 4-ctr
 BEGIN:VALARM
 ACTION:AUDIO
 DURATION:PT10M

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b495c5dd5aa53392078eb43b1f906a80.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b495c5dd5aa53392078eb43b1f906a80.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b495c5dd5aa53392078eb43b1f906a80.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -21,13 +21,13 @@
 END:DAYLIGHT
 END:VTIMEZONE
 BEGIN:VEVENT
-UID:945113826375CBB89184DC36-%ctr at ninevah.local
-DTSTART;TZID=US/Eastern:20060102T100000
+UID:945113826375CBB89184DC36-ctr at ninevah.local
+DTSTART;TZID=US/Eastern:%(now)s0102T100000
 DURATION:PT1H
 CREATED:20060102T150000Z
 DTSTAMP:20051222T210412Z
 RRULE:FREQ=DAILY;COUNT=5
-SUMMARY:event 5-%ctr
+SUMMARY:event 5-ctr
 BEGIN:VALARM
 ACTION:AUDIO
 TRIGGER;RELATED=START:-PT10M

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b88dd50941e4a31520ee396fd7894c96.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b88dd50941e4a31520ee396fd7894c96.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_2/b88dd50941e4a31520ee396fd7894c96.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -21,11 +21,11 @@
 END:DAYLIGHT
 END:VTIMEZONE
 BEGIN:VEVENT
-UID:54E181BC7CCC373042B28842-10-%ctr at ninevah.local
-DTSTART;TZID=US/Eastern:20060108T100000
+UID:54E181BC7CCC373042B28842-10-ctr at ninevah.local
+DTSTART;TZID=US/Eastern:%(now)s0108T100000
 DURATION:PT1H
 CREATED:20060101T150000Z
 DTSTAMP:20051222T205953Z
-SUMMARY:event 10-%ctr
+SUMMARY:event 10-ctr
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/1.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/1.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid1
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid1.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid1.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/2.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/2.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/2.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid2
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid2.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid2.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/3.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/3.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_attachments/calendar_1/3.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid3
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid2.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid2.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/1.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/1.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -26,6 +26,6 @@
 SUMMARY:Busted
 DTSTART;TZID=US/Pacific:20000324T121500
 CREATED:20090326T145440Z
-RRULE:FREQ=DAILY
+RRULE:FREQ=HOURLY
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/2.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/2.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_bad/2.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -22,22 +22,22 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid2
-DTSTART;TZID=US/Eastern:20060102T140000
+DTSTART;TZID=US/Eastern:%(now)s0102T140000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DTSTAMP:20051222T210507Z
 RRULE:FREQ=DAILY;COUNT=5
-SUMMARY:event 6-%ctr
+SUMMARY:event 6-ctr
 END:VEVENT
 BEGIN:VEVENT
 UID:uid2
-RECURRENCE-ID;TZID=US/Eastern:20060104T140000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T140000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 BEGIN:VALARM
 ACTION:AUDIO
 TRIGGER;RELATED=START:-PT10M

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/1.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/1.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -22,22 +22,22 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid2
-DTSTART;TZID=US/Eastern:20060102T140000
+DTSTART;TZID=US/Eastern:%(now)s0102T140000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DTSTAMP:20051222T210507Z
 RRULE:FREQ=DAILY;COUNT=5
-SUMMARY:event 6-%ctr
+SUMMARY:event 6-ctr
 END:VEVENT
 BEGIN:VEVENT
 UID:uid2
-RECURRENCE-ID;TZID=US/Eastern:20060104T160000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T160000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 BEGIN:VALARM
 ACTION:AUDIO
 TRIGGER;RELATED=START:-PT10M

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/2.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/2.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/2.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -22,24 +22,24 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid3
-DTSTART;TZID=US/Eastern:20060102T140000
+DTSTART;TZID=US/Eastern:%(now)s0102T140000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DTSTAMP:20051222T210507Z
 RRULE:FREQ=DAILY;COUNT=5
-SUMMARY:event 6-%ctr
+SUMMARY:event 6-ctr
 ORGANIZER:urn:uuid:home_bad
 ATTENDEE:urn:uuid:home_bad
 END:VEVENT
 BEGIN:VEVENT
 UID:uid3
-RECURRENCE-ID;TZID=US/Eastern:20060104T140000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T140000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 BEGIN:VALARM
 ACTION:AUDIO
 TRIGGER;RELATED=START:-PT10M

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_bad/calendar_fix_recurrence/3.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -22,21 +22,21 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid4
-DTSTART;TZID=US/Eastern:20060104T160000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 END:VEVENT
 BEGIN:VEVENT
 UID:uid4
-RECURRENCE-ID;TZID=US/Eastern:20060104T160000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T160000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/1.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/1.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid1
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid1.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid1.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/2.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/2.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/2.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid2
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid2.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid2.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/3.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/3.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_no_splits/calendar_1/3.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid3
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid2.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid2.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/1.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/1.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid1
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid1.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid1.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/2.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/2.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/2.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid2
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid2.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid2.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/3.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/3.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_1/3.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid3
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid2.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid2.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/1.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/1.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid2-1
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid1.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid1.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/2.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/2.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/2.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,13 +20,13 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20090324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid2-2
 DTSTAMP:20090326T145447Z
 X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/uid2.dropbox
 ATTACH;VALUE=URI:/calendars/__uids__/user01/dropbox/uid2.dropbox/test.txt
 SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:20090324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/3.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/3.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/3.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -23,7 +23,7 @@
 UID:uid2-3
 DTSTAMP:20090326T145447Z
 SUMMARY:Do something...
-DUE;TZID=US/Pacific:20110324T121500
+DUE;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VTODO
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/4.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/4.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/4.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -20,11 +20,11 @@
 END:STANDARD
 END:VTIMEZONE
 BEGIN:VEVENT
-DTEND;TZID=US/Pacific:20100324T124500
+DTEND;TZID=US/Pacific:%(now)s0324T124500
 UID:uid4-2
 DTSTAMP:20090326T145447Z
 SUMMARY:CalDAV protocol updates 2010
-DTSTART;TZID=US/Pacific:20100324T121500
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VEVENT
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/5.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/5.ics	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home_splits/calendar_2/5.ics	2012-06-25 16:27:34 UTC (rev 9383)
@@ -23,7 +23,7 @@
 UID:uid2-5
 DTSTAMP:20090326T145447Z
 SUMMARY:Do something...2012
-DUE;TZID=US/Pacific:20120324T121500
+DUE;TZID=US/Pacific:%(now)s0324T121500
 CREATED:20090326T145440Z
 END:VTODO
 END:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -42,6 +42,8 @@
 from txdav.caldav.datastore.test.common import (
     CommonTests, test_event_text, event1modified_text)
 
+from pycalendar.datetime import PyCalendarDateTime
+
 storePath = FilePath(__file__).parent().child("calendar_store")
 
 def _todo(f, why):
@@ -65,6 +67,16 @@
     calendarPath.parent().makedirs()
     storePath.copyTo(calendarPath)
 
+    # Set year values to current year    
+    nowYear = PyCalendarDateTime.getToday().getYear()
+    for home in calendarPath.child("ho").child("me").children():
+        if not home.basename().startswith("."):
+            for calendar in home.children():
+                if not calendar.basename().startswith("."):
+                    for resource in calendar.children():
+                        if resource.basename().endswith(".ics"):
+                            resource.setContent(resource.getContent() % {"now":nowYear})
+
     testID = test.id()
     test.calendarStore = CalendarStore(storeRootPath, test.notifierFactory,
                                        quota=deriveQuota(test))

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -49,6 +49,7 @@
 from twistedcaldav.sharing import SharedCollectionRecord
 
 import datetime
+from pycalendar.datetime import PyCalendarDateTime
 
 class CalendarSQLStorageTests(CalendarCommonTests, unittest.TestCase):
     """
@@ -61,6 +62,7 @@
         self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
         yield self.populate()
 
+        self.nowYear = {"now":PyCalendarDateTime.getToday().getYear()}
 
     @inlineCallbacks
     def populate(self):
@@ -226,30 +228,30 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid2
-DTSTART;TZID=US/Eastern:20060102T140000
+DTSTART;TZID=US/Eastern:%(now)s0102T140000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DTSTAMP:20051222T210507Z
-RDATE;TZID=US/Eastern:20060104T160000
+RDATE;TZID=US/Eastern:%(now)s0104T160000
 RRULE:FREQ=DAILY;COUNT=5
-SUMMARY:event 6-%ctr
+SUMMARY:event 6-ctr
 END:VEVENT
 BEGIN:VEVENT
 UID:uid2
-RECURRENCE-ID;TZID=US/Eastern:20060104T160000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T160000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 BEGIN:VALARM
 ACTION:AUDIO
 TRIGGER;RELATED=START:-PT10M
 END:VALARM
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n"))
+""".replace("\n", "\r\n") % self.nowYear)
 
         toResource = yield toCalendar.calendarObjectWithName("2.ics")
         caldata = yield toResource.component()
@@ -277,32 +279,32 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid3
-DTSTART;TZID=US/Eastern:20060102T140000
+DTSTART;TZID=US/Eastern:%(now)s0102T140000
 DURATION:PT1H
 ATTENDEE:urn:uuid:home_bad
 CREATED:20060102T190000Z
 DTSTAMP:20051222T210507Z
 ORGANIZER:urn:uuid:home_bad
 RRULE:FREQ=DAILY;COUNT=5
-SUMMARY:event 6-%ctr
+SUMMARY:event 6-ctr
 END:VEVENT
 BEGIN:VEVENT
 UID:uid3
-RECURRENCE-ID;TZID=US/Eastern:20060104T140000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T140000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
 ORGANIZER:urn:uuid:home_bad
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 BEGIN:VALARM
 ACTION:AUDIO
 TRIGGER;RELATED=START:-PT10M
 END:VALARM
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n"))
+""".replace("\n", "\r\n") % self.nowYear)
         
         toResource = yield toCalendar.calendarObjectWithName("3.ics")
         caldata = yield toResource.component()
@@ -330,26 +332,26 @@
 END:VTIMEZONE
 BEGIN:VEVENT
 UID:uid4
-DTSTART;TZID=US/Eastern:20060104T160000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-RDATE;TZID=US/Eastern:20060104T160000
-SUMMARY:event 6-%ctr changed again
+RDATE;TZID=US/Eastern:%(now)s0104T160000
+SUMMARY:event 6-ctr changed again
 END:VEVENT
 BEGIN:VEVENT
 UID:uid4
-RECURRENCE-ID;TZID=US/Eastern:20060104T160000
-DTSTART;TZID=US/Eastern:20060104T160000
+RECURRENCE-ID;TZID=US/Eastern:%(now)s0104T160000
+DTSTART;TZID=US/Eastern:%(now)s0104T160000
 DURATION:PT1H
 CREATED:20060102T190000Z
 DESCRIPTION:Some notes
 DTSTAMP:20051222T210507Z
-SUMMARY:event 6-%ctr changed again
+SUMMARY:event 6-ctr changed again
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n"))
+""".replace("\n", "\r\n") % self.nowYear)
         
     @inlineCallbacks
     def test_migrateDuplicateAttachmentsCalendarFromFile(self):
@@ -385,7 +387,7 @@
         filter =  caldavxml.Filter(
                       caldavxml.ComponentFilter(
                           caldavxml.ComponentFilter(
-                              caldavxml.TimeRange(start="20060201T000000Z", end="20060202T000000Z"),
+                              caldavxml.TimeRange(start="%(now)s0201T000000Z"  % self.nowYear, end="%(now)s0202T000000Z" % self.nowYear),
                               name=("VEVENT", "VFREEBUSY", "VAVAILABILITY"),
                           ),
                           name="VCALENDAR",
@@ -613,44 +615,45 @@
         @inlineCallbacks
         def _defer1():
             yield cal1.createObjectResourceWithName("1.ics", VComponent.fromString(
-    "BEGIN:VCALENDAR\r\n"
-      "VERSION:2.0\r\n"
-      "PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
-      "CALSCALE:GREGORIAN\r\n"
-      "BEGIN:VTIMEZONE\r\n"
-        "TZID:US/Pacific\r\n"
-        "BEGIN:DAYLIGHT\r\n"
-          "TZOFFSETFROM:-0800\r\n"
-          "RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\n"
-          "DTSTART:20070311T020000\r\n"
-          "TZNAME:PDT\r\n"
-          "TZOFFSETTO:-0700\r\n"
-        "END:DAYLIGHT\r\n"
-        "BEGIN:STANDARD\r\n"
-          "TZOFFSETFROM:-0700\r\n"
-          "RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\n"
-          "DTSTART:20071104T020000\r\n"
-          "TZNAME:PST\r\n"
-          "TZOFFSETTO:-0800\r\n"
-        "END:STANDARD\r\n"
-      "END:VTIMEZONE\r\n"
-      "BEGIN:VEVENT\r\n"
-        "CREATED:20100203T013849Z\r\n"
-        "UID:uid1\r\n"
-        "DTEND;TZID=US/Pacific:20100207T173000\r\n"
-        "TRANSP:OPAQUE\r\n"
-        "SUMMARY:New Event\r\n"
-        "DTSTART;TZID=US/Pacific:20100207T170000\r\n"
-        "DTSTAMP:20100203T013909Z\r\n"
-        "SEQUENCE:3\r\n"
-        "BEGIN:VALARM\r\n"
-          "X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
-          "TRIGGER:-PT20M\r\n"
-          "ATTACH;VALUE=URI:Basso\r\n"
-          "ACTION:AUDIO\r\n"
-        "END:VALARM\r\n"
-      "END:VEVENT\r\n"
-    "END:VCALENDAR\r\n"
+"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:PDT
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:PST
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20100203T013849Z
+UID:uid1
+DTEND;TZID=US/Pacific:%(now)s0207T173000
+TRANSP:OPAQUE
+SUMMARY:New Event
+DTSTART;TZID=US/Pacific:%(now)s0207T170000
+DTSTAMP:20100203T013909Z
+SEQUENCE:3
+BEGIN:VALARM
+X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1
+TRIGGER:-PT20M
+ATTACH;VALUE=URI:Basso
+ACTION:AUDIO
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % self.nowYear
             ))
             yield txn1.commit()
         d1 = _defer1()
@@ -658,44 +661,45 @@
         @inlineCallbacks
         def _defer2():
             yield cal2.createObjectResourceWithName("2.ics", VComponent.fromString(
-    "BEGIN:VCALENDAR\r\n"
-      "VERSION:2.0\r\n"
-      "PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
-      "CALSCALE:GREGORIAN\r\n"
-      "BEGIN:VTIMEZONE\r\n"
-        "TZID:US/Pacific\r\n"
-        "BEGIN:DAYLIGHT\r\n"
-          "TZOFFSETFROM:-0800\r\n"
-          "RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\n"
-          "DTSTART:20070311T020000\r\n"
-          "TZNAME:PDT\r\n"
-          "TZOFFSETTO:-0700\r\n"
-        "END:DAYLIGHT\r\n"
-        "BEGIN:STANDARD\r\n"
-          "TZOFFSETFROM:-0700\r\n"
-          "RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\n"
-          "DTSTART:20071104T020000\r\n"
-          "TZNAME:PST\r\n"
-          "TZOFFSETTO:-0800\r\n"
-        "END:STANDARD\r\n"
-      "END:VTIMEZONE\r\n"
-      "BEGIN:VEVENT\r\n"
-        "CREATED:20100203T013849Z\r\n"
-        "UID:uid2\r\n"
-        "DTEND;TZID=US/Pacific:20100207T173000\r\n"
-        "TRANSP:OPAQUE\r\n"
-        "SUMMARY:New Event\r\n"
-        "DTSTART;TZID=US/Pacific:20100207T170000\r\n"
-        "DTSTAMP:20100203T013909Z\r\n"
-        "SEQUENCE:3\r\n"
-        "BEGIN:VALARM\r\n"
-          "X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1\r\n"
-          "TRIGGER:-PT20M\r\n"
-          "ATTACH;VALUE=URI:Basso\r\n"
-          "ACTION:AUDIO\r\n"
-        "END:VALARM\r\n"
-      "END:VEVENT\r\n"
-    "END:VCALENDAR\r\n"
+"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:PDT
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:PST
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20100203T013849Z
+UID:uid2
+DTEND;TZID=US/Pacific:%(now)s0207T173000
+TRANSP:OPAQUE
+SUMMARY:New Event
+DTSTART;TZID=US/Pacific:%(now)s0207T170000
+DTSTAMP:20100203T013909Z
+SEQUENCE:3
+BEGIN:VALARM
+X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1
+TRIGGER:-PT20M
+ATTACH;VALUE=URI:Basso
+ACTION:AUDIO
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % self.nowYear
             ))
             yield txn2.commit()
         d2 = _defer2()
@@ -1171,16 +1175,17 @@
 
 
     @inlineCallbacks
-    def test_recurrenceMax(self):
+    def test_recurrenceMinMax(self):
         """
-        Test CalendarObjectResource.recurrenceMax to make sure it handles a None value.
+        Test CalendarObjectResource.recurrenceMinMax to make sure it handles a None value.
         """
         
         # Valid object
         resource = yield self.calendarObjectUnderTest()
         
         # Valid lock
-        rMax = yield resource.recurrenceMax()
+        rMin, rMax = yield resource.recurrenceMinMax()
+        self.assertEqual(rMin, None)
         self.assertEqual(rMax, None)
 
     @inlineCallbacks
@@ -1196,7 +1201,7 @@
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
 BEGIN:VEVENT
 UID:instance
-DTSTART:20060102T140000Z
+DTSTART:%(now)s0102T140000Z
 DURATION:PT1H
 CREATED:20060102T190000Z
 DTSTAMP:20051222T210507Z
@@ -1204,7 +1209,7 @@
 SUMMARY:instance
 END:VEVENT
 END:VCALENDAR
-""".replace("\n", "\r\n")
+""".replace("\n", "\r\n") % self.nowYear
 
         self.patch(config, "FreeBusyIndexDelayedExpand", False)
 
@@ -1212,7 +1217,8 @@
         calendar = yield self.calendarUnderTest()
         component = Component.fromString(caldata)
         calendarObject = yield calendar.createCalendarObjectWithName("indexing.ics", component)
-        rmax = yield calendarObject.recurrenceMax()
+        rmin, rmax = yield calendarObject.recurrenceMinMax()
+        self.assertEqual(rmin, None)
         self.assertNotEqual(rmax.getYear(), 1900)
         instances = yield calendarObject.instances()
         self.assertNotEqual(len(instances), 0)

Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -44,6 +44,8 @@
 
     # Exceptions
     "QuotaExceeded",
+    "TimeRangeLowerLimit",
+    "TimeRangeUpperLimit",
 
     # Enumerations
     "BIND_OWN",
@@ -61,6 +63,24 @@
 
 
 
+class TimeRangeLowerLimit(Exception):
+    """
+    A request for time-range information too far in the past cannot be satisfied.
+    """
+
+    def __init__(self, lowerLimit):
+        self.limit = lowerLimit
+
+
+class TimeRangeUpperLimit(Exception):
+    """
+    A request for time-range information too far in the future cannot be satisfied.
+    """
+
+    def __init__(self, upperLimit):
+        self.limit = upperLimit
+
+
 class ICalendarTransaction(ICommonTransaction):
     """
     Transaction functionality required to be implemented by calendar stores.

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -72,6 +72,8 @@
     Delete, utcNowSQL, Union, Insert, Len, Max, Parameter, SavepointAction, \
     Select, Update, ColumnSyntax, TableSyntax, Upper
 
+from twistedcaldav.config import config
+
 from txdav.base.propertystore.base import PropertyName
 from txdav.base.propertystore.none import PropertyStore as NonePropertyStore
 from txdav.base.propertystore.sql import PropertyStore
@@ -84,6 +86,8 @@
 
 from txdav.base.datastore.util import normalizeUUIDOrNot
 
+from pycalendar.datetime import PyCalendarDateTime
+
 from cStringIO import StringIO
 from sqlparse import parse
 import collections
@@ -770,6 +774,14 @@
         Returns a deferred to a list of (uid, calendarName, eventName, maxDate)
         tuples.
         """
+
+        # Make sure cut off is after any lower limit truncation in the DB
+        if config.FreeBusyIndexLowerLimitDays:
+            truncateLowerLimit = PyCalendarDateTime.getToday()
+            truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
+            if cutoff < truncateLowerLimit:
+                raise ValueError("Cannot query events older than %s" % (truncateLowerLimit.getText(),))
+
         kwds = { "CutOff" : pyCalendarTodatetime(cutoff) }
         if batchSize is not None:
             kwds["batchSize"] = batchSize
@@ -786,6 +798,13 @@
         many were removed.
         """
 
+        # Make sure cut off is after any lower limit truncation in the DB
+        if config.FreeBusyIndexLowerLimitDays:
+            truncateLowerLimit = PyCalendarDateTime.getToday()
+            truncateLowerLimit.offsetDay(-config.FreeBusyIndexLowerLimitDays)
+            if cutoff < truncateLowerLimit:
+                raise ValueError("Cannot query events older than %s" % (truncateLowerLimit.getText(),))
+
         results = (yield self.eventsOlderThan(cutoff, batchSize=batchSize))
         count = 0
         for uid, calendarName, eventName, _ignore_maxDate in results:

Modified: CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -22,39 +22,36 @@
 
 import StringIO
 
-from twistedcaldav.sharing import SharedCollectionRecord
 
 from twisted.python import hashlib
 from twisted.internet.defer import succeed, inlineCallbacks, returnValue
 
-from twext.python.clsprop import classproperty
-from twext.python.log import Logger, LoggingMixIn
-
 from twistedcaldav.config import config
 from twistedcaldav.dateops import normalizeForIndex, pyCalendarTodatetime
 from twistedcaldav.memcachepool import CachePoolUserMixIn
 from twistedcaldav.notifications import NotificationRecord
-from twistedcaldav.query import (
-    calendarqueryfilter, calendarquery, addressbookquery, expression,
-    addressbookqueryfilter)
+from twistedcaldav.query import \
+    calendarqueryfilter, calendarquery, addressbookquery, expression, \
+    addressbookqueryfilter
 from twistedcaldav.query.sqlgenerator import sqlgenerator
 from twistedcaldav.sharing import Invite
+from twistedcaldav.sharing import SharedCollectionRecord
 
-from txdav.common.icommondatastore import (
-    IndexedSearchException, ReservationError, NoSuchObjectResourceError)
+from txdav.caldav.icalendarstore import TimeRangeLowerLimit, TimeRangeUpperLimit
+from txdav.common.icommondatastore import IndexedSearchException, \
+    ReservationError, NoSuchObjectResourceError
 
-from twext.enterprise.dal.syntax import Update, SavepointAction
-from twext.enterprise.dal.syntax import Insert
-from twext.enterprise.dal.syntax import Select
-from twext.enterprise.dal.syntax import Delete
-from twext.enterprise.dal.syntax import Parameter
 from txdav.common.datastore.sql_tables import (
     _BIND_MODE_OWN, _BIND_MODE_READ, _BIND_MODE_WRITE, _BIND_MODE_DIRECT,
     _BIND_STATUS_INVITED, _BIND_STATUS_ACCEPTED, _BIND_STATUS_DECLINED,
     _BIND_STATUS_INVALID, CALENDAR_BIND_TABLE, CALENDAR_HOME_TABLE,
     ADDRESSBOOK_HOME_TABLE, ADDRESSBOOK_BIND_TABLE, schema)
+from twext.enterprise.dal.syntax import Delete, Insert, Parameter, \
+    SavepointAction, Select, Update 
+from twext.python.clsprop import classproperty
+from twext.python.log import Logger, LoggingMixIn
 
-
+from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.duration import PyCalendarDuration
 
 log = Logger()
@@ -1126,31 +1123,37 @@
 
 
     @classproperty
-    def _notExpandedBeyondQuery(cls): #@NoSelf
+    def _notExpandedWithinQuery(cls): #@NoSelf
         """
         DAL query to satisfy L{PostgresLegacyIndexEmulator.notExpandedBeyond}.
         """
         co = schema.CALENDAR_OBJECT
-        return Select([co.RESOURCE_NAME], From=co,
-                      Where=(co.RECURRANCE_MAX < Parameter("minDate"))
-                      .And(co.CALENDAR_RESOURCE_ID == Parameter("resourceID")))
+        return Select(
+            [co.RESOURCE_NAME],
+            From=co,
+            Where=((co.RECURRANCE_MIN < Parameter("minDate"))
+                .Or(co.RECURRANCE_MAX < Parameter("maxDate")))
+                .And(co.CALENDAR_RESOURCE_ID == Parameter("resourceID"))
+        )
 
 
     @inlineCallbacks
-    def notExpandedBeyond(self, minDate):
+    def notExpandedWithin(self, minDate, maxDate):
         """
         Gives all resources which have not been expanded beyond a given date
         in the database.  (Unused; see above L{postgresqlgenerator}.
         """
         returnValue([row[0] for row in (
-            yield self._notExpandedBeyondQuery.on(
-                self._txn, minDate=pyCalendarTodatetime(normalizeForIndex(minDate)),
+            yield self._notExpandedWithinQuery.on(
+                self._txn,
+                minDate=pyCalendarTodatetime(normalizeForIndex(minDate)) if minDate is not None else None,
+                maxDate=pyCalendarTodatetime(normalizeForIndex(maxDate)),
                 resourceID=self.calendar._resourceID))]
         )
 
 
     @inlineCallbacks
-    def reExpandResource(self, name, expand_until):
+    def reExpandResource(self, name, expand_start, expand_end):
         """
         Given a resource name, remove it from the database and re-add it
         with a longer expansion.
@@ -1172,15 +1175,25 @@
 
         # Now do the re-expand using the appropriate transaction
         try:
+            doExpand = False
             if newTxn is None:
-                rmax = None
+                doExpand = True
             else:
-                rmax = (yield obj.recurrenceMax(txn=newTxn))
+                # We repeat this check because the resource may have been re-expanded by someone else
+                rmin, rmax = (yield obj.recurrenceMinMax(txn=newTxn))
+                
+                # If the resource is not fully expanded, see if within the required range or not.
+                # Note that expand_start could be None if no lower limit is applied, but expand_end will
+                # never be None
+                if rmax is not None and rmax < expand_end:
+                    doExpand = True
+                if rmin is not None and expand_start is not None and rmin > expand_start:
+                    doExpand = True
 
-            if rmax is None or rmax < expand_until:
+            if doExpand:
                 yield obj.updateDatabase(
                     (yield obj.component()),
-                    expand_until=expand_until,
+                    expand_until=expand_end,
                     reCreate=True,
                     txn=newTxn,
                 )
@@ -1190,15 +1203,15 @@
 
 
     @inlineCallbacks
-    def testAndUpdateIndex(self, minDate):
+    def testAndUpdateIndex(self, minDate, maxDate):
         # Find out if the index is expanded far enough
-        names = yield self.notExpandedBeyond(minDate)
+        names = yield self.notExpandedWithin(minDate, maxDate)
 
         # Actually expand recurrence max
         for name in names:
-            self.log_info("Search falls outside range of index for %s %s" %
-                          (name, minDate))
-            yield self.reExpandResource(name, minDate)
+            self.log_info("Search falls outside range of index for %s %s to %s" %
+                          (name, minDate, maxDate))
+            yield self.reExpandResource(name, minDate, maxDate)
 
 
     @inlineCallbacks
@@ -1230,6 +1243,9 @@
                 generator=generator
             )
             if qualifiers is not None:
+
+                today = PyCalendarDateTime.getToday()
+
                 # Determine how far we need to extend the current expansion of
                 # events. If we have an open-ended time-range we will expand
                 # one year past the start. That should catch bounded
@@ -1239,11 +1255,29 @@
                 if maxDate:
                     maxDate = maxDate.duplicate()
                     maxDate.setDateOnly(True)
+                    upperLimit = today + PyCalendarDuration(days=config.FreeBusyIndexExpandMaxDays)
+                    if maxDate > upperLimit:
+                        raise TimeRangeUpperLimit(upperLimit)
                     if isStartDate:
                         maxDate += PyCalendarDuration(days=365)
-                    yield self.testAndUpdateIndex(maxDate)
+
+                # Determine if the start date is too early for the restricted range we 
+                # are applying. If it is today or later we don't need to worry about truncation
+                # in the past.
+                minDate, _ignore_isEndDate = filter.getmintimerange()
+                if minDate >= today:
+                    minDate = None
+                if minDate is not None and config.FreeBusyIndexLowerLimitDays:
+                    truncateLowerLimit = today - PyCalendarDuration(days=config.FreeBusyIndexLowerLimitDays)
+                    if minDate < truncateLowerLimit:
+                        raise TimeRangeLowerLimit(truncateLowerLimit)
+
+                        
+                if maxDate is not None or minDate is not None:
+                    yield self.testAndUpdateIndex(minDate, maxDate)
+
             else:
-                # We cannot handler this filter in an indexed search
+                # We cannot handle this filter in an indexed search
                 raise IndexedSearchException()
         else:
             qualifiers = None

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current-oracle-dialect.sql	2012-06-25 16:27:34 UTC (rev 9383)
@@ -92,6 +92,7 @@
     "DROPBOX_ID" nvarchar2(255),
     "ORGANIZER" nvarchar2(255),
     "ORGANIZER_OBJECT" integer references CALENDAR_OBJECT,
+    "RECURRANCE_MIN" date,
     "RECURRANCE_MAX" date,
     "ACCESS" integer default 0 not null,
     "SCHEDULE_OBJECT" integer default 0,
@@ -257,7 +258,7 @@
     "VALUE" nvarchar2(255)
 );
 
-insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '9');
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '10');
 insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '2');
 insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '1');
 create index INVITE_INVITE_UID_9b0902ff on INVITE (

Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/current.sql	2012-06-25 16:27:34 UTC (rev 9383)
@@ -174,6 +174,7 @@
   DROPBOX_ID           varchar(255),
   ORGANIZER            varchar(255),
   ORGANIZER_OBJECT     integer      references CALENDAR_OBJECT,
+  RECURRANCE_MIN       date,        -- minimum date that recurrences have been expanded to.
   RECURRANCE_MAX       date,        -- maximum date that recurrences have been expanded to.
   ACCESS               integer      default 0 not null,
   SCHEDULE_OBJECT      boolean      default false,
@@ -494,6 +495,6 @@
   VALUE                         varchar(255)
 );
 
-insert into CALENDARSERVER values ('VERSION', '9');
+insert into CALENDARSERVER values ('VERSION', '10');
 insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '3');
 insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '1');

Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v9.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v9.sql	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/old/oracle-dialect/v9.sql	2012-06-25 16:27:34 UTC (rev 9383)
@@ -0,0 +1,359 @@
+create sequence RESOURCE_ID_SEQ;
+create sequence INSTANCE_ID_SEQ;
+create sequence REVISION_SEQ;
+create table CALENDAR_HOME (
+    "RESOURCE_ID" integer primary key,
+    "OWNER_UID" nvarchar2(255) unique,
+    "DATAVERSION" integer default 0 not null
+);
+
+create table CALENDAR_HOME_METADATA (
+    "RESOURCE_ID" integer primary key references CALENDAR_HOME on delete cascade,
+    "QUOTA_USED_BYTES" integer default 0 not null,
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table CALENDAR (
+    "RESOURCE_ID" integer primary key
+);
+
+create table CALENDAR_METADATA (
+    "RESOURCE_ID" integer primary key references CALENDAR on delete cascade,
+    "SUPPORTED_COMPONENTS" nvarchar2(255) default null,
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table INVITE (
+    "INVITE_UID" nvarchar2(255),
+    "NAME" nvarchar2(255),
+    "RECIPIENT_ADDRESS" nvarchar2(255),
+    "HOME_RESOURCE_ID" integer not null,
+    "RESOURCE_ID" integer not null
+);
+
+create table NOTIFICATION_HOME (
+    "RESOURCE_ID" integer primary key,
+    "OWNER_UID" nvarchar2(255) unique
+);
+
+create table NOTIFICATION (
+    "RESOURCE_ID" integer primary key,
+    "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME,
+    "NOTIFICATION_UID" nvarchar2(255),
+    "XML_TYPE" nvarchar2(255),
+    "XML_DATA" nclob,
+    "MD5" nchar(32),
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique("NOTIFICATION_UID", "NOTIFICATION_HOME_RESOURCE_ID")
+);
+
+create table CALENDAR_BIND (
+    "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+    "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+    "CALENDAR_RESOURCE_NAME" nvarchar2(255),
+    "BIND_MODE" integer not null,
+    "BIND_STATUS" integer not null,
+    "SEEN_BY_OWNER" integer not null,
+    "SEEN_BY_SHAREE" integer not null,
+    "MESSAGE" nclob, 
+    primary key("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_ID"), 
+    unique("CALENDAR_HOME_RESOURCE_ID", "CALENDAR_RESOURCE_NAME")
+);
+
+create table CALENDAR_BIND_MODE (
+    "ID" integer primary key,
+    "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('own', 0);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('write', 2);
+insert into CALENDAR_BIND_MODE (DESCRIPTION, ID) values ('direct', 3);
+create table CALENDAR_BIND_STATUS (
+    "ID" integer primary key,
+    "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invited', 0);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('accepted', 1);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('declined', 2);
+insert into CALENDAR_BIND_STATUS (DESCRIPTION, ID) values ('invalid', 3);
+create table CALENDAR_OBJECT (
+    "RESOURCE_ID" integer primary key,
+    "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+    "RESOURCE_NAME" nvarchar2(255),
+    "ICALENDAR_TEXT" nclob,
+    "ICALENDAR_UID" nvarchar2(255),
+    "ICALENDAR_TYPE" nvarchar2(255),
+    "ATTACHMENTS_MODE" integer default 0 not null,
+    "DROPBOX_ID" nvarchar2(255),
+    "ORGANIZER" nvarchar2(255),
+    "ORGANIZER_OBJECT" integer references CALENDAR_OBJECT,
+    "RECURRANCE_MAX" date,
+    "ACCESS" integer default 0 not null,
+    "SCHEDULE_OBJECT" integer default 0,
+    "SCHEDULE_TAG" nvarchar2(36) default null,
+    "SCHEDULE_ETAGS" nclob default null,
+    "PRIVATE_COMMENTS" integer default 0 not null,
+    "MD5" nchar(32),
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique("CALENDAR_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MO (
+    "ID" integer primary key,
+    "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('none', 0);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('read', 1);
+insert into CALENDAR_OBJECT_ATTACHMENTS_MO (DESCRIPTION, ID) values ('write', 2);
+create table CALENDAR_ACCESS_TYPE (
+    "ID" integer primary key,
+    "DESCRIPTION" nvarchar2(32) unique
+);
+
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('', 0);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('public', 1);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('private', 2);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('confidential', 3);
+insert into CALENDAR_ACCESS_TYPE (DESCRIPTION, ID) values ('restricted', 4);
+create table TIME_RANGE (
+    "INSTANCE_ID" integer primary key,
+    "CALENDAR_RESOURCE_ID" integer not null references CALENDAR on delete cascade,
+    "CALENDAR_OBJECT_RESOURCE_ID" integer not null references CALENDAR_OBJECT on delete cascade,
+    "FLOATING" integer not null,
+    "START_DATE" timestamp not null,
+    "END_DATE" timestamp not null,
+    "FBTYPE" integer not null,
+    "TRANSPARENT" integer not null
+);
+
+create table FREE_BUSY_TYPE (
+    "ID" integer primary key,
+    "DESCRIPTION" nvarchar2(16) unique
+);
+
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('unknown', 0);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('free', 1);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy', 2);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-unavailable', 3);
+insert into FREE_BUSY_TYPE (DESCRIPTION, ID) values ('busy-tentative', 4);
+create table TRANSPARENCY (
+    "TIME_RANGE_INSTANCE_ID" integer not null references TIME_RANGE on delete cascade,
+    "USER_ID" nvarchar2(255),
+    "TRANSPARENT" integer not null
+);
+
+create table ATTACHMENT (
+    "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+    "DROPBOX_ID" nvarchar2(255),
+    "CONTENT_TYPE" nvarchar2(255),
+    "SIZE" integer not null,
+    "MD5" nchar(32),
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "PATH" nvarchar2(1024), 
+    primary key("DROPBOX_ID", "PATH")
+);
+
+create table RESOURCE_PROPERTY (
+    "RESOURCE_ID" integer not null,
+    "NAME" nvarchar2(255),
+    "VALUE" nclob,
+    "VIEWER_UID" nvarchar2(255), 
+    primary key("RESOURCE_ID", "NAME", "VIEWER_UID")
+);
+
+create table ADDRESSBOOK_HOME (
+    "RESOURCE_ID" integer primary key,
+    "OWNER_UID" nvarchar2(255) unique,
+    "DATAVERSION" integer default 0 not null
+);
+
+create table ADDRESSBOOK_HOME_METADATA (
+    "RESOURCE_ID" integer primary key references ADDRESSBOOK_HOME on delete cascade,
+    "QUOTA_USED_BYTES" integer default 0 not null,
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK (
+    "RESOURCE_ID" integer primary key
+);
+
+create table ADDRESSBOOK_METADATA (
+    "RESOURCE_ID" integer primary key references ADDRESSBOOK on delete cascade,
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC'
+);
+
+create table ADDRESSBOOK_BIND (
+    "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+    "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+    "ADDRESSBOOK_RESOURCE_NAME" nvarchar2(255),
+    "BIND_MODE" integer not null,
+    "BIND_STATUS" integer not null,
+    "SEEN_BY_OWNER" integer not null,
+    "SEEN_BY_SHAREE" integer not null,
+    "MESSAGE" nclob, 
+    primary key("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_ID"), 
+    unique("ADDRESSBOOK_HOME_RESOURCE_ID", "ADDRESSBOOK_RESOURCE_NAME")
+);
+
+create table ADDRESSBOOK_OBJECT (
+    "RESOURCE_ID" integer primary key,
+    "ADDRESSBOOK_RESOURCE_ID" integer not null references ADDRESSBOOK on delete cascade,
+    "RESOURCE_NAME" nvarchar2(255),
+    "VCARD_TEXT" nclob,
+    "VCARD_UID" nvarchar2(255),
+    "MD5" nchar(32),
+    "CREATED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC',
+    "MODIFIED" timestamp default CURRENT_TIMESTAMP at time zone 'UTC', 
+    unique("ADDRESSBOOK_RESOURCE_ID", "RESOURCE_NAME"), 
+    unique("ADDRESSBOOK_RESOURCE_ID", "VCARD_UID")
+);
+
+create table CALENDAR_OBJECT_REVISIONS (
+    "CALENDAR_HOME_RESOURCE_ID" integer not null references CALENDAR_HOME,
+    "CALENDAR_RESOURCE_ID" integer references CALENDAR,
+    "CALENDAR_NAME" nvarchar2(255) default null,
+    "RESOURCE_NAME" nvarchar2(255),
+    "REVISION" integer not null,
+    "DELETED" integer not null
+);
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+    "ADDRESSBOOK_HOME_RESOURCE_ID" integer not null references ADDRESSBOOK_HOME,
+    "ADDRESSBOOK_RESOURCE_ID" integer references ADDRESSBOOK,
+    "ADDRESSBOOK_NAME" nvarchar2(255) default null,
+    "RESOURCE_NAME" nvarchar2(255),
+    "REVISION" integer not null,
+    "DELETED" integer not null
+);
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+    "NOTIFICATION_HOME_RESOURCE_ID" integer not null references NOTIFICATION_HOME on delete cascade,
+    "RESOURCE_NAME" nvarchar2(255),
+    "REVISION" integer not null,
+    "DELETED" integer not null, 
+    unique("NOTIFICATION_HOME_RESOURCE_ID", "RESOURCE_NAME")
+);
+
+create table APN_SUBSCRIPTIONS (
+    "TOKEN" nvarchar2(255),
+    "RESOURCE_KEY" nvarchar2(255),
+    "MODIFIED" integer not null,
+    "SUBSCRIBER_GUID" nvarchar2(255), 
+    primary key("TOKEN", "RESOURCE_KEY")
+);
+
+create table CALENDARSERVER (
+    "NAME" nvarchar2(255) primary key,
+    "VALUE" nvarchar2(255)
+);
+
+insert into CALENDARSERVER (NAME, VALUE) values ('VERSION', '9');
+insert into CALENDARSERVER (NAME, VALUE) values ('CALENDAR-DATAVERSION', '2');
+insert into CALENDARSERVER (NAME, VALUE) values ('ADDRESSBOOK-DATAVERSION', '1');
+create index INVITE_INVITE_UID_9b0902ff on INVITE (
+    INVITE_UID
+);
+
+create index INVITE_RESOURCE_ID_b36ddc23 on INVITE (
+    RESOURCE_ID
+);
+
+create index INVITE_HOME_RESOURCE__e9bdf77e on INVITE (
+    HOME_RESOURCE_ID
+);
+
+create index NOTIFICATION_NOTIFICA_f891f5f9 on NOTIFICATION (
+    NOTIFICATION_HOME_RESOURCE_ID
+);
+
+create index CALENDAR_BIND_RESOURC_e57964d4 on CALENDAR_BIND (
+    CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_CALEN_a9a453a9 on CALENDAR_OBJECT (
+    CALENDAR_RESOURCE_ID,
+    ICALENDAR_UID
+);
+
+create index CALENDAR_OBJECT_CALEN_96e83b73 on CALENDAR_OBJECT (
+    CALENDAR_RESOURCE_ID,
+    RECURRANCE_MAX
+);
+
+create index CALENDAR_OBJECT_ORGAN_7ce24750 on CALENDAR_OBJECT (
+    ORGANIZER_OBJECT
+);
+
+create index CALENDAR_OBJECT_DROPB_de041d80 on CALENDAR_OBJECT (
+    DROPBOX_ID
+);
+
+create index TIME_RANGE_CALENDAR_R_beb6e7eb on TIME_RANGE (
+    CALENDAR_RESOURCE_ID
+);
+
+create index TIME_RANGE_CALENDAR_O_acf37bd1 on TIME_RANGE (
+    CALENDAR_OBJECT_RESOURCE_ID
+);
+
+create index TRANSPARENCY_TIME_RAN_5f34467f on TRANSPARENCY (
+    TIME_RANGE_INSTANCE_ID
+);
+
+create index ATTACHMENT_CALENDAR_H_0078845c on ATTACHMENT (
+    CALENDAR_HOME_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_BIND_RESO_205aa75c on ADDRESSBOOK_BIND (
+    ADDRESSBOOK_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_3a3956c4 on CALENDAR_OBJECT_REVISIONS (
+    CALENDAR_HOME_RESOURCE_ID,
+    CALENDAR_RESOURCE_ID
+);
+
+create index CALENDAR_OBJECT_REVIS_2643d556 on CALENDAR_OBJECT_REVISIONS (
+    CALENDAR_RESOURCE_ID,
+    RESOURCE_NAME
+);
+
+create index CALENDAR_OBJECT_REVIS_265c8acf on CALENDAR_OBJECT_REVISIONS (
+    CALENDAR_RESOURCE_ID,
+    REVISION
+);
+
+create index ADDRESSBOOK_OBJECT_RE_f460d62d on ADDRESSBOOK_OBJECT_REVISIONS (
+    ADDRESSBOOK_HOME_RESOURCE_ID,
+    ADDRESSBOOK_RESOURCE_ID
+);
+
+create index ADDRESSBOOK_OBJECT_RE_9a848f39 on ADDRESSBOOK_OBJECT_REVISIONS (
+    ADDRESSBOOK_RESOURCE_ID,
+    RESOURCE_NAME
+);
+
+create index ADDRESSBOOK_OBJECT_RE_cb101e6b on ADDRESSBOOK_OBJECT_REVISIONS (
+    ADDRESSBOOK_RESOURCE_ID,
+    REVISION
+);
+
+create index NOTIFICATION_OBJECT_R_036a9cee on NOTIFICATION_OBJECT_REVISIONS (
+    NOTIFICATION_HOME_RESOURCE_ID,
+    REVISION
+);
+
+create index APN_SUBSCRIPTIONS_RES_9610d78e on APN_SUBSCRIPTIONS (
+    RESOURCE_KEY
+);
+

Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v9.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v9.sql	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/old/postgres-dialect/v9.sql	2012-06-25 16:27:34 UTC (rev 9383)
@@ -0,0 +1,499 @@
+-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
+
+----
+-- Copyright (c) 2010-2012 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.
+----
+
+-----------------
+-- Resource ID --
+-----------------
+
+create sequence RESOURCE_ID_SEQ;
+
+
+-------------------
+-- Calendar Home --
+-------------------
+
+create table CALENDAR_HOME (
+  RESOURCE_ID      integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  OWNER_UID        varchar(255) not null unique,                                 -- implicit index
+  DATAVERSION	   integer      default 0 not null
+);
+
+----------------------------
+-- Calendar Home Metadata --
+----------------------------
+
+create table CALENDAR_HOME_METADATA (
+  RESOURCE_ID      integer      primary key references CALENDAR_HOME on delete cascade, -- implicit index
+  QUOTA_USED_BYTES integer      default 0 not null,
+  CREATED          timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED         timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+--------------
+-- Calendar --
+--------------
+
+create table CALENDAR (
+  RESOURCE_ID integer   primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+-----------------------
+-- Calendar Metadata --
+-----------------------
+
+create table CALENDAR_METADATA (
+  RESOURCE_ID           integer   primary key references CALENDAR on delete cascade, -- implicit index
+  SUPPORTED_COMPONENTS  varchar(255) default null,
+  CREATED               timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED              timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+------------------------
+-- Sharing Invitation --
+------------------------
+
+create table INVITE (
+    INVITE_UID         varchar(255) not null,
+    NAME               varchar(255) not null,
+    RECIPIENT_ADDRESS  varchar(255) not null,
+    HOME_RESOURCE_ID   integer      not null,
+    RESOURCE_ID        integer      not null
+
+    -- Need primary key on (INVITE_UID, NAME, RECIPIENT_ADDRESS)?
+);
+
+create index INVITE_INVITE_UID on INVITE(INVITE_UID);
+create index INVITE_RESOURCE_ID on INVITE(RESOURCE_ID);
+create index INVITE_HOME_RESOURCE_ID on INVITE(HOME_RESOURCE_ID);
+
+---------------------------
+-- Sharing Notifications --
+---------------------------
+
+create table NOTIFICATION_HOME (
+  RESOURCE_ID integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  OWNER_UID   varchar(255) not null unique                                 -- implicit index
+);
+
+create table NOTIFICATION (
+  RESOURCE_ID                   integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  NOTIFICATION_HOME_RESOURCE_ID integer      not null references NOTIFICATION_HOME,
+  NOTIFICATION_UID              varchar(255) not null,
+  XML_TYPE                      varchar(255) not null,
+  XML_DATA                      text         not null,
+  MD5                           char(32)     not null,
+  CREATED                       timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                      timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+
+  unique(NOTIFICATION_UID, NOTIFICATION_HOME_RESOURCE_ID) -- implicit index
+);
+
+create index NOTIFICATION_NOTIFICATION_HOME_RESOURCE_ID on
+  NOTIFICATION(NOTIFICATION_HOME_RESOURCE_ID);
+
+-------------------
+-- Calendar Bind --
+-------------------
+
+-- Joins CALENDAR_HOME and CALENDAR
+
+create table CALENDAR_BIND (
+  CALENDAR_HOME_RESOURCE_ID integer      not null references CALENDAR_HOME,
+  CALENDAR_RESOURCE_ID      integer      not null references CALENDAR on delete cascade,
+
+  -- An invitation which hasn't been accepted yet will not yet have a resource
+  -- name, so this field may be null.
+
+  CALENDAR_RESOURCE_NAME    varchar(255),
+  BIND_MODE                 integer      not null, -- enum CALENDAR_BIND_MODE
+  BIND_STATUS               integer      not null, -- enum CALENDAR_BIND_STATUS
+  SEEN_BY_OWNER             boolean      not null,
+  SEEN_BY_SHAREE            boolean      not null,
+  MESSAGE                   text,
+
+  primary key(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID), -- implicit index
+  unique(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_NAME)     -- implicit index
+);
+
+create index CALENDAR_BIND_RESOURCE_ID on CALENDAR_BIND(CALENDAR_RESOURCE_ID);
+
+-- Enumeration of calendar bind modes
+
+create table CALENDAR_BIND_MODE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_MODE values (0, 'own'  );
+insert into CALENDAR_BIND_MODE values (1, 'read' );
+insert into CALENDAR_BIND_MODE values (2, 'write');
+insert into CALENDAR_BIND_MODE values (3, 'direct');
+
+-- Enumeration of statuses
+
+create table CALENDAR_BIND_STATUS (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_BIND_STATUS values (0, 'invited' );
+insert into CALENDAR_BIND_STATUS values (1, 'accepted');
+insert into CALENDAR_BIND_STATUS values (2, 'declined');
+insert into CALENDAR_BIND_STATUS values (3, 'invalid');
+
+
+---------------------
+-- Calendar Object --
+---------------------
+
+create table CALENDAR_OBJECT (
+  RESOURCE_ID          integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  CALENDAR_RESOURCE_ID integer      not null references CALENDAR on delete cascade,
+  RESOURCE_NAME        varchar(255) not null,
+  ICALENDAR_TEXT       text         not null,
+  ICALENDAR_UID        varchar(255) not null,
+  ICALENDAR_TYPE       varchar(255) not null,
+  ATTACHMENTS_MODE     integer      default 0 not null, -- enum CALENDAR_OBJECT_ATTACHMENTS_MODE
+  DROPBOX_ID           varchar(255),
+  ORGANIZER            varchar(255),
+  ORGANIZER_OBJECT     integer      references CALENDAR_OBJECT,
+  RECURRANCE_MAX       date,        -- maximum date that recurrences have been expanded to.
+  ACCESS               integer      default 0 not null,
+  SCHEDULE_OBJECT      boolean      default false,
+  SCHEDULE_TAG         varchar(36)  default null,
+  SCHEDULE_ETAGS       text         default null,
+  PRIVATE_COMMENTS     boolean      default false not null,
+  MD5                  char(32)     not null,
+  CREATED              timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED             timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+
+  unique(CALENDAR_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+
+  -- since the 'inbox' is a 'calendar resource' for the purpose of storing
+  -- calendar objects, this constraint has to be selectively enforced by the
+  -- application layer.
+
+  -- unique(CALENDAR_RESOURCE_ID, ICALENDAR_UID)
+);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_AND_ICALENDAR_UID on
+  CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, ICALENDAR_UID);
+
+create index CALENDAR_OBJECT_CALENDAR_RESOURCE_ID_RECURRANCE_MAX on
+  CALENDAR_OBJECT(CALENDAR_RESOURCE_ID, RECURRANCE_MAX);
+
+create index CALENDAR_OBJECT_ORGANIZER_OBJECT on
+  CALENDAR_OBJECT(ORGANIZER_OBJECT);
+
+create index CALENDAR_OBJECT_DROPBOX_ID on
+  CALENDAR_OBJECT(DROPBOX_ID);
+
+-- Enumeration of attachment modes
+
+create table CALENDAR_OBJECT_ATTACHMENTS_MODE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (0, 'none' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (1, 'read' );
+insert into CALENDAR_OBJECT_ATTACHMENTS_MODE values (2, 'write');
+
+
+-- Enumeration of calendar access types
+
+create table CALENDAR_ACCESS_TYPE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(32) not null unique
+);
+
+insert into CALENDAR_ACCESS_TYPE values (0, ''             );
+insert into CALENDAR_ACCESS_TYPE values (1, 'public'       );
+insert into CALENDAR_ACCESS_TYPE values (2, 'private'      );
+insert into CALENDAR_ACCESS_TYPE values (3, 'confidential' );
+insert into CALENDAR_ACCESS_TYPE values (4, 'restricted'   );
+
+-----------------
+-- Instance ID --
+-----------------
+
+create sequence INSTANCE_ID_SEQ;
+
+
+----------------
+-- Time Range --
+----------------
+
+create table TIME_RANGE (
+  INSTANCE_ID                 integer        primary key default nextval('INSTANCE_ID_SEQ'), -- implicit index
+  CALENDAR_RESOURCE_ID        integer        not null references CALENDAR on delete cascade,
+  CALENDAR_OBJECT_RESOURCE_ID integer        not null references CALENDAR_OBJECT on delete cascade,
+  FLOATING                    boolean        not null,
+  START_DATE                  timestamp      not null,
+  END_DATE                    timestamp      not null,
+  FBTYPE                      integer        not null,
+  TRANSPARENT                 boolean        not null
+);
+
+create index TIME_RANGE_CALENDAR_RESOURCE_ID on
+  TIME_RANGE(CALENDAR_RESOURCE_ID);
+create index TIME_RANGE_CALENDAR_OBJECT_RESOURCE_ID on
+  TIME_RANGE(CALENDAR_OBJECT_RESOURCE_ID);
+
+
+-- Enumeration of free/busy types
+
+create table FREE_BUSY_TYPE (
+  ID          integer     primary key,
+  DESCRIPTION varchar(16) not null unique
+);
+
+insert into FREE_BUSY_TYPE values (0, 'unknown'         );
+insert into FREE_BUSY_TYPE values (1, 'free'            );
+insert into FREE_BUSY_TYPE values (2, 'busy'            );
+insert into FREE_BUSY_TYPE values (3, 'busy-unavailable');
+insert into FREE_BUSY_TYPE values (4, 'busy-tentative'  );
+
+
+------------------
+-- Transparency --
+------------------
+
+create table TRANSPARENCY (
+  TIME_RANGE_INSTANCE_ID      integer      not null references TIME_RANGE on delete cascade,
+  USER_ID                     varchar(255) not null,
+  TRANSPARENT                 boolean      not null
+);
+
+create index TRANSPARENCY_TIME_RANGE_INSTANCE_ID on
+  TRANSPARENCY(TIME_RANGE_INSTANCE_ID);
+
+----------------
+-- Attachment --
+----------------
+
+create table ATTACHMENT (
+  CALENDAR_HOME_RESOURCE_ID   integer       not null references CALENDAR_HOME,
+  DROPBOX_ID                  varchar(255)  not null,
+  CONTENT_TYPE                varchar(255)  not null,
+  SIZE                        integer       not null,
+  MD5                         char(32)      not null,
+  CREATED                     timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                    timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  PATH                        varchar(1024) not null,
+
+  primary key(DROPBOX_ID, PATH) --implicit index
+);
+
+create index ATTACHMENT_CALENDAR_HOME_RESOURCE_ID on
+  ATTACHMENT(CALENDAR_HOME_RESOURCE_ID);
+
+-----------------------
+-- Resource Property --
+-----------------------
+
+create table RESOURCE_PROPERTY (
+  RESOURCE_ID integer      not null, -- foreign key: *.RESOURCE_ID
+  NAME        varchar(255) not null,
+  VALUE       text         not null, -- FIXME: xml?
+  VIEWER_UID  varchar(255),
+
+  primary key(RESOURCE_ID, NAME, VIEWER_UID) -- implicit index
+);
+
+
+----------------------
+-- AddressBook Home --
+----------------------
+
+create table ADDRESSBOOK_HOME (
+  RESOURCE_ID      integer      primary key default nextval('RESOURCE_ID_SEQ'), -- implicit index
+  OWNER_UID        varchar(255) not null unique,                                -- implicit index
+  DATAVERSION	   integer      default 0 not null
+);
+
+-------------------------------
+-- AddressBook Home Metadata --
+-------------------------------
+
+create table ADDRESSBOOK_HOME_METADATA (
+  RESOURCE_ID      integer      primary key references ADDRESSBOOK_HOME on delete cascade, -- implicit index
+  QUOTA_USED_BYTES integer      default 0 not null,
+  CREATED          timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED         timestamp    default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+-----------------
+-- AddressBook --
+-----------------
+
+create table ADDRESSBOOK (
+  RESOURCE_ID integer   primary key default nextval('RESOURCE_ID_SEQ') -- implicit index
+);
+
+
+--------------------------
+-- AddressBook Metadata --
+--------------------------
+
+create table ADDRESSBOOK_METADATA (
+  RESOURCE_ID integer   primary key references ADDRESSBOOK on delete cascade, -- implicit index
+  CREATED     timestamp default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED    timestamp default timezone('UTC', CURRENT_TIMESTAMP)
+);
+
+
+----------------------
+-- AddressBook Bind --
+----------------------
+
+-- Joins ADDRESSBOOK_HOME and ADDRESSBOOK
+
+create table ADDRESSBOOK_BIND (
+  ADDRESSBOOK_HOME_RESOURCE_ID integer      not null references ADDRESSBOOK_HOME,
+  ADDRESSBOOK_RESOURCE_ID      integer      not null references ADDRESSBOOK on delete cascade,
+
+  -- An invitation which hasn't been accepted yet will not yet have a resource
+  -- name, so this field may be null.
+
+  ADDRESSBOOK_RESOURCE_NAME    varchar(255),
+  BIND_MODE                    integer      not null, -- enum CALENDAR_BIND_MODE
+  BIND_STATUS                  integer      not null, -- enum CALENDAR_BIND_STATUS
+  SEEN_BY_OWNER                boolean      not null,
+  SEEN_BY_SHAREE               boolean      not null,
+  MESSAGE                      text,                  -- FIXME: xml?
+
+  primary key(ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID), -- implicit index
+  unique(ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_NAME)     -- implicit index
+);
+
+create index ADDRESSBOOK_BIND_RESOURCE_ID on
+  ADDRESSBOOK_BIND(ADDRESSBOOK_RESOURCE_ID);
+
+create table ADDRESSBOOK_OBJECT (
+  RESOURCE_ID             integer      primary key default nextval('RESOURCE_ID_SEQ'),    -- implicit index
+  ADDRESSBOOK_RESOURCE_ID integer      not null references ADDRESSBOOK on delete cascade,
+  RESOURCE_NAME           varchar(255) not null,
+  VCARD_TEXT              text         not null,
+  VCARD_UID               varchar(255) not null,
+  MD5                     char(32)     not null,
+  CREATED                 timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+  MODIFIED                timestamp    default timezone('UTC', CURRENT_TIMESTAMP),
+
+  unique(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME), -- implicit index
+  unique(ADDRESSBOOK_RESOURCE_ID, VCARD_UID)      -- implicit index
+);
+
+---------------
+-- Revisions --
+---------------
+
+create sequence REVISION_SEQ;
+
+
+---------------
+-- Revisions --
+---------------
+
+create table CALENDAR_OBJECT_REVISIONS (
+  CALENDAR_HOME_RESOURCE_ID integer      not null references CALENDAR_HOME,
+  CALENDAR_RESOURCE_ID      integer      references CALENDAR,
+  CALENDAR_NAME             varchar(255) default null,
+  RESOURCE_NAME             varchar(255),
+  REVISION                  integer      default nextval('REVISION_SEQ') not null,
+  DELETED                   boolean      not null
+);
+
+create index CALENDAR_OBJECT_REVISIONS_HOME_RESOURCE_ID_CALENDAR_RESOURCE_ID
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_HOME_RESOURCE_ID, CALENDAR_RESOURCE_ID);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, RESOURCE_NAME);
+
+create index CALENDAR_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+  on CALENDAR_OBJECT_REVISIONS(CALENDAR_RESOURCE_ID, REVISION);
+
+-------------------------------
+-- AddressBook Object Revisions --
+-------------------------------
+
+create table ADDRESSBOOK_OBJECT_REVISIONS (
+  ADDRESSBOOK_HOME_RESOURCE_ID integer      not null references ADDRESSBOOK_HOME,
+  ADDRESSBOOK_RESOURCE_ID      integer      references ADDRESSBOOK,
+  ADDRESSBOOK_NAME             varchar(255) default null,
+  RESOURCE_NAME                varchar(255),
+  REVISION                     integer      default nextval('REVISION_SEQ') not null,
+  DELETED                      boolean      not null
+);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_HOME_RESOURCE_ID_ADDRESSBOOK_RESOURCE_ID
+  on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_HOME_RESOURCE_ID, ADDRESSBOOK_RESOURCE_ID);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_RESOURCE_NAME
+  on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME);
+
+create index ADDRESSBOOK_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+  on ADDRESSBOOK_OBJECT_REVISIONS(ADDRESSBOOK_RESOURCE_ID, REVISION);
+
+-----------------------------------
+-- Notification Object Revisions --
+-----------------------------------
+
+create table NOTIFICATION_OBJECT_REVISIONS (
+  NOTIFICATION_HOME_RESOURCE_ID integer      not null references NOTIFICATION_HOME on delete cascade,
+  RESOURCE_NAME                 varchar(255),
+  REVISION                      integer      default nextval('REVISION_SEQ') not null,
+  DELETED                       boolean      not null,
+
+  unique(NOTIFICATION_HOME_RESOURCE_ID, RESOURCE_NAME) -- implicit index
+);
+
+create index NOTIFICATION_OBJECT_REVISIONS_RESOURCE_ID_REVISION
+  on NOTIFICATION_OBJECT_REVISIONS(NOTIFICATION_HOME_RESOURCE_ID, REVISION);
+
+-------------------------------------------
+-- Apple Push Notification Subscriptions --
+-------------------------------------------
+
+create table APN_SUBSCRIPTIONS (
+  TOKEN                         varchar(255) not null,
+  RESOURCE_KEY                  varchar(255) not null,
+  MODIFIED                      integer not null,
+  SUBSCRIBER_GUID               varchar(255) not null,
+
+  primary key(TOKEN, RESOURCE_KEY) -- implicit index
+);
+
+create index APN_SUBSCRIPTIONS_RESOURCE_KEY
+   on APN_SUBSCRIPTIONS(RESOURCE_KEY);
+
+
+--------------------
+-- Schema Version --
+--------------------
+
+create table CALENDARSERVER (
+  NAME                          varchar(255) primary key, -- implicit index
+  VALUE                         varchar(255)
+);
+
+insert into CALENDARSERVER values ('VERSION', '9');
+insert into CALENDARSERVER values ('CALENDAR-DATAVERSION', '3');
+insert into CALENDARSERVER values ('ADDRESSBOOK-DATAVERSION', '1');

Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_9_to_10.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_9_to_10.sql	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/oracle-dialect/upgrade_from_9_to_10.sql	2012-06-25 16:27:34 UTC (rev 9383)
@@ -0,0 +1,27 @@
+----
+-- Copyright (c) 2012 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.
+----
+
+--------------------------------------------------
+-- Upgrade database schema from VERSION 9 to 10 --
+--------------------------------------------------
+
+ -- Just need to modify one column
+alter table CALENDAR_OBJECT
+ add ("RECURRANCE_MIN" date);
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '10' where NAME = 'VERSION';

Added: CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_9_to_10.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_9_to_10.sql	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema/upgrades/postgres-dialect/upgrade_from_9_to_10.sql	2012-06-25 16:27:34 UTC (rev 9383)
@@ -0,0 +1,27 @@
+----
+-- Copyright (c) 2012 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.
+----
+
+--------------------------------------------------
+-- Upgrade database schema from VERSION 9 to 10 --
+--------------------------------------------------
+
+-- Just need to add one column
+alter table CALENDAR_OBJECT
+ add column RECURRANCE_MIN date;
+
+-- Now update the version
+-- No data upgrades
+update CALENDARSERVER set VALUE = '10' where NAME = 'VERSION';

Modified: CalendarServer/trunk/txdav/common/datastore/test/test_sql_tables.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/test_sql_tables.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/common/datastore/test/test_sql_tables.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -1,6 +1,6 @@
 # -*- test-case-name: txdav.caldav.datastore.test.test_sql -*-
 ##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2012 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.
@@ -85,7 +85,7 @@
         )
         schema = pathObj.getContent()
         pos = schema.find("('VERSION', '")
-        version = int(schema[pos+13])
+        version = int(schema[pos+13:pos+15])
         self.assertIn("insert into CALENDARSERVER (NAME, VALUE) "
                       "values ('VERSION', '%s');" % version,
                       self.translated())

Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py	2012-06-25 16:10:40 UTC (rev 9382)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py	2012-06-25 16:27:34 UTC (rev 9383)
@@ -49,6 +49,8 @@
 from twext.enterprise.ienterprise import AlreadyFinishedError
 from twistedcaldav.vcard import Component as ABComponent
 
+from pycalendar.datetime import PyCalendarDateTime
+
 md5key = PropertyName.fromElement(TwistedGETContentMD5)
 
 def allInstancesOf(cls):
@@ -373,12 +375,19 @@
                         objData, metadata = calendarObjNames[objectName]
                         yield calendar.createCalendarObjectWithName(
                             objectName,
-                            VComponent.fromString(objData),
+                            VComponent.fromString(updateToCurrentYear(objData)),
                             metadata = metadata,
                         )
     yield populateTxn.commit()
 
 
+def updateToCurrentYear(data):
+    """
+    Update the supplied iCalendar data so that all dates are updated to the current year.
+    """
+    
+    nowYear = PyCalendarDateTime.getToday().getYear()
+    return data % {"now":nowYear}
 
 @inlineCallbacks
 def resetCalendarMD5s(md5s, store):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120625/ace38744/attachment-0001.html>


More information about the calendarserver-changes mailing list