[CalendarServer-changes] [3511] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Thu Dec 11 11:53:02 PST 2008


Revision: 3511
          http://trac.macosforge.org/projects/calendarserver/changeset/3511
Author:   cdaboo at apple.com
Date:     2008-12-11 11:53:02 -0800 (Thu, 11 Dec 2008)
Log Message:
-----------
Need to handle the case of DTEND/DURATION normalization as iCal does not support DURATION and always
converts that to DTEND when it writes back an event.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/dateops.py
    CalendarServer/trunk/twistedcaldav/ical.py

Added Paths:
-----------
    CalendarServer/trunk/twistedcaldav/test/test_dateops.py

Modified: CalendarServer/trunk/twistedcaldav/dateops.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/dateops.py	2008-12-11 19:30:06 UTC (rev 3510)
+++ CalendarServer/trunk/twistedcaldav/dateops.py	2008-12-11 19:53:02 UTC (rev 3511)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2008 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.
@@ -19,6 +19,7 @@
 """
 
 __all__ = [
+    "normalizeStartEndDuration",
     "normalizeToUTC",
     "normalizeForIndex",
     "floatoffset",
@@ -34,6 +35,21 @@
 import datetime
 from vobject.icalendar import utc
 
+def normalizeStartEndDuration(dtstart, dtend=None, duration=None):
+    """
+    Given a DTSTART and DTEND or DURATION (or neither), return a normalized tuple of
+    DTSTART and DTEND.
+    """
+    
+    assert dtend is None or duration is None, "Cannot specify both dtend and duration" 
+    dtstart = normalizeToUTC(dtstart)
+    if dtend is not None:
+        dtend = normalizeToUTC(dtend)
+    elif duration:
+        dtend = dtstart + duration
+    
+    return (dtstart, dtend)
+
 def normalizeToUTC(dt):
     """
     Normalize a L{datetime.date} or L{datetime.datetime} object to UTC.

Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py	2008-12-11 19:30:06 UTC (rev 3510)
+++ CalendarServer/trunk/twistedcaldav/ical.py	2008-12-11 19:53:02 UTC (rev 3511)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2008 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.
@@ -34,7 +34,8 @@
 
 from twisted.web2.dav.util import allDataFromStream
 from twisted.web2.stream import IStream
-from twistedcaldav.dateops import compareDateTime, normalizeToUTC, timeRangesOverlap
+from twistedcaldav.dateops import compareDateTime, normalizeToUTC, timeRangesOverlap,\
+    normalizeStartEndDuration
 from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
 from twistedcaldav.instance import InstanceList
 from twistedcaldav.log import Logger
@@ -1664,10 +1665,48 @@
             if normalize_function:
                 prop.setValue(normalize_function(prop.value()))
 
+        # Do datetime normalization
+        self.normalizeDateTimes()
+
         # Do to all sub-components too
         for component in self.subcomponents():
             component.normalizeAll()
 
