Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/attr.py: 35%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

296 statements  

1"""Attributes of Components and properties.""" 

2 

3from __future__ import annotations 

4 

5import itertools 

6from datetime import date, datetime, timedelta 

7from typing import TYPE_CHECKING, Optional, Sequence, Union 

8 

9from icalendar.enums import BUSYTYPE, CLASS, STATUS, TRANSP, StrEnum 

10from icalendar.error import IncompleteComponent, InvalidCalendar 

11from icalendar.parser_tools import SEQUENCE_TYPES 

12from icalendar.prop import vCalAddress, vCategory, vDDDTypes, vDuration, vRecur, vText 

13from icalendar.timezone import tzp 

14 

15if TYPE_CHECKING: 

16 from icalendar.cal import Component 

17 

18 

19def _get_rdates( 

20 self: Component, 

21) -> list[Union[tuple[date, None], tuple[datetime, None], tuple[datetime, datetime]]]: 

22 """The RDATE property defines the list of DATE-TIME values for recurring components. 

23 

24 RDATE is defined in :rfc:`5545`. 

25 The return value is a list of tuples ``(start, end)``. 

26 

27 ``start`` can be a :class:`datetime.date` or a :class:`datetime.datetime`, 

28 with and without timezone. 

29 

30 ``end`` is :obj:`None` if the end is not specified and a :class:`datetime.datetime` 

31 if the end is specified. 

32 

33 Value Type: 

34 The default value type for this property is DATE-TIME. 

35 The value type can be set to DATE or PERIOD. 

36 

37 Property Parameters: 

38 IANA, non-standard, value data type, and time 

39 zone identifier property parameters can be specified on this 

40 property. 

41 

42 Conformance: 

43 This property can be specified in recurring "VEVENT", 

44 "VTODO", and "VJOURNAL" calendar components as well as in the 

45 "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" 

46 calendar component. 

47 

48 Description: 

49 This property can appear along with the "RRULE" 

50 property to define an aggregate set of repeating occurrences. 

51 When they both appear in a recurring component, the recurrence 

52 instances are defined by the union of occurrences defined by both 

53 the "RDATE" and "RRULE". 

54 

55 The recurrence dates, if specified, are used in computing the 

56 recurrence set. The recurrence set is the complete set of 

57 recurrence instances for a calendar component. The recurrence set 

58 is generated by considering the initial "DTSTART" property along 

59 with the "RRULE", "RDATE", and "EXDATE" properties contained 

60 within the recurring component. The "DTSTART" property defines 

61 the first instance in the recurrence set. The "DTSTART" property 

62 value SHOULD match the pattern of the recurrence rule, if 

63 specified. The recurrence set generated with a "DTSTART" property 

64 value that doesn't match the pattern of the rule is undefined. 

65 The final recurrence set is generated by gathering all of the 

66 start DATE-TIME values generated by any of the specified "RRULE" 

67 and "RDATE" properties, and then excluding any start DATE-TIME 

68 values specified by "EXDATE" properties. This implies that start 

69 DATE-TIME values specified by "EXDATE" properties take precedence 

70 over those specified by inclusion properties (i.e., "RDATE" and 

71 "RRULE"). Where duplicate instances are generated by the "RRULE" 

72 and "RDATE" properties, only one recurrence is considered. 

73 Duplicate instances are ignored. 

74 

75 Example: 

76 Below, we set one RDATE in a list and get the resulting tuple of start and end. 

77 

78 .. code-block:: pycon 

79 

80 >>> from icalendar import Event 

81 >>> from datetime import datetime 

82 >>> event = Event() 

83 

84 # Add a list of recurrence dates 

85 >>> event.add("RDATE", [datetime(2025, 4, 28, 16, 5)]) 

86 >>> event.rdates 

87 [(datetime.datetime(2025, 4, 28, 16, 5), None)] 

88 

89 .. note:: 

90 

91 You cannot modify the RDATE value by modifying the result. 

92 Use :func:`icalendar.cal.Component.add` to add values. 

93 

94 If you want to compute recurrences, have a look at :ref:`Related projects`. 

95 

96 """ 

97 result = [] 

98 rdates = self.get("RDATE", []) 

99 for rdates in (rdates,) if not isinstance(rdates, list) else rdates: 

100 for dts in rdates.dts: 

101 rdate = dts.dt 

102 if isinstance(rdate, tuple): 

103 # we have a period as rdate 

104 if isinstance(rdate[1], timedelta): 

105 result.append((rdate[0], rdate[0] + rdate[1])) 

106 else: 

107 result.append(rdate) 

108 else: 

109 # we have a date/datetime 

110 result.append((rdate, None)) 

111 return result 

112 

113 

114rdates_property = property(_get_rdates) 

115 

116 

117def _get_exdates(self: Component) -> list[date | datetime]: 

118 """EXDATE defines the list of DATE-TIME exceptions for recurring components. 

119 

120 EXDATE is defined in :rfc:`5545`. 

121 

122 Value Type: 

123 The default value type for this property is DATE-TIME. 

124 The value type can be set to DATE. 

125 

126 Property Parameters: 

127 IANA, non-standard, value data type, and time 

128 zone identifier property parameters can be specified on this 

129 property. 

130 

131 Conformance: 

132 This property can be specified in recurring "VEVENT", 

133 "VTODO", and "VJOURNAL" calendar components as well as in the 

134 "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" 

135 calendar component. 

136 

137 Description: 

138 The exception dates, if specified, are used in 

139 computing the recurrence set. The recurrence set is the complete 

140 set of recurrence instances for a calendar component. The 

141 recurrence set is generated by considering the initial "DTSTART" 

142 property along with the "RRULE", "RDATE", and "EXDATE" properties 

143 contained within the recurring component. The "DTSTART" property 

144 defines the first instance in the recurrence set. The "DTSTART" 

145 property value SHOULD match the pattern of the recurrence rule, if 

146 specified. The recurrence set generated with a "DTSTART" property 

147 value that doesn't match the pattern of the rule is undefined. 

148 The final recurrence set is generated by gathering all of the 

149 start DATE-TIME values generated by any of the specified "RRULE" 

150 and "RDATE" properties, and then excluding any start DATE-TIME 

151 values specified by "EXDATE" properties. This implies that start 

152 DATE-TIME values specified by "EXDATE" properties take precedence 

153 over those specified by inclusion properties (i.e., "RDATE" and 

154 "RRULE"). When duplicate instances are generated by the "RRULE" 

155 and "RDATE" properties, only one recurrence is considered. 

156 Duplicate instances are ignored. 

157 

158 The "EXDATE" property can be used to exclude the value specified 

159 in "DTSTART". However, in such cases, the original "DTSTART" date 

160 MUST still be maintained by the calendaring and scheduling system 

161 because the original "DTSTART" value has inherent usage 

162 dependencies by other properties such as the "RECURRENCE-ID". 

163 

164 Example: 

165 Below, we add an exdate in a list and get the resulting list of exdates. 

166 

167 .. code-block:: pycon 

168 

169 >>> from icalendar import Event 

170 >>> from datetime import datetime 

171 >>> event = Event() 

172 

173 # Add a list of excluded dates 

174 >>> event.add("EXDATE", [datetime(2025, 4, 28, 16, 5)]) 

175 >>> event.exdates 

176 [datetime.datetime(2025, 4, 28, 16, 5)] 

177 

178 .. note:: 

179 

180 You cannot modify the EXDATE value by modifying the result. 

181 Use :func:`icalendar.cal.Component.add` to add values. 

182 

183 If you want to compute recurrences, have a look at :ref:`Related projects`. 

184 

185 """ 

