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

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

723 statements  

1"""This module contains the parser/generators (or coders/encoders if you 

2prefer) for the classes/datatypes that are used in iCalendar: 

3 

4########################################################################### 

5 

6# This module defines these property value data types and property parameters 

7 

84.2 Defined property parameters are: 

9 

10.. code-block:: text 

11 

12 ALTREP, CN, CUTYPE, DELEGATED-FROM, DELEGATED-TO, DIR, ENCODING, FMTTYPE, 

13 FBTYPE, LANGUAGE, MEMBER, PARTSTAT, RANGE, RELATED, RELTYPE, ROLE, RSVP, 

14 SENT-BY, TZID, VALUE 

15 

164.3 Defined value data types are: 

17 

18.. code-block:: text 

19 

20 BINARY, BOOLEAN, CAL-ADDRESS, DATE, DATE-TIME, DURATION, FLOAT, INTEGER, 

21 PERIOD, RECUR, TEXT, TIME, URI, UTC-OFFSET 

22 

23########################################################################### 

24 

25iCalendar properties have values. The values are strongly typed. This module 

26defines these types, calling val.to_ical() on them will render them as defined 

27in rfc5545. 

28 

29If you pass any of these classes a Python primitive, you will have an object 

30that can render itself as iCalendar formatted date. 

31 

32Property Value Data Types start with a 'v'. they all have an to_ical() and 

33from_ical() method. The to_ical() method generates a text string in the 

34iCalendar format. The from_ical() method can parse this format and return a 

35primitive Python datatype. So it should always be true that: 

36 

37.. code-block:: python 

38 

39 x == vDataType.from_ical(VDataType(x).to_ical()) 

40 

41These types are mainly used for parsing and file generation. But you can set 

42them directly. 

43""" 

44 

45from __future__ import annotations 

46 

47import base64 

48import binascii 

49import re 

50from datetime import date, datetime, time, timedelta 

51from typing import Any, Optional, Union 

52 

53from icalendar.caselessdict import CaselessDict 

54from icalendar.enums import VALUE, Enum 

55from icalendar.parser import Parameters, escape_char, unescape_char 

56from icalendar.parser_tools import ( 

57 DEFAULT_ENCODING, 

58 ICAL_TYPE, 

59 SEQUENCE_TYPES, 

60 from_unicode, 

61 to_unicode, 

62) 

63from icalendar.tools import to_datetime 

64 

65from .timezone import tzid_from_dt, tzid_from_tzinfo, tzp 

66 

67DURATION_REGEX = re.compile( 

68 r"([-+]?)P(?:(\d+)W)?(?:(\d+)D)?" r"(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$" 

69) 

70 

71WEEKDAY_RULE = re.compile( 

72 r"(?P<signal>[+-]?)(?P<relative>[\d]{0,2})" r"(?P<weekday>[\w]{2})$" 

73) 

74 

75 

76class vBinary: 

77 """Binary property values are base 64 encoded.""" 

78 

79 params: Parameters 

80 

81 def __init__(self, obj): 

82 self.obj = to_unicode(obj) 

83 self.params = Parameters(encoding="BASE64", value="BINARY") 

84 

85 def __repr__(self): 

86 return f"vBinary({self.to_ical()})" 

87 

88 def to_ical(self): 

89 return binascii.b2a_base64(self.obj.encode("utf-8"))[:-1] 

90 

91 @staticmethod 

92 def from_ical(ical): 

93 try: 

94 return base64.b64decode(ical) 

95 except ValueError as e: 

96 raise ValueError("Not valid base 64 encoding.") from e 

97 

98 def __eq__(self, other): 

99 """self == other""" 

100 return isinstance(other, vBinary) and self.obj == other.obj 

101 

102 

103class vBoolean(int): 

104 """Boolean 

105 

106 Value Name: BOOLEAN 

107 

108 Purpose: This value type is used to identify properties that contain 

109 either a "TRUE" or "FALSE" Boolean value. 

110 

111 Format Definition: This value type is defined by the following 

112 notation: 

113 

114 .. code-block:: text 

115 

116 boolean = "TRUE" / "FALSE" 

117 

118 Description: These values are case-insensitive text. No additional 

119 content value encoding is defined for this value type. 

120 

121 Example: The following is an example of a hypothetical property that 

122 has a BOOLEAN value type: 

123 

124 .. code-block:: python 

125 

126 TRUE 

127 

128 .. code-block:: pycon 

129 

130 >>> from icalendar.prop import vBoolean 

131 >>> boolean = vBoolean.from_ical('TRUE') 

132 >>> boolean 

133 True 

134 >>> boolean = vBoolean.from_ical('FALSE') 

135 >>> boolean 

136 False 

137 >>> boolean = vBoolean.from_ical('True') 

138 >>> boolean 

139 True 

140 """ 

141 

142 params: Parameters 

143 

144 BOOL_MAP = CaselessDict({"true": True, "false": False}) 

145 

146 def __new__(cls, *args, params: Optional[dict[str, Any]] = None, **kwargs): 

147 if params is None: 

148 params = {} 

149 self = super().__new__(cls, *args, **kwargs) 

150 self.params = Parameters(params) 

151 return self 

152 

153 def to_ical(self): 

154 return b"TRUE" if self else b"FALSE" 

155 

156 @classmethod 

157 def from_ical(cls, ical): 

158 try: 

159 return cls.BOOL_MAP[ical] 

160 except Exception as e: 

161 raise ValueError(f"Expected 'TRUE' or 'FALSE'. Got {ical}") from e 

162 

163 

164class vText(str): 

165 """Simple text.""" 

166 

167 params: Parameters 

168 __slots__ = ("encoding", "params") 

169 

170 def __new__( 

171 cls, 

172 value, 

173 encoding=DEFAULT_ENCODING, 

174 /, 

175 params: Optional[dict[str, Any]] = None, 

176 ): 

177 if params is None: 

178 params = {} 

179 value = to_unicode(value, encoding=encoding) 

180 self = super().__new__(cls, value) 

181 self.encoding = encoding 

182 self.params = Parameters(params) 

183 return self 

184 

185 def __repr__(self) -> str: 

186 return f"vText({self.to_ical()!r})" 

187 

188 def to_ical(self) -> bytes: 

189 return escape_char(self).encode(self.encoding) 

190 

191 @classmethod 

192 def from_ical(cls, ical: ICAL_TYPE): 

193 ical_unesc = unescape_char(ical) 

194 return cls(ical_unesc) 

195 

196 from icalendar.param import ALTREP, LANGUAGE, RELTYPE 

197 

198 

199class vCalAddress(str): 

200 """Calendar User Address 

201 

202 Value Name: 

203 CAL-ADDRESS 

204 

205 Purpose: 

206 This value type is used to identify properties that contain a 

207 calendar user address. 

208 

209 Description: 

210 The value is a URI as defined by [RFC3986] or any other 

211 IANA-registered form for a URI. When used to address an Internet 

212 email transport address for a calendar user, the value MUST be a 

213 mailto URI, as defined by [RFC2368]. 

214 

215 Example: 

216 ``mailto:`` is in front of the address. 

217 

218 .. code-block:: text 

219 

220 mailto:jane_doe@example.com 

221 

222 Parsing: 

223 

224 .. code-block:: pycon 

225 

226 >>> from icalendar import vCalAddress 

227 >>> cal_address = vCalAddress.from_ical('mailto:jane_doe@example.com') 

228 >>> cal_address 

229 vCalAddress('mailto:jane_doe@example.com') 

230 

231 Encoding: 

232 

233 .. code-block:: pycon 

234 

235 >>> from icalendar import vCalAddress, Event 

236 >>> event = Event() 

237 >>> jane = vCalAddress("mailto:jane_doe@example.com") 

238 >>> jane.name = "Jane" 

239 >>> event["organizer"] = jane 

240 >>> print(event.to_ical().decode().replace('\\r\\n', '\\n').strip()) 

241 BEGIN:VEVENT 

242 ORGANIZER;CN=Jane:mailto:jane_doe@example.com 

243 END:VEVENT 

244 """ 

245 

246 params: Parameters 

247 __slots__ = ("params",) 

248 

249 def __new__( 

250 cls, 

251 value, 

252 encoding=DEFAULT_ENCODING, 

253 /, 

254 params: Optional[dict[str, Any]] = None, 

255 ): 

256 if params is None: 

257 params = {} 

258 value = to_unicode(value, encoding=encoding) 

259 self = super().__new__(cls, value) 

260 self.params = Parameters(params) 

261 return self 

262 

263 def __repr__(self): 

264 return f"vCalAddress('{self}')" 

265 

266 def to_ical(self): 

267 return self.encode(DEFAULT_ENCODING) 

268 

269 @classmethod 

270 def from_ical(cls, ical): 

271 return cls(ical) 

272 

273 @property 

274 def email(self) -> str: 

275 """The email address without mailto: at the start.""" 

276 if self.lower().startswith("mailto:"): 

277 return self[7:] 

278 return str(self) 

279 

280 from icalendar.param import ( 

281 CN, 

282 CUTYPE, 

283 DELEGATED_FROM, 

284 DELEGATED_TO, 

285 DIR, 

286 LANGUAGE, 

287 PARTSTAT, 

288 ROLE, 

289 RSVP, 

290 SENT_BY, 

291 ) 

292 

293 name = CN 

294 

295 @staticmethod 

296 def _get_email(email: str) -> str: 

