Source code for icalendar.prop.dt.date

"""DATE property type from :rfc:`5545`."""

from __future__ import annotations

from datetime import date, datetime
from typing import TYPE_CHECKING, Any, ClassVar
from xml.etree.ElementTree import Element, SubElement

from icalendar.error import ICalParsingError, JCalParsingError, XCalParsingError
from icalendar.parser import Parameters
from icalendar.parser.xcal.match import XCalRegexMatcher
from icalendar.parser.xcal.wrapper import from_xcal_wrapper

from .base import TimeBase

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

XCAL_DATE_REGEX = XCalRegexMatcher(
    r"(\d\d\d\d)-(\d\d)-(\d\d)", "Expected date format YYYY-MM-DD"
)


[docs] class vDate(TimeBase): """Date Value Name: DATE Purpose: This value type is used to identify values that contain a calendar date. Format Definition: This value type is defined by the following notation: .. code-block:: text date = date-value date-value = date-fullyear date-month date-mday date-fullyear = 4DIGIT date-month = 2DIGIT ;01-12 date-mday = 2DIGIT ;01-28, 01-29, 01-30, 01-31 ;based on month/year Description: If the property permits, multiple "date" values are specified as a COMMA-separated list of values. The format for the value type is based on the [ISO.8601.2004] complete representation, basic format for a calendar date. The textual format specifies a four-digit year, two-digit month, and two-digit day of the month. There are no separator characters between the year, month, and day component text. Example: The following represents July 14, 1997: .. code-block:: ics 19970714 .. code-block:: pycon >>> from icalendar.prop import vDate >>> date = vDate.from_ical('19970714') >>> date.year 1997 >>> date.month 7 >>> date.day 14 """ default_value: ClassVar[str] = "DATE" params: Parameters def __init__(self, dt: date, params: dict[str, Any] | None = None) -> None: if not isinstance(dt, date): raise TypeError("Value MUST be a date instance") self.dt = dt self.params = Parameters(params or {})
[docs] def to_ical(self) -> bytes: s = f"{self.dt.year:04}{self.dt.month:02}{self.dt.day:02}" return s.encode("utf-8")
[docs] @staticmethod def from_ical(ical: str) -> date: """Parse an iCalendar DATE value. Parameters: ical (str | bytes): Required. The DATE value in ``YYYYMMDD`` format. Raises: ~icalendar.error.ICalParsingError: If the value is not a valid DATE. """ # date-value = 4DIGIT 2DIGIT 2DIGIT, no separators, # per https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.4 if len(ical) != 8 or not ical.isascii() or not ical.isdigit(): raise ICalParsingError( "Wrong date format", value=ical, ) try: timetuple = ( int(ical[:4]), # year int(ical[4:6]), # month int(ical[6:8]), # day ) return date(*timetuple) except Exception as e: raise ICalParsingError( "Wrong date format", value=ical, ) from e
[docs] @classmethod def examples(cls) -> list[Self]: """Examples of vDate.""" return [cls(date(2025, 11, 10))]
from icalendar.param import VALUE
[docs] def to_jcal(self, name: str) -> list: """The jCal representation of this property according to :rfc:`7265`.""" return [ name, self.params.to_jcal(), self.VALUE.lower(), self.dt.strftime("%Y-%m-%d"), ]
[docs] @classmethod def parse_jcal_value(cls, jcal: str) -> date: """Parse a jCal string to a :class:`datetime.date`. Raises: ~icalendar.error.JCalParsingError: If it can't parse a date. """ JCalParsingError.validate_value_type(jcal, str, cls) try: return datetime.strptime(jcal, "%Y-%m-%d").date() except ValueError as e: raise JCalParsingError("Cannot parse date.", cls, value=jcal) from e
[docs] @classmethod def from_jcal(cls, jcal_property: list) -> Self: """Parse jCal from :rfc:`7265`. Parameters: jcal_property: The jCal property to parse. Raises: ~icalendar.error.JCalParsingError: If the provided jCal is invalid. """ JCalParsingError.validate_property(jcal_property, cls) with JCalParsingError.reraise_with_path_added(3): value = cls.parse_jcal_value(jcal_property[3]) return cls( value, params=Parameters.from_jcal_property(jcal_property), )
[docs] def to_xcal(self, element: Element) -> None: """The xCal representation of this property according to :rfc:`6321`.""" self.params.to_xcal(element) element = SubElement(element, "date") element.text = self.dt.strftime("%Y-%m-%d")
[docs] @classmethod def from_xcal_element(cls, element: ElementAdapter) -> date: """Parse xCal from :rfc:`6321`. Parameters: element: The xCal element to parse. Returns: The date object. Raises: ~error.XCalParsingError: If the provided xCal is invalid. """ year, month, day = XCAL_DATE_REGEX.groups(element) try: return date(int(year), int(month), int(day)) except ValueError as e: raise XCalParsingError( "Date is out of range", element.get_xsd_token(), element ) 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. """ element = parser.parse_tag(cls.default_value) d = cls.from_xcal_element(element) return cls( d, params=params, )
@property def ical_value(self) -> date: """Returns the date as a date value, in year-month-day format. .. seealso:: :rfc:`5545#section-3.3.4` for the ``DATE`` value type. """ return self.dt
__all__ = ["vDate"]