186 result = [] 

187 exdates = self.get("EXDATE", []) 

188 for exdates in (exdates,) if not isinstance(exdates, list) else exdates: 

189 for dts in exdates.dts: 

190 exdate = dts.dt 

191 # we have a date/datetime 

192 result.append(exdate) 

193 return result 

194 

195 

196exdates_property = property(_get_exdates) 

197 

198 

199def _get_rrules(self: Component) -> list[vRecur]: 

200 """RRULE defines a rule or repeating pattern for recurring components. 

201 

202 RRULE is defined in :rfc:`5545`. 

203 :rfc:`7529` adds the ``SKIP`` parameter :class:`icalendar.prop.vSkip`. 

204 

205 Property Parameters: 

206 IANA and non-standard property parameters can 

207 be specified on this property. 

208 

209 Conformance: 

210 This property can be specified in recurring "VEVENT", 

211 "VTODO", and "VJOURNAL" calendar components as well as in the 

212 "STANDARD" and "DAYLIGHT" sub-components of the "VTIMEZONE" 

213 calendar component, but it SHOULD NOT be specified more than once. 

214 The recurrence set generated with multiple "RRULE" properties is 

215 undefined. 

216 

217 Description: 

218 The recurrence rule, if specified, is used in computing 

219 the recurrence set. The recurrence set is the complete set of 

220 recurrence instances for a calendar component. The recurrence set 

221 is generated by considering the initial "DTSTART" property along 

222 with the "RRULE", "RDATE", and "EXDATE" properties contained 

223 within the recurring component. The "DTSTART" property defines 

224 the first instance in the recurrence set. The "DTSTART" property 

225 value SHOULD be synchronized with the recurrence rule, if 

226 specified. The recurrence set generated with a "DTSTART" property 

227 value not synchronized with the recurrence rule is undefined. The 

228 final recurrence set is generated by gathering all of the start 

229 DATE-TIME values generated by any of the specified "RRULE" and 

230 "RDATE" properties, and then excluding any start DATE-TIME values 

231 specified by "EXDATE" properties. This implies that start DATE- 

232 TIME values specified by "EXDATE" properties take precedence over 

233 those specified by inclusion properties (i.e., "RDATE" and 

234 "RRULE"). Where duplicate instances are generated by the "RRULE" 

235 and "RDATE" properties, only one recurrence is considered. 

236 Duplicate instances are ignored. 

237 

238 The "DTSTART" property specified within the iCalendar object 

239 defines the first instance of the recurrence. In most cases, a 

240 "DTSTART" property of DATE-TIME value type used with a recurrence 

241 rule, should be specified as a date with local time and time zone 

242 reference to make sure all the recurrence instances start at the 

243 same local time regardless of time zone changes. 

244 

245 If the duration of the recurring component is specified with the 

246 "DTEND" or "DUE" property, then the same exact duration will apply 

247 to all the members of the generated recurrence set. Else, if the 

248 duration of the recurring component is specified with the 

249 "DURATION" property, then the same nominal duration will apply to 

250 all the members of the generated recurrence set and the exact 

251 duration of each recurrence instance will depend on its specific 

252 start time. For example, recurrence instances of a nominal 

253 duration of one day will have an exact duration of more or less 

254 than 24 hours on a day where a time zone shift occurs. The 

255 duration of a specific recurrence may be modified in an exception 

256 component or simply by using an "RDATE" property of PERIOD value 

257 type. 

258 

259 Examples: 

260 Daily for 10 occurrences: 

261 

262 .. code-block:: pycon 

263 

264 >>> from icalendar import Event 

265 >>> from datetime import datetime 

266 >>> from zoneinfo import ZoneInfo 

267 >>> event = Event() 

268 >>> event.start = datetime(1997, 9, 2, 9, 0, tzinfo=ZoneInfo("America/New_York")) 

269 >>> event.add("RRULE", "FREQ=DAILY;COUNT=10") 

270 >>> print(event.to_ical()) 

271 BEGIN:VEVENT 

272 DTSTART;TZID=America/New_York:19970902T090000 

273 RRULE:FREQ=DAILY;COUNT=10 

274 END:VEVENT 

275 >>> event.rrules 

276 [vRecur({'FREQ': ['DAILY'], 'COUNT': [10]})] 

277 

278 Daily until December 24, 1997: 

279 

280 .. code-block:: pycon 

281 

282 >>> from icalendar import Event, vRecur 

283 >>> from datetime import datetime 

284 >>> from zoneinfo import ZoneInfo 

285 >>> event = Event() 

286 >>> event.start = datetime(1997, 9, 2, 9, 0, tzinfo=ZoneInfo("America/New_York")) 

287 >>> event.add("RRULE", vRecur({"FREQ": ["DAILY"]}, until=datetime(1997, 12, 24, tzinfo=ZoneInfo("UTC")))) 

288 >>> print(event.to_ical()) 

289 BEGIN:VEVENT 

290 DTSTART;TZID=America/New_York:19970902T090000 

291 RRULE:FREQ=DAILY;UNTIL=19971224T000000Z 

292 END:VEVENT 

293 >>> event.rrules 

294 [vRecur({'FREQ': ['DAILY'], 'UNTIL': [datetime.datetime(1997, 12, 24, 0, 0, tzinfo=ZoneInfo(key='UTC'))]})] 

295 

296 .. note:: 

297 

298 You cannot modify the RRULE value by modifying the result. 

299 Use :func:`icalendar.cal.Component.add` to add values. 

300 

301 If you want to compute recurrences, have a look at :ref:`Related projects`. 

302 

303 """ # noqa: E501 

304 rrules = self.get("RRULE", []) 

305 if not isinstance(rrules, list): 

306 return [rrules] 

307 return rrules 

308 

309 

310rrules_property = property(_get_rrules) 

311 

312 

313def multi_language_text_property( 

314 main_prop: str, compatibility_prop: Optional[str], doc: str 

315) -> property: 

316 """This creates a text property. 

317 

318 This property can be defined several times with different ``LANGUAGE`` parameters. 

319 

320 Args: 

321 main_prop (str): The property to set and get, such as ``NAME`` 

322 compatibility_prop (str): An old property used before, such as ``X-WR-CALNAME`` 

323 doc (str): The documentation string 

324 """ 

325 

326 def fget(self: Component) -> Optional[str]: 

327 """Get the property""" 

328 result = self.get(main_prop) 

329 if result is None and compatibility_prop is not None: 

330 result = self.get(compatibility_prop) 

331 if isinstance(result, list): 

332 for item in result: 

333 if "LANGUAGE" not in item.params: 

334 return item 

335 return result 

336 

337 def fset(self: Component, value: Optional[str]): 

338 """Set the property.""" 

339 fdel(self) 