297 """Extract email and add mailto: prefix if needed. 

298 

299 Handles case-insensitive mailto: prefix checking. 

300 

301 Args: 

302 email: Email string that may or may not have mailto: prefix 

303 

304 Returns: 

305 Email string with mailto: prefix 

306 """ 

307 if not email.lower().startswith("mailto:"): 

308 return f"mailto:{email}" 

309 return email 

310 

311 @classmethod 

312 def new( 

313 cls, 

314 email: str, 

315 /, 

316 cn: str | None = None, 

317 cutype: str | None = None, 

318 delegated_from: str | None = None, 

319 delegated_to: str | None = None, 

320 directory: str | None = None, 

321 language: str | None = None, 

322 partstat: str | None = None, 

323 role: str | None = None, 

324 rsvp: bool | None = None, 

325 sent_by: str | None = None, 

326 ): 

327 """Create a new vCalAddress with RFC 5545 parameters. 

328 

329 Creates a vCalAddress instance with automatic mailto: prefix handling 

330 and support for all standard RFC 5545 parameters. 

331 

332 Args: 

333 email: The email address (mailto: prefix added automatically if missing) 

334 cn: Common Name parameter 

335 cutype: Calendar user type (INDIVIDUAL, GROUP, RESOURCE, ROOM) 

336 delegated_from: Email of the calendar user that delegated 

337 delegated_to: Email of the calendar user that was delegated to 

338 directory: Reference to directory information 

339 language: Language for text values 

340 partstat: Participation status (NEEDS-ACTION, ACCEPTED, DECLINED, etc.) 

341 role: Role (REQ-PARTICIPANT, OPT-PARTICIPANT, NON-PARTICIPANT, CHAIR) 

342 rsvp: Whether RSVP is requested 

343 sent_by: Email of the calendar user acting on behalf of this user 

344 

345 Returns: 

346 vCalAddress: A new calendar address with specified parameters 

347 

348 Raises: 

349 TypeError: If email is not a string 

350 

351 Examples: 

352 Basic usage: 

353 

354 >>> from icalendar.prop import vCalAddress 

355 >>> addr = vCalAddress.new("test@test.com") 

356 >>> str(addr) 

357 'mailto:test@test.com' 

358 

359 With parameters: 

360 

361 >>> addr = vCalAddress.new("test@test.com", cn="Test User", role="CHAIR") 

362 >>> addr.params["CN"] 

363 'Test User' 

364 >>> addr.params["ROLE"] 

365 'CHAIR' 

366 """ 

367 if not isinstance(email, str): 

368 raise TypeError(f"Email must be a string, not {type(email).__name__}") 

369 

370 # Handle mailto: prefix (case-insensitive) 

371 email_with_prefix = cls._get_email(email) 

372 

373 # Create the address 

374 addr = cls(email_with_prefix) 

375 

376 # Set parameters if provided 

377 if cn is not None: 

378 addr.params["CN"] = cn 

379 if cutype is not None: 

380 addr.params["CUTYPE"] = cutype 

381 if delegated_from is not None: 

382 addr.params["DELEGATED-FROM"] = cls._get_email(delegated_from) 

383 if delegated_to is not None: 

384 addr.params["DELEGATED-TO"] = cls._get_email(delegated_to) 

385 if directory is not None: 

386 addr.params["DIR"] = directory 

387 if language is not None: 

388 addr.params["LANGUAGE"] = language 

389 if partstat is not None: 

390 addr.params["PARTSTAT"] = partstat 

391 if role is not None: 

392 addr.params["ROLE"] = role 

393 if rsvp is not None: 

394 addr.params["RSVP"] = "TRUE" if rsvp else "FALSE" 

395 if sent_by is not None: 

396 addr.params["SENT-BY"] = cls._get_email(sent_by) 

397 

398 return addr 

399 

400 

401class vFloat(float): 

402 """Float 

403 

404 Value Name: 

405 FLOAT 

406 

407 Purpose: 

408 This value type is used to identify properties that contain 

409 a real-number value. 

410 

411 Format Definition: 

412 This value type is defined by the following notation: 

413 

414 .. code-block:: text 

415 

416 float = (["+"] / "-") 1*DIGIT ["." 1*DIGIT] 

417 

418 Description: 

419 If the property permits, multiple "float" values are 

420 specified by a COMMA-separated list of values. 

421 

422 Example: 

423 

424 .. code-block:: text 

425 

426 1000000.0000001 

427 1.333 

428 -3.14 

429 

430 .. code-block:: pycon 

431 

432 >>> from icalendar.prop import vFloat 

433 >>> float = vFloat.from_ical('1000000.0000001') 

434 >>> float 

435 1000000.0000001 

436 >>> float = vFloat.from_ical('1.333') 

437 >>> float 

438 1.333 

439 >>> float = vFloat.from_ical('+1.333') 

440 >>> float 

441 1.333 

442 >>> float = vFloat.from_ical('-3.14') 

443 >>> float 

444 -3.14 

445 """ 

446 

447 params: Parameters 

448 

449 def __new__(cls, *args, params: Optional[dict[str, Any]] = None, **kwargs): 

450 if params is None: 

451 params = {} 

452 self = super().__new__(cls, *args, **kwargs) 

453 self.params = Parameters(params) 

454 return self 

455 

456 def to_ical(self): 

457 return str(self).encode("utf-8") 

458 

459 @classmethod 

460 def from_ical(cls, ical): 

461 try: 

462 return cls(ical) 

463 except Exception as e: 

464 raise ValueError(f"Expected float value, got: {ical}") from e 

465 

466 

467class vInt(int): 

468 """Integer 

469 

470 Value Name: 

471 INTEGER 

472 

473 Purpose: 

474 This value type is used to identify properties that contain a 

475 signed integer value. 

476 

477 Format Definition: 

478 This value type is defined by the following notation: 

479 

480 .. code-block:: text 

481 

482 integer = (["+"] / "-") 1*DIGIT 

483 

484 Description: 

485 If the property permits, multiple "integer" values are 

486 specified by a COMMA-separated list of values. The valid range 

487 for "integer" is -2147483648 to 2147483647. If the sign is not 

488 specified, then the value is assumed to be positive. 

489 

490 Example: 

491 

492 .. code-block:: text 

493 

494 1234567890 

495 -1234567890 

496 +1234567890 

497 432109876 

498 

499 .. code-block:: pycon 

500 

501 >>> from icalendar.prop import vInt 

502 >>> integer = vInt.from_ical('1234567890') 

503 >>> integer 

504 1234567890 

505 >>> integer = vInt.from_ical('-1234567890') 

506 >>> integer 

507 -1234567890 

508 >>> integer = vInt.from_ical('+1234567890') 

509 >>> integer 

510 1234567890 

511 >>> integer = vInt.from_ical('432109876') 

512 >>> integer 

513 432109876 

514 """ 

515 

516 params: Parameters 

517 

518 def __new__(cls, *args, params: Optional[dict[str, Any]] = None, **kwargs): 

519 if params is None: 

520 params = {} 

521 self = super().__new__(cls, *args, **kwargs) 

522 self.params = Parameters(params) 

523 return self 

524 

525 def to_ical(self) -> bytes: 

526 return str(self).encode("utf-8") 

527 

528 @classmethod 

529 def from_ical(cls, ical: ICAL_TYPE): 

530 try: 

531 return cls(ical) 

532 except Exception as e: 

533 raise ValueError(f"Expected int, got: {ical}") from e 

534 

535 

536class vDDDLists: 

537 """A list of vDDDTypes values.""" 

538 

539 params: Parameters 

540 dts: list 

541 

542 def __init__(self, dt_list): 

543 if not hasattr(dt_list, "__iter__"): 

544 dt_list = [dt_list] 

545 vddd = [] 

546 tzid = None 

547 for dt_l in dt_list: 

548 dt = vDDDTypes(dt_l) 

549 vddd.append(dt) 

550 if "TZID" in dt.params: 

551 tzid = dt.params["TZID"] 

552 

553 params = {} 

554 if tzid: 

555 # NOTE: no support for multiple timezones here! 

556 params["TZID"] = tzid 

557 self.params = Parameters(params) 

558 self.dts = vddd 

559 

560 def to_ical(self): 

561 dts_ical = (from_unicode(dt.to_ical()) for dt in self.dts) 

562 return b",".join(dts_ical) 

563 

564 @staticmethod 

565 def from_ical(ical, timezone=None): 

566 out = [] 

567 ical_dates = ical.split(",") 

568 for ical_dt in ical_dates: 

569 out.append(vDDDTypes.from_ical(ical_dt, timezone=timezone)) 

570 return out 

571 

572 def __eq__(self, other): 

573 if isinstance(other, vDDDLists): 

574 return self.dts == other.dts 

575 if isinstance(other, (TimeBase, date)): 

576 return self.dts == [other] 

577 return False 

578 

579 def __repr__(self): 

580 """String representation.""" 

581 return f"{self.__class__.__name__}({self.dts})" 

582 

583 

584class vCategory: 

585 params: Parameters 

586 

587 def __init__( 

588 self, c_list: list[str] | str, /, params: Optional[dict[str, Any]] = None 

589 ): 

590 if params is None: 

591 params = {} 

592 if not hasattr(c_list, "__iter__") or isinstance(c_list, str): 

593 c_list = [c_list] 

594 self.cats: list[vText | str] = [vText(c) for c in c_list] 

595 self.params = Parameters(params) 

596 

597 def __iter__(self): 

598 return iter(vCategory.from_ical(self.to_ical())) 

599 

600 def to_ical(self): 

