@@ -129,6 +129,28 @@ def __init__(
129129 old_id = self .icalendar_component .pop ("UID" , None )
130130 self .icalendar_component .add ("UID" , id )
131131
132+ def set_end (self , end , move_dtstart = False ):
133+ """The RFC specifies that a VEVENT/VTODO cannot have both
134+ dtend/due and duration, so when setting dtend/due, the duration
135+ field must be evicted
136+
137+ WARNING: this method is likely to be deprecated and parts of
138+ it moved to the icalendar library. If you decide to use it,
139+ please put caldav<3.0 in the requirements.
140+ """
141+ i = self .icalendar_component
142+ if hasattr (end , "tzinfo" ) and not end .tzinfo :
143+ end = end .astimezone (timezone .utc )
144+ duration = self .get_duration ()
145+ i .pop ("DURATION" , None )
146+ i .pop (self ._ENDPARAM , None )
147+
148+ if move_dtstart and duration and "DTSTART" in i :
149+ i .pop ("DTSTART" )
150+ i .add ("DTSTART" , end - duration )
151+
152+ i .add (self ._ENDPARAM , end )
153+
132154 def add_organizer (self ) -> None :
133155 """
134156 goes via self.client, finds the principal, figures out the right attendee-format and adds an
@@ -1027,13 +1049,13 @@ class Event(CalendarObjectResource):
10271049 """
10281050 The `Event` object is used to represent an event (VEVENT).
10291051
1030- As of 2020-12 it adds nothing to the inheritated class. (I have
1052+ As of 2020-12 it adds very little to the inheritated class. (I have
10311053 frequently asked myself if we need those subclasses ... perhaps
10321054 not)
10331055 """
10341056
1057+ set_dtend = CalendarObjectResource .set_end
10351058 _ENDPARAM = "DTEND"
1036- pass
10371059
10381060
10391061class Journal (CalendarObjectResource ):
@@ -1073,9 +1095,13 @@ def __init__(
10731095
10741096class Todo (CalendarObjectResource ):
10751097 """The `Todo` object is used to represent a todo item (VTODO). A
1076- Todo-object can be completed. Extra logic for different ways to
1077- complete one recurrence of a recurrent todo. Extra logic to
1078- handle due vs duration.
1098+ Todo-object can be completed.
1099+
1100+ There is some extra logic here - arguably none of it belongs to
1101+ the caldav library, and should be moved either to the icalendar
1102+ library or to the plann library (plann is a cli-tool, should
1103+ probably be split up into one library for advanced calendaring
1104+ operations and the cli-tool as separate packages)
10791105 """
10801106
10811107 _ENDPARAM = "DUE"
@@ -1421,7 +1447,7 @@ def set_due(self, due, move_dtstart=False, check_dependent=False):
14211447 please put caldav<2.0 in the requirements.
14221448
14231449 WARNING: the check_dependent-logic may be rewritten to support
1424- RFC9253 in 1.x already
1450+ RFC9253 in 3.x
14251451 """
14261452 i = self .icalendar_component
14271453 if hasattr (due , "tzinfo" ) and not due .tzinfo :
@@ -1440,12 +1466,6 @@ def set_due(self, due, move_dtstart=False, check_dependent=False):
14401466 raise error .ConsistencyError (
14411467 "parent object has due/end %s, cannot procrastinate child object without first procrastinating parent object"
14421468 )
1443- duration = self .get_duration ()
1444- i .pop ("DURATION" , None )
1445- i .pop ("DUE" , None )
1446-
1447- if move_dtstart and duration and "DTSTART" in i :
1448- i .pop ("DTSTART" )
1449- i .add ("DTSTART" , due - duration )
1469+ CalendarObjectResource .set_end (self , due , move_dtstart )
14501470
1451- i . add ( "DUE" , due )
1471+ set_end = set_due
0 commit comments