340 if value is not None: 

341 self.add(main_prop, value) 

342 

343 def fdel(self: Component): 

344 """Delete the property.""" 

345 self.pop(main_prop, None) 

346 if compatibility_prop is not None: 

347 self.pop(compatibility_prop, None) 

348 

349 return property(fget, fset, fdel, doc) 

350 

351 

352def single_int_property(prop: str, default: int, doc: str) -> property: 

353 """Create a property for an int value that exists only once. 

354 

355 Args: 

356 prop: The name of the property 

357 default: The default value 

358 doc: The documentation string 

359 """ 

360 

361 def fget(self: Component) -> int: 

362 """Get the property""" 

363 try: 

364 return int(self.get(prop, default)) 

365 except ValueError as e: 

366 raise InvalidCalendar(f"{prop} must be an int") from e 

367 

368 def fset(self: Component, value: Optional[int]): 

369 """Set the property.""" 

370 fdel(self) 

371 if value is not None: 

372 self.add(prop, value) 

373 

374 def fdel(self: Component): 

375 """Delete the property.""" 

376 self.pop(prop, None) 

377 

378 return property(fget, fset, fdel, doc) 

379 

380 

381def single_utc_property(name: str, docs: str) -> property: 

382 """Create a property to access a value of datetime in UTC timezone. 

383 

384 Args: 

385 name: name of the property 

386 docs: documentation string 

387 """ 

388 docs = ( 

389 f"""The {name} property. datetime in UTC 

390 

391 All values will be converted to a datetime in UTC. 

392 """ 

393 + docs 

394 ) 

395 

396 def fget(self: Component) -> Optional[datetime]: 

397 """Get the value.""" 

398 if name not in self: 

399 return None 

400 dt = self.get(name) 

401 if isinstance(dt, vText): 

402 # we might be in an attribute that is not typed 

403 value = vDDDTypes.from_ical(dt) 

404 else: 

405 value = getattr(dt, "dt", dt) 

406 if value is None or not isinstance(value, date): 

407 raise InvalidCalendar(f"{name} must be a datetime in UTC, not {value}") 

408 return tzp.localize_utc(value) 

409 

410 def fset(self: Component, value: Optional[datetime]): 

411 """Set the value""" 

412 if value is None: 

413 fdel(self) 

414 return 

415 if not isinstance(value, date): 

416 raise TypeError(f"{name} takes a datetime in UTC, not {value}") 

417 fdel(self) 

418 self.add(name, tzp.localize_utc(value)) 

419 

420 def fdel(self: Component): 

421 """Delete the property.""" 

422 self.pop(name, None) 

423 

424 return property(fget, fset, fdel, doc=docs) 

425 

426 

427def single_string_property( 

428 name: str, docs: str, other_name: Optional[str] = None, default: str = "" 

429) -> property: 

430 """Create a property to access a single string value.""" 

431 

432 def fget(self: Component) -> str: 

433 """Get the value.""" 

434 result = self.get( 

435 name, None if other_name is None else self.get(other_name, None) 

436 ) 

437 if result is None or result == []: 

438 return default 

439 if isinstance(result, list): 

440 return result[0] 

441 return result 

442 

443 def fset(self: Component, value: Optional[str]): 

444 """Set the value. 

445 

446 Setting the value to None will delete it. 

447 """ 

448 fdel(self) 

449 if value is not None: 

450 self.add(name, value) 

451 

452 def fdel(self: Component): 

453 """Delete the property.""" 

454 self.pop(name, None) 

455 if other_name is not None: 

456 self.pop(other_name, None) 

457 

458 return property(fget, fset, fdel, doc=docs) 

459 

460 

461color_property = single_string_property( 

462 "COLOR", 

463 """This property specifies a color used for displaying the component. 

464 

465 This implements :rfc:`7986` ``COLOR`` property. 

466 

467 Property Parameters: 

468 IANA and non-standard property parameters can 

469 be specified on this property. 

470 

471 Conformance: 

472 This property can be specified once in an iCalendar 

473 object or in ``VEVENT``, ``VTODO``, or ``VJOURNAL`` calendar components. 

474 

475 Description: 

476 This property specifies a color that clients MAY use 

477 when presenting the relevant data to a user. Typically, this 

478 would appear as the "background" color of events or tasks. The 

479 value is a case-insensitive color name taken from the CSS3 set of 

480 names, defined in Section 4.3 of `W3C.REC-css3-color-20110607 <https://www.w3.org/TR/css-color-3/>`_. 

481 

482 Example: 

483 ``"turquoise"``, ``"#ffffff"`` 

484 

485 .. code-block:: pycon 

486 

487 >>> from icalendar import Todo 

488 >>> todo = Todo() 

489 >>> todo.color = "green" 

490 >>> print(todo.to_ical()) 

491 BEGIN:VTODO 

492 COLOR:green 

493 END:VTODO 

494 """, 

495) 

496 

497sequence_property = single_int_property( 

498 "SEQUENCE", 

499 0, 

500 """This property defines the revision sequence number of the calendar component within a sequence of revisions. 

501 

502Value Type: 

503 INTEGER 

504 

505Property Parameters: 

506 IANA and non-standard property parameters can be specified on this property. 

507 

508Conformance: 

509 The property can be specified in "VEVENT", "VTODO", or 

510 "VJOURNAL" calendar component. 

511 

512Description: 

513 When a calendar component is created, its sequence 

514 number is 0. It is monotonically incremented by the "Organizer's" 

515 CUA each time the "Organizer" makes a significant revision to the 

516 calendar component. 

517 

518 The "Organizer" includes this property in an iCalendar object that 

519 it sends to an "Attendee" to specify the current version of the 

520 calendar component. 

521 

522 The "Attendee" includes this property in an iCalendar object that 

523 it sends to the "Organizer" to specify the version of the calendar 

524 component to which the "Attendee" is referring. 

525 

526 A change to the sequence number is not the mechanism that an 

527 "Organizer" uses to request a response from the "Attendees". The 

528 "RSVP" parameter on the "ATTENDEE" property is used by the 

529 "Organizer" to indicate that a response from the "Attendees" is 

530 requested. 

531 

532 Recurrence instances of a recurring component MAY have different 

533 sequence numbers. 

534 

535Examples: 

536 The following is an example of this property for a calendar 

537 component that was just created by the "Organizer": 

538 

539 .. code-block:: pycon 

540 

541 >>> from icalendar import Event 

542 >>> event = Event() 

543 >>> event.sequence 

544 0 

545 

546 The following is an example of this property for a calendar 

547 component that has been revised 10 different times by the 

548 "Organizer": 

549 

550 .. code-block:: pycon 

551 

552 >>> from icalendar import Calendar 

553 >>> calendar = Calendar.example("issue_156_RDATE_with_PERIOD_TZID_khal") 

554 >>> event = calendar.events[0] 

555 >>> event.sequence 

556 10 

557 """, # noqa: E501 

558) 

559 

560 

561def _get_categories(component: Component) -> list[str]: 

562 """Get all the categories.""" 

563 categories: Optional[vCategory | list[vCategory]] = component.get("CATEGORIES") 

564 if isinstance(categories, list): 