601 return b",".join( 

602 [ 

603 c.to_ical() if hasattr(c, "to_ical") else vText(c).to_ical() 

604 for c in self.cats 

605 ] 

606 ) 

607 

608 @staticmethod 

609 def from_ical(ical): 

610 ical = to_unicode(ical) 

611 return unescape_char(ical).split(",") 

612 

613 def __eq__(self, other): 

614 """self == other""" 

615 return isinstance(other, vCategory) and self.cats == other.cats 

616 

617 def __repr__(self): 

618 """String representation.""" 

619 return f"{self.__class__.__name__}({self.cats}, params={self.params})" 

620 

621 

622class TimeBase: 

623 """Make classes with a datetime/date comparable.""" 

624 

625 params: Parameters 

626 ignore_for_equality = {"TZID", "VALUE"} 

627 

628 def __eq__(self, other): 

629 """self == other""" 

630 if isinstance(other, date): 

631 return self.dt == other 

632 if isinstance(other, TimeBase): 

633 default = object() 

634 for key in ( 

635 set(self.params) | set(other.params) 

636 ) - self.ignore_for_equality: 

637 if key[:2].lower() != "x-" and self.params.get( 

638 key, default 

639 ) != other.params.get(key, default): 

640 return False 

641 return self.dt == other.dt 

642 if isinstance(other, vDDDLists): 

643 return other == self 

644 return False 

645 

646 def __hash__(self): 

647 return hash(self.dt) 

648 

649 from icalendar.param import RANGE, RELATED, TZID 

650 

651 def __repr__(self): 

652 """String representation.""" 

653 return f"{self.__class__.__name__}({self.dt}, {self.params})" 

654 

655 

656class vDDDTypes(TimeBase): 

657 """A combined Datetime, Date or Duration parser/generator. Their format 

658 cannot be confused, and often values can be of either types. 

659 So this is practical. 

660 """ 

661 

662 params: Parameters 

663 

664 def __init__(self, dt): 

665 if not isinstance(dt, (datetime, date, timedelta, time, tuple)): 

666 raise TypeError( 

667 "You must use datetime, date, timedelta, time or tuple (for periods)" 

668 ) 

669 if isinstance(dt, (datetime, timedelta)): 

670 self.params = Parameters() 

671 elif isinstance(dt, date): 

672 self.params = Parameters({"value": "DATE"}) 

673 elif isinstance(dt, time): 

674 self.params = Parameters({"value": "TIME"}) 

675 else: # isinstance(dt, tuple) 

676 self.params = Parameters({"value": "PERIOD"}) 

677 

678 tzid = tzid_from_dt(dt) if isinstance(dt, (datetime, time)) else None 

679 if tzid is not None and tzid != "UTC": 

680 self.params.update({"TZID": tzid}) 

681 

682 self.dt = dt 

683 

684 def to_ical(self): 

685 dt = self.dt 

686 if isinstance(dt, datetime): 

687 return vDatetime(dt).to_ical() 

688 if isinstance(dt, date): 

689 return vDate(dt).to_ical() 

690 if isinstance(dt, timedelta): 

691 return vDuration(dt).to_ical() 

692 if isinstance(dt, time): 

693 return vTime(dt).to_ical() 

694 if isinstance(dt, tuple) and len(dt) == 2: 

695 return vPeriod(dt).to_ical() 

696 raise ValueError(f"Unknown date type: {type(dt)}") 

697 

698 @classmethod 

699 def from_ical(cls, ical, timezone=None): 

700 if isinstance(ical, cls): 

701 return ical.dt 

702 u = ical.upper() 

703 if u.startswith(("P", "-P", "+P")): 

704 return vDuration.from_ical(ical) 

705 if "/" in u: 

706 return vPeriod.from_ical(ical, timezone=timezone) 

707 

708 if len(ical) in (15, 16): 

709 return vDatetime.from_ical(ical, timezone=timezone) 

710 if len(ical) == 8: 

711 if timezone: 

712 tzinfo = tzp.timezone(timezone) 

713 if tzinfo is not None: 

714 return to_datetime(vDate.from_ical(ical)).replace(tzinfo=tzinfo) 

715 return vDate.from_ical(ical) 

716 if len(ical) in (6, 7): 

717 return vTime.from_ical(ical) 

718 raise ValueError(f"Expected datetime, date, or time. Got: '{ical}'") 

719 

720 

721class vDate(TimeBase): 

722 """Date 

723 

724 Value Name: 

725 DATE 

726 

727 Purpose: 

728 This value type is used to identify values that contain a 

729 calendar date. 

730 

731 Format Definition: 

732 This value type is defined by the following notation: 

733 

734 .. code-block:: text 

735 

736 date = date-value 

737 

738 date-value = date-fullyear date-month date-mday 

739 date-fullyear = 4DIGIT 

740 date-month = 2DIGIT ;01-12 

741 date-mday = 2DIGIT ;01-28, 01-29, 01-30, 01-31 

742 ;based on month/year 

743 

744 Description: 

745 If the property permits, multiple "date" values are 

746 specified as a COMMA-separated list of values. The format for the 

747 value type is based on the [ISO.8601.2004] complete 

748 representation, basic format for a calendar date. The textual 

749 format specifies a four-digit year, two-digit month, and two-digit 

750 day of the month. There are no separator characters between the 

751 year, month, and day component text. 

752 

753 Example: 

754 The following represents July 14, 1997: 

755 

756 .. code-block:: text 

757 

758 19970714 

759 

760 .. code-block:: pycon 

761 

762 >>> from icalendar.prop import vDate 

763 >>> date = vDate.from_ical('19970714') 

764 >>> date.year 

765 1997 

766 >>> date.month 

767 7 

768 >>> date.day 

769 14 

770 """ 

771 

772 params: Parameters 

773 

774 def __init__(self, dt): 

775 if not isinstance(dt, date): 

776 raise TypeError("Value MUST be a date instance") 

777 self.dt = dt 

778 self.params = Parameters({"value": "DATE"}) 

779 

780 def to_ical(self): 

781 s = f"{self.dt.year:04}{self.dt.month:02}{self.dt.day:02}" 

782 return s.encode("utf-8") 

783 

784 @staticmethod 

785 def from_ical(ical): 

786 try: 

787 timetuple = ( 

788 int(ical[:4]), # year 

789 int(ical[4:6]), # month 

790 int(ical[6:8]), # day 

791 ) 

792 return date(*timetuple) 

793 except Exception as e: 

794 raise ValueError(f"Wrong date format {ical}") from e 

795 

796 

797class vDatetime(TimeBase): 

798 """Render and generates icalendar datetime format. 

799 

800 vDatetime is timezone aware and uses a timezone library. 

801 When a vDatetime object is created from an 

802 ical string, you can pass a valid timezone identifier. When a 

803 vDatetime object is created from a python datetime object, it uses the 

804 tzinfo component, if present. Otherwise a timezone-naive object is 

805 created. Be aware that there are certain limitations with timezone naive 

806 DATE-TIME components in the icalendar standard. 

807 """ 

808 

809 params: Parameters 

810 

811 def __init__(self, dt, /, params: Optional[dict[str, Any]] = None): 

812 if params is None: 

813 params = {} 

814 self.dt = dt 

815 self.params = Parameters(params) 

816 

817 def to_ical(self): 

818 dt = self.dt 

819 tzid = tzid_from_dt(dt) 

820 

821 s = ( 

822 f"{dt.year:04}{dt.month:02}{dt.day:02}" 

823 f"T{dt.hour:02}{dt.minute:02}{dt.second:02}" 

824 ) 

825 if tzid == "UTC": 

826 s += "Z" 

827 elif tzid: 

828 self.params.update({"TZID": tzid}) 

829 return s.encode("utf-8") 

830 

831 @staticmethod 

832 def from_ical(ical, timezone=None): 

833 """Create a datetime from the RFC string. 

834 

835 Format: 

836 

837 .. code-block:: text 

838 

839 YYYYMMDDTHHMMSS 

840 

841 .. code-block:: pycon 

842 

843 >>> from icalendar import vDatetime 

844 >>> vDatetime.from_ical("20210302T101500") 

845 datetime.datetime(2021, 3, 2, 10, 15) 

846 

847 >>> vDatetime.from_ical("20210302T101500", "America/New_York") 

848 datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='America/New_York')) 

849 

850 >>> from zoneinfo import ZoneInfo 

851 >>> timezone = ZoneInfo("Europe/Berlin") 

852 >>> vDatetime.from_ical("20210302T101500", timezone) 

853 datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='Europe/Berlin')) 

854 """ # noqa: E501 

855 tzinfo = None 

856 if isinstance(timezone, str): 

857 tzinfo = tzp.timezone(timezone) 

858 elif timezone is not None: 

859 tzinfo = timezone 

860 

861 try: 

862 timetuple = ( 

863 int(ical[:4]), # year 

864 int(ical[4:6]), # month 

865 int(ical[6:8]), # day 

866 int(ical[9:11]), # hour 

867 int(ical[11:13]), # minute 

868 int(ical[13:15]), # second 

869 ) 

870 if tzinfo: 

871 return tzp.localize(datetime(*timetuple), tzinfo) # noqa: DTZ001 

872 if not ical[15:]: 

873 return datetime(*timetuple) # noqa: DTZ001 

874 if ical[15:16] == "Z": 

875 return tzp.localize_utc(datetime(*timetuple)) # noqa: DTZ001 

876 except Exception as e: 

877 raise ValueError(f"Wrong datetime format: {ical}") from e 

878 raise ValueError(f"Wrong datetime format: {ical}") 

