Source code for icalendar.prop.recur.weekday

"""BYDAY and WKST value type of RECUR from :rfc:`5545`."""

from __future__ import annotations

import re
from typing import TYPE_CHECKING, Any
from xml.etree.ElementTree import Element, SubElement

from icalendar.caselessdict import CaselessDict
from icalendar.error import JCalParsingError, XCalParsingError
from icalendar.parser import Parameters
from icalendar.parser.xcal.wrapper import from_xcal_wrapper
from icalendar.parser_tools import DEFAULT_ENCODING, to_unicode

if TYPE_CHECKING:
    from icalendar.compatibility import Self
    from icalendar.parser.xcal.base import XCalParser

# Use ``[0-9]`` to explicitly specify that only ASCII digits should be
# matched, instead of ``\d``. In Python, ``\d`` matches a digit zero through
# nine in any script except ideographic scripts, and is equivalent to
# ``\p{Nd}``. For example, the Arabic-Indic "١٢" would match. Here, a
# malformed ``ordwk`` would get parsed into a valid ``relative`` number.
# RFC 5545, section 3.3.10, allows only ASCII characters per its definition
# of ``DIGIT``.
WEEKDAY_RULE = re.compile(
    r"(?P<signal>[+-]?)(?P<relative>[0-9]{0,2})(?P<weekday>[\w]{2})\Z"
)


[docs] class vWeekday(str): """Either a ``weekday`` or a ``weekdaynum``. .. code-block:: pycon >>> from icalendar import vWeekday >>> vWeekday("MO") # Simple weekday 'MO' >>> vWeekday("2FR").relative # Second friday 2 >>> vWeekday("2FR").weekday 'FR' >>> vWeekday("-1SU").relative # Last Sunday -1 Definition from :rfc:`5545#section-3.3.10`: .. code-block:: text weekdaynum = [[plus / minus] ordwk] weekday plus = "+" minus = "-" ordwk = 1*2DIGIT ;1 to 53 weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA" ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, ;FRIDAY, and SATURDAY days of the week. """ params: Parameters __slots__ = ("params", "relative", "weekday") week_days = CaselessDict( { "SU": 0, "MO": 1, "TU": 2, "WE": 3, "TH": 4, "FR": 5, "SA": 6, } ) def __new__( cls, value, encoding=DEFAULT_ENCODING, /, params: dict[str, Any] | None = None, ): value = to_unicode(value, encoding=encoding) self = super().__new__(cls, value) match = WEEKDAY_RULE.match(self) if match is None: raise ValueError(f"Expected weekday abbreviation, got: {self}") match = match.groupdict() sign = match["signal"] weekday = match["weekday"] relative = match["relative"] if weekday not in vWeekday.week_days or sign not in "+-": raise ValueError(f"Expected weekday abbreviation, got: {self}") self.weekday = weekday or None self.relative = (relative and int(relative)) or None if sign == "-" and self.relative: self.relative *= -1 self.params = Parameters(params) return self
[docs] def to_ical(self) -> bytes: return self.encode(DEFAULT_ENCODING).upper()
[docs] @classmethod def from_ical(cls, ical): try: return cls(ical.upper()) except Exception as e: raise ValueError(f"Expected weekday abbreviation, got: {ical}") from e
@property def ical_value(self) -> str: """Returns the weekday value as a string, for example, ``MO``, ``+2TH``, or ``-1SU``. See Also: :rfc:`5545#section-3.3.10` for the ``BYDAY`` rule grammar. """ return str(self)
[docs] @classmethod def parse_jcal_value(cls, value: Any) -> Self: """Parse a jCal value for vWeekday. Raises: ~icalendar.error.JCalParsingError: If the value is not a valid weekday. """ JCalParsingError.validate_value_type(value, str, cls) try: return cls(value) except ValueError as e: raise JCalParsingError( "The value must be a valid weekday.", cls, value=value ) from e
[docs] @classmethod @from_xcal_wrapper def from_xcal(cls, parser: XCalParser, params: Parameters) -> Self: """Parse xCal from :rfc:`6321`. Parameters: xml: The XML to parse or a parser. Raises: ~error.XCalParsingError: If the provided xCal is invalid. """ child = parser.parse_tag() text = child.get_xsd_token() try: return cls(text.upper(), params=params) except ValueError as e: raise XCalParsingError( "Expected weekday https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.10", text, child, ) from e
[docs] def to_xcal(self, element: Element) -> None: """Add the xCal representation of this property according to :rfc:`6321`.""" self.params.to_xcal(element) weekday_element = SubElement(element, "byday") weekday_element.text = self.ical_value
__all__ = ["vWeekday"]