565 _set_categories( 

566 component, 

567 list(itertools.chain.from_iterable(cat.cats for cat in categories)), 

568 ) 

569 return _get_categories(component) 

570 if categories is None: 

571 categories = vCategory([]) 

572 component.add("CATEGORIES", categories) 

573 return categories.cats 

574 

575 

576def _set_categories(component: Component, cats: Optional[Sequence[str]]) -> None: 

577 """Set the categories.""" 

578 if not cats and cats != []: 

579 _del_categories(component) 

580 return 

581 component["CATEGORIES"] = categories = vCategory(cats) 

582 if isinstance(cats, list): 

583 cats.clear() 

584 cats.extend(categories.cats) 

585 categories.cats = cats 

586 

587 

588def _del_categories(component: Component) -> None: 

589 """Delete the categories.""" 

590 component.pop("CATEGORIES", None) 

591 

592 

593categories_property = property( 

594 _get_categories, 

595 _set_categories, 

596 _del_categories, 

597 """This property defines the categories for a component. 

598 

599Property Parameters: 

600 IANA, non-standard, and language property parameters can be specified on this 

601 property. 

602 

603Conformance: 

604 The property can be specified within "VEVENT", "VTODO", or "VJOURNAL" calendar 

605 components. 

606 Since :rfc:`7986` it can also be defined on a "VCALENDAR" component. 

607 

608Description: 

609 This property is used to specify categories or subtypes 

610 of the calendar component. The categories are useful in searching 

611 for a calendar component of a particular type and category. 

612 Within the "VEVENT", "VTODO", or "VJOURNAL" calendar components, 

613 more than one category can be specified as a COMMA-separated list 

614 of categories. 

615 

616Example: 

617 Below, we add the categories to an event: 

618 

619 .. code-block:: pycon 

620 

621 >>> from icalendar import Event 

622 >>> event = Event() 

623 >>> event.categories = ["Work", "Meeting"] 

624 >>> print(event.to_ical()) 

625 BEGIN:VEVENT 

626 CATEGORIES:Work,Meeting 

627 END:VEVENT 

628 >>> event.categories.append("Lecture") 

629 >>> event.categories == ["Work", "Meeting", "Lecture"] 

630 True 

631 

632.. note:: 

633 

634 At present, we do not take the LANGUAGE parameter into account. 

635""", 

636) 

637 

638 

639def _get_attendees(self: Component) -> list[vCalAddress]: 

640 """Get attendees.""" 

641 value = self.get("ATTENDEE") 

642 if value is None: 

643 value = [] 

644 self["ATTENDEE"] = value 

645 return value 

646 if isinstance(value, vCalAddress): 

647 return [value] 

648 return value 

649 

650 

651def _set_attendees(self: Component, value: list[vCalAddress] | vCalAddress | None): 

652 """Set attendees.""" 

653 _del_attendees(self) 

654 if value is None: 

655 return 

656 if not isinstance(value, list): 

657 value = [value] 

658 self["ATTENDEE"] = value 

659 

660 

661def _del_attendees(self: Component): 

662 """Delete all attendees.""" 

663 self.pop("ATTENDEE", None) 

664 

665 

666attendees_property = property( 

667 _get_attendees, 

668 _set_attendees, 

669 _del_attendees, 

670 """ATTENDEE defines one or more "Attendees" within a calendar component. 

671 

672Conformance: 

673 This property MUST be specified in an iCalendar object 

674 that specifies a group-scheduled calendar entity. This property 

675 MUST NOT be specified in an iCalendar object when publishing the 

676 calendar information (e.g., NOT in an iCalendar object that 

677 specifies the publication of a calendar user's busy time, event, 

678 to-do, or journal). This property is not specified in an 

679 iCalendar object that specifies only a time zone definition or 

680 that defines calendar components that are not group-scheduled 

681 components, but are components only on a single user's calendar. 

682 

683Description: 

684 This property MUST only be specified within calendar 

685 components to specify participants, non-participants, and the 

686 chair of a group-scheduled calendar entity. The property is 

687 specified within an "EMAIL" category of the "VALARM" calendar 

688 component to specify an email address that is to receive the email 

689 type of iCalendar alarm. 

690 

691Examples: 

692 Add a new attendee to an existing event. 

693 

694 .. code-block:: pycon 

695 

696 >>> from icalendar import Event, vCalAddress 

697 >>> event = Event() 

698 >>> event.attendees.append(vCalAddress("mailto:me@my-domain.com")) 

699 >>> print(event.to_ical()) 

700 BEGIN:VEVENT 

701 ATTENDEE:mailto:me@my-domain.com 

702 END:VEVENT 

703 

704 Create an email alarm with several attendees: 

705 

706 >>> from icalendar import Alarm, vCalAddress 

707 >>> alarm = Alarm.new(attendees = [ 

708 ... vCalAddress("mailto:me@my-domain.com"), 

709 ... vCalAddress("mailto:you@my-domain.com"), 

710 ... ], summary = "Email alarm") 

711 >>> print(alarm.to_ical()) 

712 BEGIN:VALARM 

713 ATTENDEE:mailto:me@my-domain.com 

714 ATTENDEE:mailto:you@my-domain.com 

715 SUMMARY:Email alarm 

716 END:VALARM 

717""", 

718) 

719 

720uid_property = single_string_property( 

721 "UID", 

722 """UID specifies the persistent, globally unique identifier for a component. 

723 

724We recommend using :func:`uuid.uuid4` to generate new values. 

725 

726Returns: 

727 The value of the UID property as a string or ``""`` if no value is set. 

728 

729Description: 

730 The "UID" itself MUST be a globally unique identifier. 

731 The generator of the identifier MUST guarantee that the identifier 

732 is unique. 

733 

734 This is the method for correlating scheduling messages with the 

735 referenced "VEVENT", "VTODO", or "VJOURNAL" calendar component. 

736 The full range of calendar components specified by a recurrence 

737 set is referenced by referring to just the "UID" property value 

738 corresponding to the calendar component. The "RECURRENCE-ID" 

739 property allows the reference to an individual instance within the 

740 recurrence set. 

741 

742 This property is an important method for group-scheduling 

743 applications to match requests with later replies, modifications, 

744 or deletion requests. Calendaring and scheduling applications 

745 MUST generate this property in "VEVENT", "VTODO", and "VJOURNAL" 

746 calendar components to assure interoperability with other group- 

747 scheduling applications. This identifier is created by the 

748 calendar system that generates an iCalendar object. 

749 

750 Implementations MUST be able to receive and persist values of at 

751 least 255 octets for this property, but they MUST NOT truncate 

752 values in the middle of a UTF-8 multi-octet sequence. 

753 

754 :rfc:`7986` states that UID can be used, for 

755 example, to identify duplicate calendar streams that a client may 

756 have been given access to. It can be used in conjunction with the 

757 "LAST-MODIFIED" property also specified on the "VCALENDAR" object 

758 to identify the most recent version of a calendar. 

759 

760Conformance: 

761 :rfc:`5545` states that the "UID" property can be specified on "VEVENT", "VTODO", 

762 and "VJOURNAL" calendar components. 

763 :rfc:`7986` modifies the definition of the "UID" property to 

764 allow it to be defined in an iCalendar object. 

765 :rfc:`9074` adds a "UID" property to "VALARM" components to allow a unique 

766 identifier to be specified. The value of this property can then be used 

767 to refer uniquely to the "VALARM" component. 

768 

769 This property can be specified once only. 

770 

771Security: 

772 :rfc:`7986` states that UID values MUST NOT include any data that 

773 might identify a user, host, domain, or any other security- or 

774 privacy-sensitive information. It is RECOMMENDED that calendar user 

775 agents now generate "UID" values that are hex-encoded random 

776 Universally Unique Identifier (UUID) values as defined in 

777 Sections 4.4 and 4.5 of :rfc:`4122`. 

778 You can use the :mod:`uuid` module to generate new UUIDs. 

779 

780Compatibility: 

781 For Alarms, ``X-ALARMUID`` is also considered. 

782 

783Examples: 

784 The following is an example of such a property value: 

785 ``5FC53010-1267-4F8E-BC28-1D7AE55A7C99``. 

786 

787 Set the UID of a calendar: 

788 

789 .. code-block:: pycon 

790 

791 >>> from icalendar import Calendar 

792 >>> from uuid import uuid4 

793 >>> calendar = Calendar() 

794 >>> calendar.uid = uuid4() 

795 >>> print(calendar.to_ical()) 

796 BEGIN:VCALENDAR 

797 UID:d755cef5-2311-46ed-a0e1-6733c9e15c63 

798 END:VCALENDAR 

799 

800""", 

801) 