879 

880 

881class vDuration(TimeBase): 

882 """Duration 

883 

884 Value Name: 

885 DURATION 

886 

887 Purpose: 

888 This value type is used to identify properties that contain 

889 a duration of time. 

890 

891 Format Definition: 

892 This value type is defined by the following notation: 

893 

894 .. code-block:: text 

895 

896 dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week) 

897 

898 dur-date = dur-day [dur-time] 

899 dur-time = "T" (dur-hour / dur-minute / dur-second) 

900 dur-week = 1*DIGIT "W" 

901 dur-hour = 1*DIGIT "H" [dur-minute] 

902 dur-minute = 1*DIGIT "M" [dur-second] 

903 dur-second = 1*DIGIT "S" 

904 dur-day = 1*DIGIT "D" 

905 

906 Description: 

907 If the property permits, multiple "duration" values are 

908 specified by a COMMA-separated list of values. The format is 

909 based on the [ISO.8601.2004] complete representation basic format 

910 with designators for the duration of time. The format can 

911 represent nominal durations (weeks and days) and accurate 

912 durations (hours, minutes, and seconds). Note that unlike 

913 [ISO.8601.2004], this value type doesn't support the "Y" and "M" 

914 designators to specify durations in terms of years and months. 

915 The duration of a week or a day depends on its position in the 

916 calendar. In the case of discontinuities in the time scale, such 

917 as the change from standard time to daylight time and back, the 

918 computation of the exact duration requires the subtraction or 

919 addition of the change of duration of the discontinuity. Leap 

920 seconds MUST NOT be considered when computing an exact duration. 

921 When computing an exact duration, the greatest order time 

922 components MUST be added first, that is, the number of days MUST 

923 be added first, followed by the number of hours, number of 

924 minutes, and number of seconds. 

925 

926 Example: 

927 A duration of 15 days, 5 hours, and 20 seconds would be: 

928 

929 .. code-block:: text 

930 

931 P15DT5H0M20S 

932 

933 A duration of 7 weeks would be: 

934 

935 .. code-block:: text 

936 

937 P7W 

938 

939 .. code-block:: pycon 

940 

941 >>> from icalendar.prop import vDuration 

942 >>> duration = vDuration.from_ical('P15DT5H0M20S') 

943 >>> duration 

944 datetime.timedelta(days=15, seconds=18020) 

945 >>> duration = vDuration.from_ical('P7W') 

946 >>> duration 

947 datetime.timedelta(days=49) 

948 """ 

949 

950 params: Parameters 

951 

952 def __init__(self, td, /, params: Optional[dict[str, Any]] = None): 

953 if params is None: 

954 params = {} 

955 if not isinstance(td, timedelta): 

956 raise TypeError("Value MUST be a timedelta instance") 

957 self.td = td 

958 self.params = Parameters(params) 

959 

960 def to_ical(self): 

961 sign = "" 

962 td = self.td 

963 if td.days < 0: 

964 sign = "-" 

965 td = -td 

966 timepart = "" 

967 if td.seconds: 

968 timepart = "T" 

969 hours = td.seconds // 3600 

970 minutes = td.seconds % 3600 // 60 

971 seconds = td.seconds % 60 

972 if hours: 

973 timepart += f"{hours}H" 

974 if minutes or (hours and seconds): 

975 timepart += f"{minutes}M" 

976 if seconds: 

977 timepart += f"{seconds}S" 

978 if td.days == 0 and timepart: 

979 return str(sign).encode("utf-8") + b"P" + str(timepart).encode("utf-8") 

980 return ( 

981 str(sign).encode("utf-8") 

982 + b"P" 

983 + str(abs(td.days)).encode("utf-8") 

984 + b"D" 

985 + str(timepart).encode("utf-8") 

986 ) 

987 

988 @staticmethod 

989 def from_ical(ical): 

990 match = DURATION_REGEX.match(ical) 

991 if not match: 

992 raise ValueError(f"Invalid iCalendar duration: {ical}") 

993 

994 sign, weeks, days, hours, minutes, seconds = match.groups() 

995 value = timedelta( 

996 weeks=int(weeks or 0), 

997 days=int(days or 0), 

998 hours=int(hours or 0), 

999 minutes=int(minutes or 0), 

1000 seconds=int(seconds or 0), 

1001 ) 

1002 

1003 if sign == "-": 

1004 value = -value 

1005 

1006 return value 

1007 

1008 @property 

1009 def dt(self) -> timedelta: 

1010 """The time delta for compatibility.""" 

1011 return self.td 

1012 

1013 

1014class vPeriod(TimeBase): 

1015 """Period of Time 

1016 

1017 Value Name: 

1018 PERIOD 

1019 

1020 Purpose: 

1021 This value type is used to identify values that contain a 

1022 precise period of time. 

1023 

1024 Format Definition: 

1025 This value type is defined by the following notation: 

1026 

1027 .. code-block:: text 

1028 

1029 period = period-explicit / period-start 

1030 

1031 period-explicit = date-time "/" date-time 

1032 ; [ISO.8601.2004] complete representation basic format for a 

1033 ; period of time consisting of a start and end. The start MUST 

1034 ; be before the end. 

1035 

1036 period-start = date-time "/" dur-value 

1037 ; [ISO.8601.2004] complete representation basic format for a 

1038 ; period of time consisting of a start and positive duration 

1039 ; of time. 

1040 

1041 Description: 

1042 If the property permits, multiple "period" values are 

1043 specified by a COMMA-separated list of values. There are two 

1044 forms of a period of time. First, a period of time is identified 

1045 by its start and its end. This format is based on the 

1046 [ISO.8601.2004] complete representation, basic format for "DATE- 

1047 TIME" start of the period, followed by a SOLIDUS character 

1048 followed by the "DATE-TIME" of the end of the period. The start 

1049 of the period MUST be before the end of the period. Second, a 

1050 period of time can also be defined by a start and a positive 

1051 duration of time. The format is based on the [ISO.8601.2004] 

1052 complete representation, basic format for the "DATE-TIME" start of 

1053 the period, followed by a SOLIDUS character, followed by the 

1054 [ISO.8601.2004] basic format for "DURATION" of the period. 

1055 

1056 Example: 

1057 The period starting at 18:00:00 UTC, on January 1, 1997 and 

1058 ending at 07:00:00 UTC on January 2, 1997 would be: 

1059 

1060 .. code-block:: text 

1061 

1062 19970101T180000Z/19970102T070000Z 

1063 

1064 The period start at 18:00:00 on January 1, 1997 and lasting 5 hours 

1065 and 30 minutes would be: 

1066 

1067 .. code-block:: text 

1068 

1069 19970101T180000Z/PT5H30M 

1070 

1071 .. code-block:: pycon 

1072 

1073 >>> from icalendar.prop import vPeriod 

1074 >>> period = vPeriod.from_ical('19970101T180000Z/19970102T070000Z') 

1075 >>> period = vPeriod.from_ical('19970101T180000Z/PT5H30M') 

1076 """ 

1077 

1078 params: Parameters 

1079 

1080 def __init__(self, per: tuple[datetime, Union[datetime, timedelta]]): 

1081 start, end_or_duration = per 

1082 if not (isinstance(start, (datetime, date))): 

1083 raise TypeError("Start value MUST be a datetime or date instance") 

1084 if not (isinstance(end_or_duration, (datetime, date, timedelta))): 

1085 raise TypeError( 

1086 "end_or_duration MUST be a datetime, date or timedelta instance" 

1087 ) 

1088 by_duration = 0 

1089 if isinstance(end_or_duration, timedelta): 

1090 by_duration = 1 

1091 duration = end_or_duration 

1092 end = start + duration 

1093 else: 

1094 end = end_or_duration 

1095 duration = end - start 

1096 if start > end: 

1097 raise ValueError("Start time is greater than end time") 

1098 

1099 self.params = Parameters({"value": "PERIOD"}) 

1100 # set the timezone identifier 

1101 # does not support different timezones for start and end 

1102 tzid = tzid_from_dt(start) 

1103 if tzid: 

1104 self.params["TZID"] = tzid 

1105 

1106 self.start = start 

1107 self.end = end 

1108 self.by_duration = by_duration 

1109 self.duration = duration 

1110 

1111 def overlaps(self, other): 

1112 if self.start > other.start: 

1113 return other.overlaps(self) 

1114 return self.start <= other.start < self.end 

1115 

1116 def to_ical(self): 

1117 if self.by_duration: 

1118 return ( 

1119 vDatetime(self.start).to_ical() 

1120 + b"/" 

1121 + vDuration(self.duration).to_ical() 

1122 ) 

1123 return vDatetime(self.start).to_ical() + b"/" + vDatetime(self.end).to_ical() 

1124 

1125 @staticmethod 

1126 def from_ical(ical, timezone=None): 

1127 try: 

1128 start, end_or_duration = ical.split("/") 

1129 start = vDDDTypes.from_ical(start, timezone=timezone) 

1130 end_or_duration = vDDDTypes.from_ical(end_or_duration, timezone=timezone) 

1131 except Exception as e: 

1132 raise ValueError(f"Expected period format, got: {ical}") from e 

1133 return (start, end_or_duration) 

1134 

1135 def __repr__(self): 

1136 p = (self.start, self.duration) if self.by_duration else (self.start, self.end) 

1137 return f"vPeriod({p!r})" 

1138 

1139 @property 

1140 def dt(self): 

1141 """Make this cooperate with the other vDDDTypes.""" 