+    def normalizeDateTimes(self):
+        """
+        Normalize various datetime properties into UTC and handle DTEND/DURATION variants in such
+        a way that we can compare objects with slight differences.
+        
+        Strictly speaking we should not need to do this as clients should not be messing with
+        these properties - i.e. they should roundtrip them. Unfortunately some do...
+        """
+        
+        if self.name() == "VEVENT":
+            dtstart = self.getProperty("DTSTART")
+            dtend = self.getProperty("DTEND")
+            duration = self.getProperty("DURATION")
+            
+            newdtstart, newdtend = normalizeStartEndDuration(
+                dtstart.value(),
+                dtend.value() if dtend is not None else None,
+                duration.value() if duration is not None else None,
+            )
+            
+            dtstart.setValue(newdtstart)
+            try:
+                del dtstart.params()["TZID"]
+            except KeyError:
+                pass
+            if dtend is not None:
+                dtend.setValue(newdtend)
+                try:
+                    del dtend.params()["TZID"]
+                except KeyError:
+                    pass
+            elif duration is not None:
+                self.removeProperty(duration)
+                self.addProperty(Property("DTEND", newdtend))
+
     def normalizePropertyValueLists(self, propname):
         """
         Convert properties that have a list of values into single properties, to make it easier

Added: CalendarServer/trunk/twistedcaldav/test/test_dateops.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_dateops.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/test/test_dateops.py	2008-12-11 19:53:02 UTC (rev 3511)
@@ -0,0 +1,155 @@
+##
+# Copyright (c) 2008 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.
+##
+
+
+import twistedcaldav.test.util
+from vobject.icalendar import utc, getTzid
+from twistedcaldav.dateops import normalizeStartEndDuration
+from twistedcaldav.timezones import TimezoneCache
+import datetime
+
+#TODO: add tests for all the methods in dateops
+
+class Tests_normalizeStartEndDuration (twistedcaldav.test.util.TestCase):
+    """
+    Test abstract SQL DB class
+    """
+
+    def setUp(self):
+        super(Tests_normalizeStartEndDuration, self).setUp()
+        
+        TimezoneCache.create()
+        TimezoneCache.activeCache.loadTimezone("America/New_York")
+
+    def test_invalid(self):
+        
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
+        end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=utc)
+        duration = end - start
+        
+        self.assertRaises(AssertionError, normalizeStartEndDuration, start, end, duration)
+
+    def test_start_only_utc(self):
+        
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
+        
+        newstart, newend = normalizeStartEndDuration(start)
+        self.assertEqual(newstart, start)
+        self.assertTrue(newend is None)
+
+    def test_start_only_float(self):
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0)
+        
+        newstart, newend = normalizeStartEndDuration(start)
+        self.assertEqual(newstart, start)
+        self.assertTrue(newend is None)
+
+    def test_start_only_date(self):
+        start = datetime.date(2008, 1, 1)
+        
+        newstart, newend = normalizeStartEndDuration(start)
+        self.assertEqual(newstart, start)
+        self.assertTrue(newend is None)
+
+    def test_start_only_tzid(self):
+
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=getTzid("America/New_York"))
+        utcstart = datetime.datetime(2008, 1, 1, 5, 0, 0, tzinfo=utc)
+        
+        newstart, newend = normalizeStartEndDuration(start)
+        self.assertEqual(newstart, utcstart)
+        self.assertTrue(newend is None)
+
+    def test_start_end_utc(self):
+
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
+        end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=utc)
+        
+        newstart, newend = normalizeStartEndDuration(start, dtend=end)
+        self.assertEqual(newstart, start)
+        self.assertEqual(newend, end)
+
+    def test_start_end_float(self):
+
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0)
+        end = datetime.datetime(2008, 1, 1, 1, 0, 0)
+        
+        newstart, newend = normalizeStartEndDuration(start, dtend=end)
+        self.assertEqual(newstart, start)
+        self.assertEqual(newend, end)
+
+    def test_start_end_date(self):
+
+        start = datetime.date(2008, 1, 1)
+        end = datetime.date(2008, 1, 2)
+        
+        newstart, newend = normalizeStartEndDuration(start, dtend=end)
+        self.assertEqual(newstart, start)
+        self.assertEqual(newend, end)
+
+    def test_start_end_tzid(self):
+
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=getTzid("America/New_York"))
+        end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=getTzid("America/New_York"))
+        utcstart = datetime.datetime(2008, 1, 1, 5, 0, 0, tzinfo=utc)
+        utcend = datetime.datetime(2008, 1, 1, 6, 0, 0, tzinfo=utc)
+        
+        newstart, newend = normalizeStartEndDuration(start, dtend=end)
+        self.assertEqual(newstart, utcstart)
+        self.assertEqual(newend, utcend)
+
+    def test_start_duration_utc(self):
+
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=utc)
+        end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=utc)
+        duration = end - start
+        
+        newstart, newend = normalizeStartEndDuration(start, duration=duration)
+        self.assertEqual(newstart, start)
+        self.assertEqual(newend, end)
+
+    def test_start_duration_float(self):
+
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0)
+        end = datetime.datetime(2008, 1, 1, 1, 0, 0)
+        duration = end - start
+        
+        newstart, newend = normalizeStartEndDuration(start, duration=duration)
+        self.assertEqual(newstart, start)
+        self.assertEqual(newend, end)
+
+    def test_start_duration_date(self):
+
+        start = datetime.date(2008, 1, 1)
+        end = datetime.date(2008, 1, 2)
+        duration = end - start
+        
+        newstart, newend = normalizeStartEndDuration(start, duration=duration)
+        self.assertEqual(newstart, start)
+        self.assertEqual(newend, end)
+
+    def test_start_duration_tzid(self):
+ 
+        start = datetime.datetime(2008, 1, 1, 0, 0, 0, tzinfo=getTzid("America/New_York"))
+        end = datetime.datetime(2008, 1, 1, 1, 0, 0, tzinfo=getTzid("America/New_York"))
+        utcstart = datetime.datetime(2008, 1, 1, 5, 0, 0, tzinfo=utc)
+        utcend = datetime.datetime(2008, 1, 1, 6, 0, 0, tzinfo=utc)
+        duration = end - start
+        
+        newstart, newend = normalizeStartEndDuration(start, duration=duration)
+        self.assertEqual(newstart, utcstart)
+        self.assertEqual(newend, utcend)
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20081211/32732cd4/attachment-0001.html>


More information about the calendarserver-changes mailing list