802 

803summary_property = multi_language_text_property( 

804 "SUMMARY", 

805 None, 

806 """SUMMARY defines a short summary or subject for the calendar component. 

807 

808Property Parameters: 

809 IANA, non-standard, alternate text 

810 representation, and language property parameters can be specified 

811 on this property. 

812 

813Conformance: 

814 The property can be specified in "VEVENT", "VTODO", 

815 "VJOURNAL", or "VALARM" calendar components. 

816 

817Description: 

818 This property is used in the "VEVENT", "VTODO", and 

819 "VJOURNAL" calendar components to capture a short, one-line 

820 summary about the activity or journal entry. 

821 

822 This property is used in the "VALARM" calendar component to 

823 capture the subject of an EMAIL category of alarm. 

824 

825Examples: 

826 The following is an example of this property: 

827 

828 .. code-block:: pycon 

829 

830 SUMMARY:Department Party 

831""", 

832) 

833 

834description_property = multi_language_text_property( 

835 "DESCRIPTION", 

836 None, 

837 """DESCRIPTION provides a more complete description of the calendar component than that provided by the "SUMMARY" property. 

838 

839Property Parameters: 

840 IANA, non-standard, alternate text 

841 representation, and language property parameters can be specified 

842 on this property. 

843 

844Conformance: 

845 The property can be specified in the "VEVENT", "VTODO", 

846 "VJOURNAL", or "VALARM" calendar components. The property can be 

847 specified multiple times only within a "VJOURNAL" calendar 

848 component. 

849 

850Description: 

851 This property is used in the "VEVENT" and "VTODO" to 

852 capture lengthy textual descriptions associated with the activity. 

853 

854 This property is used in the "VALARM" calendar component to 

855 capture the display text for a DISPLAY category of alarm, and to 

856 capture the body text for an EMAIL category of alarm. 

857 

858Examples: 

859 The following is an example of this property with formatted 

860 line breaks in the property value: 

861 

862 .. code-block:: pycon 

863 

864 DESCRIPTION:Meeting to provide technical review for "Phoenix" 

865 design.\\nHappy Face Conference Room. Phoenix design team 

866 MUST attend this meeting.\\nRSVP to team leader. 

867 

868 """, # noqa: E501 

869) 

870 

871 

872def create_single_property( 

873 prop: str, 

874 value_attr: Optional[str], 

875 value_type: tuple[type], 

876 type_def: type, 

877 doc: str, 

878 vProp: type = vDDDTypes, # noqa: N803 

879): 

880 """Create a single property getter and setter. 

881 

882 :param prop: The name of the property. 

883 :param value_attr: The name of the attribute to get the value from. 

884 :param value_type: The type of the value. 

885 :param type_def: The type of the property. 

886 :param doc: The docstring of the property. 

887 :param vProp: The type of the property from :mod:`icalendar.prop`. 

888 """ 

889 

890 def p_get(self: Component): 

891 default = object() 

892 result = self.get(prop, default) 

893 if result is default: 

894 return None 

895 if isinstance(result, list): 

896 raise InvalidCalendar(f"Multiple {prop} defined.") 

897 value = result if value_attr is None else getattr(result, value_attr, result) 

898 if not isinstance(value, value_type): 

899 raise InvalidCalendar( 

900 f"{prop} must be either a " 

901 f"{' or '.join(t.__name__ for t in value_type)}," 

902 f" not {value}." 

903 ) 

904 return value 

905 

906 def p_set(self: Component, value) -> None: 

907 if value is None: 

908 p_del(self) 

909 return 

910 if not isinstance(value, value_type): 

911 raise TypeError( 

912 f"Use {' or '.join(t.__name__ for t in value_type)}, " 

913 f"not {type(value).__name__}." 

914 ) 

915 self[prop] = vProp(value) 

916 if prop in self.exclusive: 

917 for other_prop in self.exclusive: 

918 if other_prop != prop: 

919 self.pop(other_prop, None) 

920 

921 p_set.__annotations__["value"] = p_get.__annotations__["return"] = Optional[ 

922 type_def 

923 ] 

924 

925 def p_del(self: Component): 

926 self.pop(prop) 

927 

928 p_doc = f"""The {prop} property. 

929 

930 {doc} 

931 

932 Accepted values: {", ".join(t.__name__ for t in value_type)}. 

933 If the attribute has invalid values, we raise InvalidCalendar. 

934 If the value is absent, we return None. 

935 You can also delete the value with del or by setting it to None. 

936 """ 

937 return property(p_get, p_set, p_del, p_doc) 

938 

939 

940X_MOZ_SNOOZE_TIME_property = single_utc_property( 

941 "X-MOZ-SNOOZE-TIME", "Thunderbird: Alarms before this time are snoozed." 

942) 

943X_MOZ_LASTACK_property = single_utc_property( 

944 "X-MOZ-LASTACK", "Thunderbird: Alarms before this time are acknowledged." 

945) 

946 

947 

948def property_get_duration(self: Component) -> Optional[timedelta]: 

949 """Getter for property DURATION.""" 

950 default = object() 

951 duration = self.get("duration", default) 

952 if isinstance(duration, vDDDTypes): 

953 return duration.dt 

954 if isinstance(duration, vDuration): 

955 return duration.td 

956 if duration is not default and not isinstance(duration, timedelta): 

957 raise InvalidCalendar( 

958 f"DURATION must be a timedelta, not {type(duration).__name__}." 

959 ) 

960 return None 

961 

962 

963def property_set_duration(self: Component, value: Optional[timedelta]): 

964 """Setter for property DURATION.""" 

965 if value is None: 

966 self.pop("duration", None) 

967 return 

968 if not isinstance(value, timedelta): 