1142 return (self.start, (self.duration if self.by_duration else self.end)) 

1143 

1144 from icalendar.param import FBTYPE 

1145 

1146 

1147class vWeekday(str): 

1148 """Either a ``weekday`` or a ``weekdaynum`` 

1149 

1150 .. code-block:: pycon 

1151 

1152 >>> from icalendar import vWeekday 

1153 >>> vWeekday("MO") # Simple weekday 

1154 'MO' 

1155 >>> vWeekday("2FR").relative # Second friday 

1156 2 

1157 >>> vWeekday("2FR").weekday 

1158 'FR' 

1159 >>> vWeekday("-1SU").relative # Last Sunday 

1160 -1 

1161 

1162 Definition from `RFC 5545, Section 3.3.10 <https://www.rfc-editor.org/rfc/rfc5545#section-3.3.10>`_: 

1163 

1164 .. code-block:: text 

1165 

1166 weekdaynum = [[plus / minus] ordwk] weekday 

1167 plus = "+" 

1168 minus = "-" 

1169 ordwk = 1*2DIGIT ;1 to 53 

1170 weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA" 

1171 ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, 

1172 ;FRIDAY, and SATURDAY days of the week. 

1173 

1174 """ 

1175 

1176 params: Parameters 

1177 __slots__ = ("params", "relative", "weekday") 

1178 

1179 week_days = CaselessDict( 

1180 { 

1181 "SU": 0, 

1182 "MO": 1, 

1183 "TU": 2, 

1184 "WE": 3, 

1185 "TH": 4, 

1186 "FR": 5, 

1187 "SA": 6, 

1188 } 

1189 ) 

1190 

1191 def __new__( 

1192 cls, 

1193 value, 

1194 encoding=DEFAULT_ENCODING, 

1195 /, 

1196 params: Optional[dict[str, Any]] = None, 

1197 ): 

1198 if params is None: 

1199 params = {} 

1200 value = to_unicode(value, encoding=encoding) 

1201 self = super().__new__(cls, value) 

1202 match = WEEKDAY_RULE.match(self) 

1203 if match is None: 

1204 raise ValueError(f"Expected weekday abbrevation, got: {self}") 

1205 match = match.groupdict() 

1206 sign = match["signal"] 

1207 weekday = match["weekday"] 

1208 relative = match["relative"] 

1209 if weekday not in vWeekday.week_days or sign not in "+-": 

1210 raise ValueError(f"Expected weekday abbrevation, got: {self}") 

1211 self.weekday = weekday or None 

1212 self.relative = (relative and int(relative)) or None 

1213 if sign == "-" and self.relative: 

1214 self.relative *= -1 

1215 self.params = Parameters(params) 

1216 return self 

1217 

1218 def to_ical(self): 

1219 return self.encode(DEFAULT_ENCODING).upper() 

1220 

1221 @classmethod 

1222 def from_ical(cls, ical): 

1223 try: 

1224 return cls(ical.upper()) 

1225 except Exception as e: 

1226 raise ValueError(f"Expected weekday abbrevation, got: {ical}") from e 

1227 

1228 

1229class vFrequency(str): 

1230 """A simple class that catches illegal values.""" 

1231 

1232 params: Parameters 

1233 __slots__ = ("params",) 

1234 

1235 frequencies = CaselessDict( 

1236 { 

1237 "SECONDLY": "SECONDLY", 

1238 "MINUTELY": "MINUTELY", 

1239 "HOURLY": "HOURLY", 

1240 "DAILY": "DAILY", 

1241 "WEEKLY": "WEEKLY", 

1242 "MONTHLY": "MONTHLY", 

1243 "YEARLY": "YEARLY", 

1244 } 

1245 ) 

1246 

1247 def __new__( 

1248 cls, 

1249 value, 

1250 encoding=DEFAULT_ENCODING, 

1251 /, 

1252 params: Optional[dict[str, Any]] = None, 

1253 ): 

1254 if params is None: 

1255 params = {} 

1256 value = to_unicode(value, encoding=encoding) 

1257 self = super().__new__(cls, value) 

1258 if self not in vFrequency.frequencies: 

1259 raise ValueError(f"Expected frequency, got: {self}") 

1260 self.params = Parameters(params) 

1261 return self 

1262 

1263 def to_ical(self): 

1264 return self.encode(DEFAULT_ENCODING).upper() 

1265 

1266 @classmethod 

1267 def from_ical(cls, ical): 

1268 try: 

1269 return cls(ical.upper()) 

1270 except Exception as e: 

1271 raise ValueError(f"Expected frequency, got: {ical}") from e 

1272 

1273 

1274class vMonth(int): 

1275 """The number of the month for recurrence. 

1276 

1277 In :rfc:`5545`, this is just an int. 

1278 In :rfc:`7529`, this can be followed by `L` to indicate a leap month. 

1279 

1280 .. code-block:: pycon 

1281 

1282 >>> from icalendar import vMonth 

1283 >>> vMonth(1) # first month January 

1284 vMonth('1') 

1285 >>> vMonth("5L") # leap month in Hebrew calendar 

1286 vMonth('5L') 

1287 >>> vMonth(1).leap 

1288 False 

1289 >>> vMonth("5L").leap 

1290 True 

1291 

1292 Definition from RFC: 

1293 

1294 .. code-block:: text 

1295 

1296 type-bymonth = element bymonth { 

1297 xsd:positiveInteger | 

1298 xsd:string 

1299 } 

1300 """ 

1301 

1302 params: Parameters 

1303 

1304 def __new__( 

1305 cls, month: Union[str, int], /, params: Optional[dict[str, Any]] = None 

1306 ): 

1307 if params is None: 

1308 params = {} 

1309 if isinstance(month, vMonth): 

1310 return cls(month.to_ical().decode()) 

1311 if isinstance(month, str): 

1312 if month.isdigit(): 

1313 month_index = int(month) 

1314 leap = False 

1315 else: 

1316 if month[-1] != "L" and month[:-1].isdigit(): 

1317 raise ValueError(f"Invalid month: {month!r}") 

1318 month_index = int(month[:-1]) 

1319 leap = True 

1320 else: 

1321 leap = False 

1322 month_index = int(month) 

1323 self = super().__new__(cls, month_index) 

1324 self.leap = leap 

1325 self.params = Parameters(params) 

1326 return self 

1327 

1328 def to_ical(self) -> bytes: 

1329 """The ical representation.""" 

1330 return str(self).encode("utf-8") 

1331 

1332 @classmethod 

1333 def from_ical(cls, ical: str): 

1334 return cls(ical) 

1335 

1336 @property 

1337 def leap(self) -> bool: 

1338 """Whether this is a leap month.""" 

1339 return self._leap 

1340 

1341 @leap.setter 

1342 def leap(self, value: bool) -> None: 

1343 self._leap = value 

1344 

1345 def __repr__(self) -> str: 

1346 """repr(self)""" 

1347 return f"{self.__class__.__name__}({str(self)!r})" 

1348 

1349 def __str__(self) -> str: 

1350 """str(self)""" 

1351 return f"{int(self)}{'L' if self.leap else ''}" 

1352 

1353 

1354class vSkip(vText, Enum): 

1355 """Skip values for RRULE. 

1356 

1357 These are defined in :rfc:`7529`. 

1358 

1359 OMIT is the default value. 

1360 

1361 Examples: 

1362 

1363 .. code-block:: pycon 

1364 

1365 >>> from icalendar import vSkip 

1366 >>> vSkip.OMIT 

1367 vSkip('OMIT') 

1368 >>> vSkip.FORWARD 

1369 vSkip('FORWARD') 

1370 >>> vSkip.BACKWARD 

1371 vSkip('BACKWARD') 

1372 """ 

1373 

1374 OMIT = "OMIT" 

1375 FORWARD = "FORWARD" 

1376 BACKWARD = "BACKWARD" 

1377 

1378 __reduce_ex__ = Enum.__reduce_ex__ 

1379 

1380 def __repr__(self): 

1381 return f"{self.__class__.__name__}({self._name_!r})" 

1382 

1383 

1384class vRecur(CaselessDict): 