969 raise TypeError(f"Use timedelta, not {type(value).__name__}.") 

970 self["duration"] = vDuration(value) 

971 self.pop("DTEND") 

972 self.pop("DUE") 

973 

974 

975def property_del_duration(self: Component): 

976 """Delete property DURATION.""" 

977 self.pop("DURATION") 

978 

979 

980property_doc_duration_template = """The DURATION property. 

981 

982The "DTSTART" property for a "{component}" specifies the inclusive 

983start of the {component}. 

984The "DURATION" property in conjunction with the DTSTART property 

985for a "{component}" calendar component specifies the non-inclusive end 

986of the event. 

987 

988If you would like to calculate the duration of a {component}, do not use this. 

989Instead use the duration property (lower case). 

990""" 

991 

992 

993def duration_property(component: str) -> property: 

994 """Return the duration property.""" 

995 return property( 

996 property_get_duration, 

997 property_set_duration, 

998 property_del_duration, 

999 property_doc_duration_template.format(component=component), 

1000 ) 

1001 

1002 

1003def multi_text_property(name: str, docs: str) -> property: 

1004 """Get a property that can occur several times and is text. 

1005 

1006 Examples: Journal.descriptions, Event.comments 

1007 """ 

1008 

1009 def fget(self: Component) -> list[str]: 

1010 """Get the values.""" 

1011 descriptions = self.get(name) 

1012 if descriptions is None: 

1013 return [] 

1014 if not isinstance(descriptions, SEQUENCE_TYPES): 

1015 return [descriptions] 

1016 return descriptions 

1017 

1018 def fset(self: Component, values: Optional[str | Sequence[str]]): 

1019 """Set the values.""" 

1020 fdel(self) 

1021 if values is None: 

1022 return 

1023 if isinstance(values, str): 

1024 self.add(name, values) 

1025 else: 

1026 for description in values: 

1027 self.add(name, description) 

1028 

1029 def fdel(self: Component): 

1030 """Delete the values.""" 

1031 self.pop(name) 

1032 

1033 return property(fget, fset, fdel, docs) 

1034 

1035 

1036descriptions_property = multi_text_property( 

1037 "DESCRIPTION", 

1038 """DESCRIPTION provides a more complete description of the calendar component than that provided by the "SUMMARY" property. 

1039 

1040Property Parameters: 

1041 IANA, non-standard, alternate text 

1042 representation, and language property parameters can be specified 

1043 on this property. 

1044 

1045Conformance: 

1046 The property can be 

1047 specified multiple times only within a "VJOURNAL" calendar component. 

1048 

1049Description: 

1050 This property is used in the "VJOURNAL" calendar component to 

1051 capture one or more textual journal entries. 

1052 

1053Examples: 

1054 The following is an example of this property with formatted 

1055 line breaks in the property value: 

1056 

1057 .. code-block:: pycon 

1058 

1059 DESCRIPTION:Meeting to provide technical review for "Phoenix" 

1060 design.\\nHappy Face Conference Room. Phoenix design team 

1061 MUST attend this meeting.\\nRSVP to team leader. 

1062 

1063""", # noqa: E501 

1064) 

1065 

1066comments_property = multi_text_property( 

1067 "COMMENT", 

1068 """COMMENT is used to specify a comment to the calendar user. 

1069 

1070Purpose: 

1071 This property specifies non-processing information intended 

1072 to provide a comment to the calendar user. 

1073 

1074Conformance: 

1075 In :rfc:`5545`, this property can be specified multiple times in 

1076 "VEVENT", "VTODO", "VJOURNAL", and "VFREEBUSY" calendar components 

1077 as well as in the "STANDARD" and "DAYLIGHT" sub-components. 

1078 In :rfc:`7953`, this property can be specified multiple times in 

1079 "VAVAILABILITY" and "VAVAILABLE". 

1080 

1081Property Parameters: 

1082 IANA, non-standard, alternate text 

1083 representation, and language property parameters can be specified 

1084 on this property. 

1085 

1086""", 

1087) 

1088 

1089 

1090def _get_organizer(self: Component) -> Optional[vCalAddress]: 

1091 """ORGANIZER defines the organizer for a calendar component. 

1092 

1093 Property Parameters: 

1094 IANA, non-standard, language, common name, 

1095 directory entry reference, and sent-by property parameters can be 

1096 specified on this property. 

1097 

1098 Conformance: 

1099 This property MUST be specified in an iCalendar object 

1100 that specifies a group-scheduled calendar entity. This property 

1101 MUST be specified in an iCalendar object that specifies the 

1102 publication of a calendar user's busy time. This property MUST 

1103 NOT be specified in an iCalendar object that specifies only a time 

1104 zone definition or that defines calendar components that are not 

1105 group-scheduled components, but are components only on a single 

1106 user's calendar. 

1107 

1108 Description: 

1109 This property is specified within the "VEVENT", 

1110 "VTODO", and "VJOURNAL" calendar components to specify the 

1111 organizer of a group-scheduled calendar entity. The property is 

1112 specified within the "VFREEBUSY" calendar component to specify the 

1113 calendar user requesting the free or busy time. When publishing a 

1114 "VFREEBUSY" calendar component, the property is used to specify 

1115 the calendar that the published busy time came from. 

1116 

1117 The property has the property parameters "CN", for specifying the 

1118 common or display name associated with the "Organizer", "DIR", for 

1119 specifying a pointer to the directory information associated with 

1120 the "Organizer", "SENT-BY", for specifying another calendar user 

1121 that is acting on behalf of the "Organizer". The non-standard 

1122 parameters may also be specified on this property. If the 

1123 "LANGUAGE" property parameter is specified, the identified 

1124 language applies to the "CN" parameter value. 

1125 """ 

1126 return self.get("ORGANIZER") 

1127 

1128 

1129def _set_organizer(self: Component, value: Optional[vCalAddress | str]): 

1130 """Set the value.""" 

1131 _del_organizer(self) 

1132 if value is not None: 

1133 self.add("ORGANIZER", value) 

1134 

1135 

1136def _del_organizer(self: Component): 

1137 """Delete the value.""" 

1138 self.pop("ORGANIZER") 

1139 

1140 

1141organizer_property = property(_get_organizer, _set_organizer, _del_organizer) 

1142 

1143 

1144def single_string_enum_property( 

1145 name: str, enum: type[StrEnum], default: StrEnum, docs: str 

1146) -> property: 

1147 """Create a property to access a single string value and convert it to an enum.""" 

1148 prop = single_string_property(name, docs, default=default) 

1149 

1150 def fget(self: Component) -> StrEnum: 

1151 """Get the value.""" 

1152 value = prop.fget(self) 

1153 if value == default: 

1154 return default 

1155 return enum(str(value)) 

1156 

1157 def fset(self: Component, value: str | StrEnum | None) -> None: 

1158 """Set the value.""" 

1159 if value == "": 

1160 value = None 

1161 prop.fset(self, value) 

1162 

1163 return property(fget, fset, prop.fdel, doc=docs) 

1164 

1165 

1166busy_type_property = single_string_enum_property( 

1167 "BUSYTYPE", 

1168 BUSYTYPE, 

1169 BUSYTYPE.BUSY_UNAVAILABLE, 

1170 """BUSYTYPE specifies the default busy time type. 

1171 

1172Returns: 

1173 :class:`icalendar.enums.BUSYTYPE` 

1174 

1175Description: 

1176 This property is used to specify the default busy time 

1177 type. The values correspond to those used by the FBTYPE" 

1178 parameter used on a "FREEBUSY" property, with the exception that 

1179 the "FREE" value is not used in this property. If not specified 

1180 on a component that allows this property, the default is "BUSY- 

1181 UNAVAILABLE". 

1182""", 

1183) 

1184 

1185priority_property = single_int_property( 

1186 "PRIORITY", 

1187 0, 

1188 """ 

1189 

1190Conformance: 

1191 This property can be specified in "VEVENT" and "VTODO" calendar components 

1192 according to :rfc:`5545`. 

1193 :rfc:`7953` adds this property to "VAVAILABILITY". 

1194 

1195Description: 

1196 This priority is specified as an integer in the range 0 

1197 to 9. A value of 0 specifies an undefined priority. A value of 1 

1198 is the highest priority. A value of 2 is the second highest 

1199 priority. Subsequent numbers specify a decreasing ordinal 

1200 priority. A value of 9 is the lowest priority. 

1201 

1202 A CUA with a three-level priority scheme of "HIGH", "MEDIUM", and 

1203 "LOW" is mapped into this property such that a property value in 

1204 the range of 1 to 4 specifies "HIGH" priority. A value of 5 is 

1205 the normal or "MEDIUM" priority. A value in the range of 6 to 9 

1206 is "LOW" priority. 

1207 

1208 A CUA with a priority schema of "A1", "A2", "A3", "B1", "B2", ..., 

1209 "C3" is mapped into this property such that a property value of 1 

1210 specifies "A1", a property value of 2 specifies "A2", a property 

1211 value of 3 specifies "A3", and so forth up to a property value of 

1212 9 specifies "C3". 

1213 

1214 Other integer values are reserved for future use. 

1215 

1216 Within a "VEVENT" calendar component, this property specifies a 

1217 priority for the event. This property may be useful when more 

1218 than one event is scheduled for a given time period. 

1219 

1220 Within a "VTODO" calendar component, this property specified a 

1221 priority for the to-do. This property is useful in prioritizing 

1222 multiple action items for a given time period. 

1223""", 

1224) 

1225 

1226class_property = single_string_enum_property( 

1227 "CLASS", 

1228 CLASS, 

1229 CLASS.PUBLIC, 

1230 """CLASS specifies the class of the calendar component. 

1231 

1232Returns: 

1233 :class:`icalendar.enums.CLASS` 

1234 

1235Description: 

1236 An access classification is only one component of the 

1237 general security system within a calendar application. It 

1238 provides a method of capturing the scope of the access the 

1239 calendar owner intends for information within an individual 

1240 calendar entry. The access classification of an individual 

1241 iCalendar component is useful when measured along with the other 

1242 security components of a calendar system (e.g., calendar user 

1243 authentication, authorization, access rights, access role, etc.). 

1244 Hence, the semantics of the individual access classifications 

1245 cannot be completely defined by this memo alone. Additionally, 

1246 due to the "blind" nature of most exchange processes using this 

1247 memo, these access classifications cannot serve as an enforcement 

1248 statement for a system receiving an iCalendar object. Rather, 

1249 they provide a method for capturing the intention of the calendar 

1250 owner for the access to the calendar component. If not specified 

1251 in a component that allows this property, the default value is 

1252 PUBLIC. Applications MUST treat x-name and iana-token values they 

1253 don't recognize the same way as they would the PRIVATE value. 

1254""", 

1255) 

1256 

1257transparency_property = single_string_enum_property( 

1258 "TRANSP", 

1259 TRANSP, 

1260 TRANSP.OPAQUE, 

1261 """TRANSP defines whether or not an event is transparent to busy time searches. 

1262 

1263Returns: 

1264 :class:`icalendar.enums.TRANSP` 

1265 

1266Description: 

1267 Time Transparency is the characteristic of an event 

1268 that determines whether it appears to consume time on a calendar. 

1269 Events that consume actual time for the individual or resource 

1270 associated with the calendar SHOULD be recorded as OPAQUE, 

1271 allowing them to be detected by free/busy time searches. Other 

1272 events, which do not take up the individual's (or resource's) time 

1273 SHOULD be recorded as TRANSPARENT, making them invisible to free/ 

1274 busy time searches. 

1275""", 

1276) 

1277status_property = single_string_enum_property( 

1278 "STATUS", 

1279 STATUS, 

1280 "", 

1281 """STATUS defines the overall status or confirmation for the calendar component. 

1282 

1283Returns: 

1284 :class:`icalendar.enums.STATUS` 

1285 

1286The default value is ``""``. 

1287 

1288Description: 

1289 In a group-scheduled calendar component, the property 

1290 is used by the "Organizer" to provide a confirmation of the event 

1291 to the "Attendees". For example in a "VEVENT" calendar component, 

1292 the "Organizer" can indicate that a meeting is tentative, 

1293 confirmed, or cancelled. In a "VTODO" calendar component, the 

1294 "Organizer" can indicate that an action item needs action, is 

1295 completed, is in process or being worked on, or has been 

1296 cancelled. In a "VJOURNAL" calendar component, the "Organizer" 

1297 can indicate that a journal entry is draft, final, or has been 

1298 cancelled or removed. 

1299""", 

1300) 

1301 

1302url_property = single_string_property( 

1303 "URL", 

1304 """A Uniform Resource Locator (URL) associated with the iCalendar object. 

1305 

1306Description: 

1307 This property may be used in a calendar component to 

1308 convey a location where a more dynamic rendition of the calendar 

1309 information associated with the calendar component can be found. 

1310 This memo does not attempt to standardize the form of the URI, nor 

1311 the format of the resource pointed to by the property value. If 

1312 the URL property and Content-Location MIME header are both 

1313 specified, they MUST point to the same resource. 

1314""", 

1315) 

1316 

1317location_property = multi_language_text_property( 

1318 "LOCATION", 

1319 None, 

1320 """The intended venue for the activity defined by a calendar component. 

1321 

1322Property Parameters: 

1323 IANA, non-standard, alternate text 

1324 representation, and language property parameters can be specified 

1325 on this property. 

1326 

1327Conformance: 

1328 Since :rfc:`5545`, this property can be specified in "VEVENT" or "VTODO" 

1329 calendar component. 

1330 :rfc:`7953` adds this property to "VAVAILABILITY" and "VAVAILABLE". 

1331 

1332Description: 

1333 Specific venues such as conference or meeting rooms may 

1334 be explicitly specified using this property. An alternate 

1335 representation may be specified that is a URI that points to 

1336 directory information with more structured specification of the 

1337 location. For example, the alternate representation may specify 

1338 either an LDAP URL :rfc:`4516` pointing to an LDAP server entry or a 

1339 CID URL :rfc:`2392` pointing to a MIME body part containing a 

1340 Virtual-Information Card (vCard) :rfc:`2426` for the location. 

1341 

1342""", 

1343) 

1344 