1385 """Recurrence definition. 

1386 

1387 Property Name: 

1388 RRULE 

1389 

1390 Purpose: 

1391 This property defines a rule or repeating pattern for recurring events, to-dos, 

1392 journal entries, or time zone definitions. 

1393 

1394 Value Type: 

1395 RECUR 

1396 

1397 Property Parameters: 

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

1399 

1400 Conformance: 

1401 This property can be specified in recurring "VEVENT", "VTODO", and "VJOURNAL" 

1402 calendar components as well as in the "STANDARD" and "DAYLIGHT" sub-components 

1403 of the "VTIMEZONE" calendar component, but it SHOULD NOT be specified more than once. 

1404 The recurrence set generated with multiple "RRULE" properties is undefined. 

1405 

1406 Description: 

1407 The recurrence rule, if specified, is used in computing the recurrence set. 

1408 The recurrence set is the complete set of recurrence instances for a calendar component. 

1409 The recurrence set is generated by considering the initial "DTSTART" property along 

1410 with the "RRULE", "RDATE", and "EXDATE" properties contained within the 

1411 recurring component. The "DTSTART" property defines the first instance in the 

1412 recurrence set. The "DTSTART" property value SHOULD be synchronized with the 

1413 recurrence rule, if specified. The recurrence set generated with a "DTSTART" property 

1414 value not synchronized with the recurrence rule is undefined. 

1415 The final recurrence set is generated by gathering all of the start DATE-TIME 

1416 values generated by any of the specified "RRULE" and "RDATE" properties, and then 

1417 excluding any start DATE-TIME values specified by "EXDATE" properties. 

1418 This implies that start DATE- TIME values specified by "EXDATE" properties take 

1419 precedence over those specified by inclusion properties (i.e., "RDATE" and "RRULE"). 

1420 Where duplicate instances are generated by the "RRULE" and "RDATE" properties, 

1421 only one recurrence is considered. Duplicate instances are ignored. 

1422 

1423 The "DTSTART" property specified within the iCalendar object defines the first 

1424 instance of the recurrence. In most cases, a "DTSTART" property of DATE-TIME value 

1425 type used with a recurrence rule, should be specified as a date with local time 

1426 and time zone reference to make sure all the recurrence instances start at the 

1427 same local time regardless of time zone changes. 

1428 

1429 If the duration of the recurring component is specified with the "DTEND" or 

1430 "DUE" property, then the same exact duration will apply to all the members of the 

1431 generated recurrence set. Else, if the duration of the recurring component is 

1432 specified with the "DURATION" property, then the same nominal duration will apply 

1433 to all the members of the generated recurrence set and the exact duration of each 

1434 recurrence instance will depend on its specific start time. For example, recurrence 

1435 instances of a nominal duration of one day will have an exact duration of more or less 

1436 than 24 hours on a day where a time zone shift occurs. The duration of a specific 

1437 recurrence may be modified in an exception component or simply by using an 

1438 "RDATE" property of PERIOD value type. 

1439 

1440 Examples: 

1441 The following RRULE specifies daily events for 10 occurrences. 

1442 

1443 .. code-block:: text 

1444 

1445 RRULE:FREQ=DAILY;COUNT=10 

1446 

1447 Below, we parse the RRULE ical string. 

1448 

1449 .. code-block:: pycon 

1450 

1451 >>> from icalendar.prop import vRecur 

1452 >>> rrule = vRecur.from_ical('FREQ=DAILY;COUNT=10') 

1453 >>> rrule 

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

1455 

1456 You can choose to add an rrule to an :class:`icalendar.cal.Event` or 

1457 :class:`icalendar.cal.Todo`. 

1458 

1459 .. code-block:: pycon 

1460 

1461 >>> from icalendar import Event 

1462 >>> event = Event() 

1463 >>> event.add('RRULE', 'FREQ=DAILY;COUNT=10') 

1464 >>> event.rrules 

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

1466 """ # noqa: E501 

1467 

1468 params: Parameters 

1469 

1470 frequencies = [ 

1471 "SECONDLY", 

1472 "MINUTELY", 

1473 "HOURLY", 

1474 "DAILY", 

1475 "WEEKLY", 

1476 "MONTHLY", 

1477 "YEARLY", 

1478 ] 

1479 

1480 # Mac iCal ignores RRULEs where FREQ is not the first rule part. 

1481 # Sorts parts according to the order listed in RFC 5545, section 3.3.10. 

1482 canonical_order = ( 

1483 "RSCALE", 

1484 "FREQ", 

1485 "UNTIL", 

1486 "COUNT", 

1487 "INTERVAL", 

1488 "BYSECOND", 

1489 "BYMINUTE", 

1490 "BYHOUR", 

1491 "BYDAY", 

1492 "BYWEEKDAY", 

1493 "BYMONTHDAY", 

1494 "BYYEARDAY", 

1495 "BYWEEKNO", 

1496 "BYMONTH", 

1497 "BYSETPOS", 

1498 "WKST", 

1499 "SKIP", 

1500 ) 

1501 

1502 types = CaselessDict( 

1503 { 

1504 "COUNT": vInt, 

1505 "INTERVAL": vInt, 

1506 "BYSECOND": vInt, 

1507 "BYMINUTE": vInt, 

1508 "BYHOUR": vInt, 

1509 "BYWEEKNO": vInt, 

1510 "BYMONTHDAY": vInt, 

1511 "BYYEARDAY": vInt, 

1512 "BYMONTH": vMonth, 

1513 "UNTIL": vDDDTypes, 

1514 "BYSETPOS": vInt, 

1515 "WKST": vWeekday, 

1516 "BYDAY": vWeekday, 

1517 "FREQ": vFrequency, 

1518 "BYWEEKDAY": vWeekday, 

1519 "SKIP": vSkip, 

1520 } 

1521 ) 

1522 

1523 def __init__(self, *args, params: Optional[dict[str, Any]] = None, **kwargs): 

1524 if params is None: 

1525 params = {} 

1526 if args and isinstance(args[0], str): 

1527 # we have a string as an argument. 

1528 args = (self.from_ical(args[0]),) + args[1:] 

1529 for k, v in kwargs.items(): 

1530 if not isinstance(v, SEQUENCE_TYPES): 

1531 kwargs[k] = [v] 

1532 super().__init__(*args, **kwargs) 

1533 self.params = Parameters(params) 

1534 

1535 def to_ical(self): 

1536 result = [] 

1537 for key, vals in self.sorted_items(): 

1538 typ = self.types.get(key, vText) 

1539 if not isinstance(vals, SEQUENCE_TYPES): 

1540 vals = [vals] # noqa: PLW2901 

1541 param_vals = b",".join(typ(val).to_ical() for val in vals) 

1542 

1543 # CaselessDict keys are always unicode 

1544 param_key = key.encode(DEFAULT_ENCODING) 

1545 result.append(param_key + b"=" + param_vals) 

1546 

1547 return b";".join(result) 

1548 

1549 @classmethod 

1550 def parse_type(cls, key, values): 

1551 # integers 

1552 parser = cls.types.get(key, vText) 

1553 return [parser.from_ical(v) for v in values.split(",")] 

1554 

1555 @classmethod 

1556 def from_ical(cls, ical: str): 

1557 if isinstance(ical, cls): 

1558 return ical 

1559 try: 

1560 recur = cls() 

1561 for pairs in ical.split(";"): 

1562 try: 

1563 key, vals = pairs.split("=") 

1564 except ValueError: 

1565 # E.g. incorrect trailing semicolon, like (issue #157): 

1566 # FREQ=YEARLY;BYMONTH=11;BYDAY=1SU; 

1567 continue 

1568 recur[key] = cls.parse_type(key, vals) 

1569 return cls(recur) 

1570 except ValueError: 

1571 raise 

1572 except Exception as e: 

1573 raise ValueError(f"Error in recurrence rule: {ical}") from e 

1574 

1575 

1576class vTime(TimeBase): 

1577 """Time 

1578 

1579 Value Name: 

1580 TIME 

1581 

1582 Purpose: 

1583 This value type is used to identify values that contain a 

1584 time of day. 

1585 

1586 Format Definition: 

1587 This value type is defined by the following notation: 

1588 

1589 .. code-block:: text 

1590 

1591 time = time-hour time-minute time-second [time-utc] 

1592 

1593 time-hour = 2DIGIT ;00-23 

1594 time-minute = 2DIGIT ;00-59 

1595 time-second = 2DIGIT ;00-60 

1596 ;The "60" value is used to account for positive "leap" seconds. 

1597 

1598 time-utc = "Z" 

1599 

1600 Description: 

1601 If the property permits, multiple "time" values are 

1602 specified by a COMMA-separated list of values. No additional 

1603 content value encoding (i.e., BACKSLASH character encoding, see 

1604 vText) is defined for this value type. 

1605 

1606 The "TIME" value type is used to identify values that contain a 

1607 time of day. The format is based on the [ISO.8601.2004] complete 

1608 representation, basic format for a time of day. The text format 

1609 consists of a two-digit, 24-hour of the day (i.e., values 00-23), 

1610 two-digit minute in the hour (i.e., values 00-59), and two-digit 

1611 seconds in the minute (i.e., values 00-60). The seconds value of 

1612 60 MUST only be used to account for positive "leap" seconds. 

1613 Fractions of a second are not supported by this format. 

1614 

1615 In parallel to the "DATE-TIME" definition above, the "TIME" value 

1616 type expresses time values in three forms: 

1617 

1618 The form of time with UTC offset MUST NOT be used. For example, 

1619 the following is not valid for a time value: 

1620 

1621 .. code-block:: text 

1622 

1623 230000-0800 ;Invalid time format 

1624 

1625 **FORM #1 LOCAL TIME** 

1626 

1627 The local time form is simply a time value that does not contain 

1628 the UTC designator nor does it reference a time zone. For 

1629 example, 11:00 PM: 

1630 

1631 .. code-block:: text 

1632 

1633 230000 

1634 

1635 Time values of this type are said to be "floating" and are not 

1636 bound to any time zone in particular. They are used to represent 

1637 the same hour, minute, and second value regardless of which time 

1638 zone is currently being observed. For example, an event can be 

1639 defined that indicates that an individual will be busy from 11:00 

1640 AM to 1:00 PM every day, no matter which time zone the person is 

1641 in. In these cases, a local time can be specified. The recipient 

1642 of an iCalendar object with a property value consisting of a local 

1643 time, without any relative time zone information, SHOULD interpret 

1644 the value as being fixed to whatever time zone the "ATTENDEE" is 

1645 in at any given moment. This means that two "Attendees", may 

1646 participate in the same event at different UTC times; floating 

1647 time SHOULD only be used where that is reasonable behavior. 

1648 

1649 In most cases, a fixed time is desired. To properly communicate a 

1650 fixed time in a property value, either UTC time or local time with 

1651 time zone reference MUST be specified. 

1652 

1653 The use of local time in a TIME value without the "TZID" property 

1654 parameter is to be interpreted as floating time, regardless of the 

1655 existence of "VTIMEZONE" calendar components in the iCalendar 

1656 object. 

1657 

1658 **FORM #2: UTC TIME** 

1659 

1660 UTC time, or absolute time, is identified by a LATIN CAPITAL 

1661 LETTER Z suffix character, the UTC designator, appended to the 

1662 time value. For example, the following represents 07:00 AM UTC: 

1663 

1664 .. code-block:: text 

1665 

1666 070000Z 

1667 

1668 The "TZID" property parameter MUST NOT be applied to TIME 

1669 properties whose time values are specified in UTC. 

1670 

1671 **FORM #3: LOCAL TIME AND TIME ZONE REFERENCE** 

1672 

1673 The local time with reference to time zone information form is 

1674 identified by the use the "TZID" property parameter to reference 

1675 the appropriate time zone definition. 

1676 

1677 Example: 

1678 The following represents 8:30 AM in New York in winter, 

1679 five hours behind UTC, in each of the three formats: 

1680 

1681 .. code-block:: text 

1682 

1683 083000 

1684 133000Z 

1685 TZID=America/New_York:083000 

1686 """ 

1687 

1688 def __init__(self, *args): 

1689 if len(args) == 1: 

1690 if not isinstance(args[0], (time, datetime)): 

1691 raise ValueError(f"Expected a datetime.time, got: {args[0]}") 

1692 self.dt = args[0] 

1693 else: 

1694 self.dt = time(*args) 

1695 self.params = Parameters({"value": "TIME"}) 

1696 

1697 def to_ical(self): 

1698 return self.dt.strftime("%H%M%S") 

1699 

1700 @staticmethod 

1701 def from_ical(ical): 

1702 # TODO: timezone support 

1703 try: 

1704 timetuple = (int(ical[:2]), int(ical[2:4]), int(ical[4:6])) 

1705 return time(*timetuple) 

1706 except Exception as e: 

1707 raise ValueError(f"Expected time, got: {ical}") from e 

1708 

1709 

1710class vUri(str): 

1711 """URI 

1712 

1713 Value Name: 

1714 URI 

1715 

1716 Purpose: 

1717 This value type is used to identify values that contain a 

1718 uniform resource identifier (URI) type of reference to the 

1719 property value. 

1720 

1721 Format Definition: 

1722 This value type is defined by the following notation: 

1723 

1724 .. code-block:: text 

1725 

1726 uri = scheme ":" hier-part [ "?" query ] [ "#" fragment ] 

1727 

1728 Description: 

1729 This value type might be used to reference binary 

1730 information, for values that are large, or otherwise undesirable 

1731 to include directly in the iCalendar object. 

1732 

1733 Property values with this value type MUST follow the generic URI 

1734 syntax defined in [RFC3986]. 

1735 

1736 When a property parameter value is a URI value type, the URI MUST 

1737 be specified as a quoted-string value. 

1738 

1739 Example: 

1740 The following is a URI for a network file: 

1741 

1742 .. code-block:: text 

1743 

1744 http://example.com/my-report.txt 

1745 

1746 .. code-block:: pycon 

1747 

1748 >>> from icalendar.prop import vUri 

1749 >>> uri = vUri.from_ical('http://example.com/my-report.txt') 

1750 >>> uri 

1751 'http://example.com/my-report.txt' 

1752 """ 

1753 

1754 params: Parameters 

1755 __slots__ = ("params",) 

1756 

1757 def __new__( 

1758 cls, 

1759 value, 

1760 encoding=DEFAULT_ENCODING, 

1761 /, 

1762 params: Optional[dict[str, Any]] = None, 

1763 ): 

1764 if params is None: 

1765 params = {} 

1766 value = to_unicode(value, encoding=encoding) 

1767 self = super().__new__(cls, value) 

1768 self.params = Parameters(params) 

1769 return self 

1770 

1771 def to_ical(self): 

1772 return self.encode(DEFAULT_ENCODING) 

1773 

1774 @classmethod 

1775 def from_ical(cls, ical): 

1776 try: 

1777 return cls(ical) 

1778 except Exception as e: 

1779 raise ValueError(f"Expected , got: {ical}") from e 

1780 

1781 

1782class vGeo: 

1783 """Geographic Position 

1784 

1785 Property Name: 

1786 GEO 

1787 

1788 Purpose: 

1789 This property specifies information related to the global 

1790 position for the activity specified by a calendar component. 

1791 

1792 Value Type: 

1793 FLOAT. The value MUST be two SEMICOLON-separated FLOAT values. 

1794 

1795 Property Parameters: 

1796 IANA and non-standard property parameters can be specified on 

1797 this property. 

1798 

1799 Conformance: 

1800 This property can be specified in "VEVENT" or "VTODO" 

1801 calendar components. 

1802 

1803 Description: 

1804 This property value specifies latitude and longitude, 

1805 in that order (i.e., "LAT LON" ordering). The longitude 

1806 represents the location east or west of the prime meridian as a 

1807 positive or negative real number, respectively. The longitude and 

1808 latitude values MAY be specified up to six decimal places, which 

1809 will allow for accuracy to within one meter of geographical 

1810 position. Receiving applications MUST accept values of this 

1811 precision and MAY truncate values of greater precision. 

1812 

1813 Example: 

1814 

1815 .. code-block:: text 

1816 

1817 GEO:37.386013;-122.082932 

1818 

1819 Parse vGeo: 

1820 

1821 .. code-block:: pycon 

1822 

1823 >>> from icalendar.prop import vGeo 

1824 >>> geo = vGeo.from_ical('37.386013;-122.082932') 

1825 >>> geo 

1826 (37.386013, -122.082932) 

1827 

1828 Add a geo location to an event: 

1829 

1830 .. code-block:: pycon 

1831 

1832 >>> from icalendar import Event 

1833 >>> event = Event() 

1834 >>> latitude = 37.386013 

1835 >>> longitude = -122.082932 

1836 >>> event.add('GEO', (latitude, longitude)) 

1837 >>> event['GEO'] 

1838 vGeo((37.386013, -122.082932)) 

1839 """ 

1840 

1841 params: Parameters 

1842 

1843 def __init__( 

1844 self, 

1845 geo: tuple[float | str | int, float | str | int], 

1846 /, 

1847 params: Optional[dict[str, Any]] = None, 

1848 ): 

1849 """Create a new vGeo from a tuple of (latitude, longitude). 

1850 

1851 Raises: 

1852 ValueError: if geo is not a tuple of (latitude, longitude) 

1853 """ 

1854 if params is None: 

1855 params = {} 

1856 try: 

1857 latitude, longitude = (geo[0], geo[1]) 

1858 latitude = float(latitude) 

1859 longitude = float(longitude) 

1860 except Exception as e: 

1861 raise ValueError( 

1862 "Input must be (float, float) for latitude and longitude" 

1863 ) from e 

1864 self.latitude = latitude 

1865 self.longitude = longitude 

1866 self.params = Parameters(params) 

1867 

1868 def to_ical(self): 

1869 return f"{self.latitude};{self.longitude}" 

1870 

1871 @staticmethod 

1872 def from_ical(ical): 

1873 try: 

1874 latitude, longitude = ical.split(";") 

1875 return (float(latitude), float(longitude)) 

1876 except Exception as e: 

1877 raise ValueError(f"Expected 'float;float' , got: {ical}") from e 

1878 

1879 def __eq__(self, other): 

1880 return self.to_ical() == other.to_ical() 

1881 

1882 def __repr__(self): 

1883 """repr(self)""" 

1884 return f"{self.__class__.__name__}(({self.latitude}, {self.longitude}))" 

1885 

1886 

1887class vUTCOffset: 

1888 """UTC Offset 

1889 

1890 Value Name: 

1891 UTC-OFFSET 

1892 

1893 Purpose: 

1894 This value type is used to identify properties that contain 

1895 an offset from UTC to local time. 

1896 

1897 Format Definition: 

1898 This value type is defined by the following notation: 

1899 

1900 .. code-block:: text 

1901 

1902 utc-offset = time-numzone 

1903 

1904 time-numzone = ("+" / "-") time-hour time-minute [time-second] 

1905 

1906 Description: 

1907 The PLUS SIGN character MUST be specified for positive 

1908 UTC offsets (i.e., ahead of UTC). The HYPHEN-MINUS character MUST 

1909 be specified for negative UTC offsets (i.e., behind of UTC). The 

1910 value of "-0000" and "-000000" are not allowed. The time-second, 

1911 if present, MUST NOT be 60; if absent, it defaults to zero. 

1912 

1913 Example: 

1914 The following UTC offsets are given for standard time for 

1915 New York (five hours behind UTC) and Geneva (one hour ahead of 

1916 UTC): 

1917 

1918 .. code-block:: text 

1919 

1920 -0500 

1921 

1922 +0100 

1923 

1924 .. code-block:: pycon 

1925 

1926 >>> from icalendar.prop import vUTCOffset 

1927 >>> utc_offset = vUTCOffset.from_ical('-0500') 

1928 >>> utc_offset 

1929 datetime.timedelta(days=-1, seconds=68400) 

1930 >>> utc_offset = vUTCOffset.from_ical('+0100') 

1931 >>> utc_offset 

1932 datetime.timedelta(seconds=3600) 

1933 """ 

1934 

1935 params: Parameters 

1936 

1937 ignore_exceptions = False # if True, and we cannot parse this 

1938 

1939 # component, we will silently ignore 

1940 # it, rather than let the exception 