1345contacts_property = multi_text_property( 

1346 "CONTACT", 

1347 """Contact information associated with the calendar component. 

1348 

1349Purpose: 

1350 This property is used to represent contact information or 

1351 alternately a reference to contact information associated with the 

1352 calendar component. 

1353 

1354Property Parameters: 

1355 IANA, non-standard, alternate text 

1356 representation, and language property parameters can be specified 

1357 on this property. 

1358 

1359Conformance: 

1360 In :rfc:`5545`, this property can be specified in a "VEVENT", "VTODO", 

1361 "VJOURNAL", or "VFREEBUSY" calendar component. 

1362 In :rfc:`7953`, this property can be specified in a "VAVAILABILITY" 

1363 amd "VAVAILABLE" calendar component. 

1364 

1365Description: 

1366 The property value consists of textual contact 

1367 information. An alternative representation for the property value 

1368 can also be specified that refers to a URI pointing to an 

1369 alternate form, such as a vCard :rfc:`2426`, for the contact 

1370 information. 

1371 

1372Example: 

1373 The following is an example of this property referencing 

1374 textual contact information: 

1375 

1376 .. code-block:: text 

1377 

1378 CONTACT:Jim Dolittle\\, ABC Industries\\, +1-919-555-1234 

1379 

1380 The following is an example of this property with an alternate 

1381 representation of an LDAP URI to a directory entry containing the 

1382 contact information: 

1383 

1384 .. code-block:: text 

1385 

1386 CONTACT;ALTREP="ldap://example.com:6666/o=ABC%20Industries\\, 

1387 c=US???(cn=Jim%20Dolittle)":Jim Dolittle\\, ABC Industries\\, 

1388 +1-919-555-1234 

1389 

1390 The following is an example of this property with an alternate 

1391 representation of a MIME body part containing the contact 

1392 information, such as a vCard :rfc:`2426` embedded in a text/ 

1393 directory media type :rfc:`2425`: 

1394 

1395 .. code-block:: text 

1396 

1397 CONTACT;ALTREP="CID:part3.msg970930T083000SILVER@example.com": 

1398 Jim Dolittle\\, ABC Industries\\, +1-919-555-1234 

1399 

1400 The following is an example of this property referencing a network 

1401 resource, such as a vCard :rfc:`2426` object containing the contact 

1402 information: 

1403 

1404 .. code-block:: text 

1405 

1406 CONTACT;ALTREP="http://example.com/pdi/jdoe.vcf":Jim 

1407 Dolittle\\, ABC Industries\\, +1-919-555-1234 

1408""", 

1409) 

1410 

1411 

1412def timezone_datetime_property(name: str, docs: str): 

1413 """Create a property to access the values with a proper timezone.""" 

1414 

1415 return single_utc_property(name, docs) 

1416 

1417 

1418rfc_7953_dtstart_property = timezone_datetime_property( 

1419 "DTSTART", 

1420 """Start of the component. 

1421 

1422 This is almost the same as :attr:`Event.DTSTART` with one exception: 

1423 The values MUST have a timezone and DATE is not allowed. 

1424 

1425 Description: 

1426 :rfc:`7953`: If specified, the "DTSTART" and "DTEND" properties in 

1427 "VAVAILABILITY" components and "AVAILABLE" subcomponents MUST be 

1428 "DATE-TIME" values specified as either the date with UTC time or 

1429 the date with local time and a time zone reference. 

1430 

1431 """, 

1432) 

1433 

1434rfc_7953_dtend_property = timezone_datetime_property( 

1435 "DTEND", 

1436 """Start of the component. 

1437 

1438 This is almost the same as :attr:`Event.DTEND` with one exception: 

1439 The values MUST have a timezone and DATE is not allowed. 

1440 

1441 Description: 

1442 :rfc:`7953`: If specified, the "DTSTART" and "DTEND" properties in 

1443 "VAVAILABILITY" components and "AVAILABLE" subcomponents MUST be 

1444 "DATE-TIME" values specified as either the date with UTC time or 

1445 the date with local time and a time zone reference. 

1446 """, 

1447) 

1448 

1449 

1450@property 

1451def rfc_7953_duration_property(self) -> Optional[timedelta]: 

1452 """Compute the duration of this component. 

1453 

1454 If there is no :attr:`DTEND` or :attr:`DURATION` set, this is None. 

1455 Otherwise, the duration is calculated from :attr:`DTSTART` and 

1456 :attr:`DTEND`/:attr:`DURATION`. 

1457 

1458 This is in accordance with :rfc:`7953`: 

1459 If "DTEND" or "DURATION" are not present, then the end time is unbounded. 

1460 """ 

1461 duration = self.DURATION 

1462 if duration: 

1463 return duration 

1464 end = self.DTEND 

1465 if end is None: 

1466 return None 

1467 start = self.DTSTART 

1468 if start is None: 

1469 raise IncompleteComponent("Cannot compute duration without start.") 

1470 return end - start 

1471 

1472 

1473@property 

1474def rfc_7953_end_property(self) -> Optional[timedelta]: 

1475 """Compute the duration of this component. 

1476 

1477 If there is no :attr:`DTEND` or :attr:`DURATION` set, this is None. 

1478 Otherwise, the duration is calculated from :attr:`DTSTART` and 

1479 :attr:`DTEND`/:attr:`DURATION`. 

1480 

1481 This is in accordance with :rfc:`7953`: 

1482 If "DTEND" or "DURATION" are not present, then the end time is unbounded. 

1483 """ 

1484 duration = self.DURATION 

1485 if duration: 

1486 start = self.DTSTART 

1487 if start is None: 

1488 raise IncompleteComponent("Cannot compute end without start.") 

1489 return start + duration 

1490 end = self.DTEND 

1491 if end is None: 

1492 return None 

1493 return end 

1494 

1495 

1496@rfc_7953_end_property.setter 

1497def rfc_7953_end_property(self, value: datetime): 

1498 self.DTEND = value 

1499 

1500 

1501@rfc_7953_end_property.deleter 

1502def rfc_7953_end_property(self): 

1503 del self.DTEND 

1504 

1505 

1506__all__ = [ 

1507 "attendees_property", 

1508 "busy_type_property", 

1509 "categories_property", 

1510 "class_property", 

1511 "color_property", 

1512 "comments_property", 

1513 "contacts_property", 

1514 "create_single_property", 

1515 "description_property", 

1516 "descriptions_property", 

1517 "duration_property", 

1518 "exdates_property", 

1519 "location_property", 

1520 "multi_language_text_property", 

1521 "organizer_property", 

1522 "priority_property", 

1523 "property_del_duration", 

1524 "property_doc_duration_template", 

1525 "property_get_duration", 

1526 "property_set_duration", 

1527 "rdates_property", 

1528 "rfc_7953_dtend_property", 

1529 "rfc_7953_dtstart_property", 

1530 "rfc_7953_duration_property", 

1531 "rfc_7953_end_property", 

1532 "rrules_property", 

1533 "sequence_property", 

1534 "single_int_property", 

1535 "single_utc_property", 

1536 "status_property", 

1537 "summary_property", 

1538 "transparency_property", 

1539 "uid_property", 

1540 "url_property", 

1541]