1941 # propagate upwards 

1942 

1943 def __init__(self, td, /, params: Optional[dict[str, Any]] = None): 

1944 if params is None: 

1945 params = {} 

1946 if not isinstance(td, timedelta): 

1947 raise TypeError("Offset value MUST be a timedelta instance") 

1948 self.td = td 

1949 self.params = Parameters(params) 

1950 

1951 def to_ical(self): 

1952 if self.td < timedelta(0): 

1953 sign = "-%s" 

1954 td = timedelta(0) - self.td # get timedelta relative to 0 

1955 else: 

1956 # Google Calendar rejects '0000' but accepts '+0000' 

1957 sign = "+%s" 

1958 td = self.td 

1959 

1960 days, seconds = td.days, td.seconds 

1961 

1962 hours = abs(days * 24 + seconds // 3600) 

1963 minutes = abs((seconds % 3600) // 60) 

1964 seconds = abs(seconds % 60) 

1965 if seconds: 

1966 duration = f"{hours:02}{minutes:02}{seconds:02}" 

1967 else: 

1968 duration = f"{hours:02}{minutes:02}" 

1969 return sign % duration 

1970 

1971 @classmethod 

1972 def from_ical(cls, ical): 

1973 if isinstance(ical, cls): 

1974 return ical.td 

1975 try: 

1976 sign, hours, minutes, seconds = ( 

1977 ical[0:1], 

1978 int(ical[1:3]), 

1979 int(ical[3:5]), 

1980 int(ical[5:7] or 0), 

1981 ) 

1982 offset = timedelta(hours=hours, minutes=minutes, seconds=seconds) 

1983 except Exception as e: 

1984 raise ValueError(f"Expected utc offset, got: {ical}") from e 

1985 if not cls.ignore_exceptions and offset >= timedelta(hours=24): 

1986 raise ValueError(f"Offset must be less than 24 hours, was {ical}") 

1987 if sign == "-": 

1988 return -offset 

1989 return offset 

1990 

1991 def __eq__(self, other): 

1992 if not isinstance(other, vUTCOffset): 

1993 return False 

1994 return self.td == other.td 

1995 

1996 def __hash__(self): 

1997 return hash(self.td) 

1998 

1999 def __repr__(self): 

2000 return f"vUTCOffset({self.td!r})" 

2001 

2002 

2003class vInline(str): 

2004 """This is an especially dumb class that just holds raw unparsed text and 

2005 has parameters. Conversion of inline values are handled by the Component 

2006 class, so no further processing is needed. 

2007 """ 

2008 

2009 params: Parameters 

2010 __slots__ = ("params",) 

2011 

2012 def __new__( 

2013 cls, 

2014 value, 

2015 encoding=DEFAULT_ENCODING, 

2016 /, 

2017 params: Optional[dict[str, Any]] = None, 

2018 ): 

2019 if params is None: 

2020 params = {} 

2021 value = to_unicode(value, encoding=encoding) 

2022 self = super().__new__(cls, value) 

2023 self.params = Parameters(params) 

2024 return self 

2025 

2026 def to_ical(self): 

2027 return self.encode(DEFAULT_ENCODING) 

2028 

2029 @classmethod 

2030 def from_ical(cls, ical): 

2031 return cls(ical) 

2032 

2033 

2034class TypesFactory(CaselessDict): 

2035 """All Value types defined in RFC 5545 are registered in this factory 

2036 class. 

2037 

2038 The value and parameter names don't overlap. So one factory is enough for 

2039 both kinds. 

2040 """ 

2041 

2042 def __init__(self, *args, **kwargs): 

2043 """Set keys to upper for initial dict""" 

2044 super().__init__(*args, **kwargs) 

2045 self.all_types = ( 

2046 vBinary, 

2047 vBoolean, 

2048 vCalAddress, 

2049 vDDDLists, 

2050 vDDDTypes, 

2051 vDate, 

2052 vDatetime, 

2053 vDuration, 

2054 vFloat, 

2055 vFrequency, 

2056 vGeo, 

2057 vInline, 

2058 vInt, 

2059 vPeriod, 

2060 vRecur, 

2061 vText, 

2062 vTime, 

2063 vUTCOffset, 

2064 vUri, 

2065 vWeekday, 

2066 vCategory, 

2067 ) 

2068 self["binary"] = vBinary 

2069 self["boolean"] = vBoolean 

2070 self["cal-address"] = vCalAddress 

2071 self["date"] = vDDDTypes 

2072 self["date-time"] = vDDDTypes 

2073 self["duration"] = vDDDTypes 

2074 self["float"] = vFloat 

2075 self["integer"] = vInt 

2076 self["period"] = vPeriod 

2077 self["recur"] = vRecur 

2078 self["text"] = vText 

2079 self["time"] = vTime 

2080 self["uri"] = vUri 

2081 self["utc-offset"] = vUTCOffset 

2082 self["geo"] = vGeo 

2083 self["inline"] = vInline 

2084 self["date-time-list"] = vDDDLists 

2085 self["categories"] = vCategory 

2086 

2087 ################################################# 

2088 # Property types 

2089 

2090 # These are the default types 

2091 types_map = CaselessDict( 

2092 { 

2093 #################################### 

2094 # Property value types 

2095 # Calendar Properties 

2096 "calscale": "text", 

2097 "method": "text", 

2098 "prodid": "text", 

2099 "version": "text", 

2100 # Descriptive Component Properties 

2101 "attach": "uri", 

2102 "categories": "categories", 

2103 "class": "text", 

2104 "comment": "text", 

2105 "description": "text", 

2106 "geo": "geo", 

2107 "location": "text", 

2108 "percent-complete": "integer", 

2109 "priority": "integer", 

2110 "resources": "text", 

2111 "status": "text", 

2112 "summary": "text", 

2113 # Date and Time Component Properties 

2114 "completed": "date-time", 

2115 "dtend": "date-time", 

2116 "due": "date-time", 

2117 "dtstart": "date-time", 

2118 "duration": "duration", 

2119 "freebusy": "period", 

2120 "transp": "text", 

2121 # Time Zone Component Properties 

2122 "tzid": "text", 

2123 "tzname": "text", 

2124 "tzoffsetfrom": "utc-offset", 

2125 "tzoffsetto": "utc-offset", 

2126 "tzurl": "uri", 

2127 # Relationship Component Properties 

2128 "attendee": "cal-address", 

2129 "contact": "text", 

2130 "organizer": "cal-address", 

2131 "recurrence-id": "date-time", 

2132 "related-to": "text", 

2133 "url": "uri", 

2134 "uid": "text", 

2135 # Recurrence Component Properties 

2136 "exdate": "date-time-list", 

2137 "exrule": "recur", 

2138 "rdate": "date-time-list", 

2139 "rrule": "recur", 

2140 # Alarm Component Properties 

2141 "action": "text", 

2142 "repeat": "integer", 

2143 "trigger": "duration", 

2144 "acknowledged": "date-time", 

2145 # Change Management Component Properties 

2146 "created": "date-time", 

2147 "dtstamp": "date-time", 

2148 "last-modified": "date-time", 

2149 "sequence": "integer", 

2150 # Miscellaneous Component Properties 

2151 "request-status": "text", 

2152 #################################### 

2153 # parameter types (luckily there is no name overlap) 

2154 "altrep": "uri", 

2155 "cn": "text", 

2156 "cutype": "text", 

2157 "delegated-from": "cal-address", 

2158 "delegated-to": "cal-address", 

2159 "dir": "uri", 

2160 "encoding": "text", 

2161 "fmttype": "text", 

2162 "fbtype": "text", 

2163 "language": "text", 

2164 "member": "cal-address", 

2165 "partstat": "text", 

2166 "range": "text", 

2167 "related": "text", 

2168 "reltype": "text", 

2169 "role": "text", 

2170 "rsvp": "boolean", 

2171 "sent-by": "cal-address", 

2172 "value": "text", 

2173 } 

2174 ) 

2175 

2176 def for_property(self, name): 

2177 """Returns a the default type for a property or parameter""" 

2178 return self[self.types_map.get(name, "text")] 

2179 

2180 def to_ical(self, name, value): 

2181 """Encodes a named value from a primitive python type to an icalendar 

2182 encoded string. 

2183 """ 

2184 type_class = self.for_property(name) 

2185 return type_class(value).to_ical() 

2186 

2187 def from_ical(self, name, value): 

2188 """Decodes a named property or parameter value from an icalendar 

2189 encoded string to a primitive python type. 

2190 """ 

2191 type_class = self.for_property(name) 

2192 return type_class.from_ical(value) 

2193 

2194 

2195__all__ = [ 

2196 "DURATION_REGEX", 

2197 "WEEKDAY_RULE", 

2198 "TimeBase", 

2199 "TypesFactory", 

2200 "tzid_from_dt", 

2201 "tzid_from_tzinfo", 

2202 "vBinary", 

2203 "vBoolean", 

2204 "vCalAddress", 

2205 "vCategory", 

2206 "vDDDLists", 

2207 "vDDDTypes", 

2208 "vDate", 

2209 "vDatetime", 

2210 "vDuration", 

2211 "vFloat", 

2212 "vFrequency", 

2213 "vGeo", 

2214 "vInline", 

2215 "vInt", 

2216 "vMonth", 

2217 "vPeriod", 

2218 "vRecur", 

2219 "vSkip", 

2220 "vText", 

2221 "vTime", 

2222 "vUTCOffset", 

2223 "vUri", 

2224 "vWeekday", 

2225]