diff --git a/ical/recur_adapter.py b/ical/recur_adapter.py index 0d1ea9d1..62a48744 100644 --- a/ical/recur_adapter.py +++ b/ical/recur_adapter.py @@ -28,20 +28,6 @@ _DateOrDatetime = datetime.datetime | datetime.date -def _recurrence_id_for(dt: _DateOrDatetime) -> RecurrenceId: - """Compute the RecurrenceId for a recurrence date. - - This converts a date/datetime from a recurrence expansion into the - floating-time RecurrenceId string used to identify that instance. - """ - # Make recurrence_id floating time to avoid dealing with serializing - # TZID. This value will still be unique within the series and is in - # the context of dtstart which may have a timezone. - if isinstance(dt, datetime.datetime) and dt.tzinfo: - dt = dt.replace(tzinfo=None) - return RecurrenceId.__parse_property_value__(dt) - - class FilteredRecurrenceIterable(Iterable[_DateOrDatetime]): """An iterable that filters out dates from a recurrence expansion. @@ -52,7 +38,7 @@ class FilteredRecurrenceIterable(Iterable[_DateOrDatetime]): def __init__( self, recur: Iterable[_DateOrDatetime], - exclude_ids: frozenset[RecurrenceId], + exclude_ids: frozenset[datetime.date | datetime.datetime], ) -> None: """Initialize the filtered iterable.""" self._recur = recur @@ -61,7 +47,7 @@ def __init__( def __iter__(self) -> Iterator[_DateOrDatetime]: """Iterate over recurrence dates, excluding overridden ones.""" for dt in self._recur: - if _recurrence_id_for(dt) not in self._exclude_ids: + if dt not in self._exclude_ids: yield dt @@ -89,7 +75,7 @@ def get( """Return a lazy sortable item.""" dtend = dtstart + self._duration if self._duration else dtstart - recurrence_id = _recurrence_id_for(dtstart) + recurrence_id = RecurrenceId.from_value(dtstart) def build() -> ItemType: updates = { @@ -137,7 +123,7 @@ def merge_and_expand_items( # An edited instance has a recurrence_id (identifying which # instance it replaces) but no rrule (it's a single instance). exclude_ids = frozenset( - item.recurrence_id + item.recurrence_id.date for item in uid_items if item.recurrence_id and not item.rrule ) diff --git a/ical/store.py b/ical/store.py index f31ca53e..b39f005f 100644 --- a/ical/store.py +++ b/ical/store.py @@ -72,7 +72,7 @@ def _ensure_timezone( ) from err -def _match_item(item: _T, uid: str, recurrence_id: str | None) -> bool: +def _match_item(item: _T, uid: str, recurrence_id: RecurrenceId | None) -> bool: """Return True if the item is an instance of a recurring event.""" if item.uid != uid: return False @@ -81,21 +81,23 @@ def _match_item(item: _T, uid: str, recurrence_id: str | None) -> bool: return True # Match a single item with the specified recurrence_id. If the item is an # edited instance match return it - if item.recurrence_id == recurrence_id: + if isinstance(recurrence_id.date, datetime.datetime) and isinstance( + item.dtstart, datetime.datetime + ): + # Add timezone info if we're missing it + if item.dtstart.tzinfo is not None: + recurrence_id = recurrence_id.from_value( + recurrence_id, timezone=item.dtstart.tzinfo + ) + + dtstart = recurrence_id.date + if item.recurrence_id and item.recurrence_id.date == dtstart: _LOGGER.debug("Matched exact recurrence_id: %s", item) return True # Otherwise, determine if this instance is in the series _LOGGER.debug( "Expanding item %s %s to look for match of %s", uid, item.dtstart, recurrence_id ) - dtstart = RecurrenceId.to_value(recurrence_id) - if isinstance(dtstart, datetime.datetime) and isinstance( - item.dtstart, datetime.datetime - ): - # The recurrence_id does not support timezone information, so put it in the - # same timezone as the item to compare. - if item.dtstart.tzinfo is not None: - dtstart = dtstart.replace(tzinfo=item.dtstart.tzinfo) for dt in item.as_rrule() or (): if isinstance(dt, datetime.datetime): if dt.date() > _MAX_SCAN_DATE: @@ -111,7 +113,7 @@ def _match_item(item: _T, uid: str, recurrence_id: str | None) -> bool: def _match_items( - items: list[_T], uid: str, recurrence_id: str | None + items: list[_T], uid: str, recurrence_id: RecurrenceId | None ) -> Generator[tuple[int, _T], None, None]: """Return items from the list that match the uid and recurrence_id.""" for index, item in enumerate(items): @@ -122,8 +124,7 @@ def _match_items( def _prepare_update( store_item: Event | Todo, item: Event | Todo, - recurrence_id: str | None = None, - recurrence_range: Range = Range.NONE, + recurrence_id: RecurrenceId | None = None, ) -> dict[str, Any]: """Prepare an update to an existing event.""" partial_update = item.model_dump( @@ -162,7 +163,7 @@ def _prepare_update( "recurrence_id": recurrence_id, } ) - if recurrence_range == Range.NONE: + if recurrence_id.range == Range.NONE: # The new event copied from the original is a single instance, # which is not recurring. update["rrule"] = None @@ -171,9 +172,7 @@ def _prepare_update( update["created"] = item.dtstamp # Adjust start and end time of the event - dtstart: datetime.datetime | datetime.date = RecurrenceId.to_value( - recurrence_id - ) + dtstart: datetime.datetime | datetime.date = recurrence_id.date if item.dtstart: dtstart = item.dtstart update["dtstart"] = dtstart @@ -245,8 +244,12 @@ def add(self, item: _T) -> _T: def delete( self, uid: str, - recurrence_id: str | None = None, - recurrence_range: Range = Range.NONE, + recurrence_id: RecurrenceId + | datetime.datetime + | datetime.date + | str + | None = None, + recurrence_range: Range | None = None, ) -> None: """Delete the item from the calendar. @@ -260,8 +263,12 @@ def delete( When deleting individual instances, the range property may specify if deletion of just a specific instance, or a range of instances. """ + recurrence = None + if recurrence_id: + recurrence = RecurrenceId.from_value(recurrence_id, recurrence_range) + items_to_delete: list[_T] = [ - item for _, item in _match_items(self._items, uid, recurrence_id) + item for _, item in _match_items(self._items, uid, recurrence) ] if not items_to_delete: raise self._exc( @@ -269,18 +276,15 @@ def delete( ) for store_item in items_to_delete: - self._apply_delete(store_item, recurrence_id, recurrence_range) + self._apply_delete(store_item, recurrence) def _apply_delete( - self, - store_item: _T, - recurrence_id: str | None = None, - recurrence_range: Range = Range.NONE, + self, store_item: _T, recurrence_id: RecurrenceId | None = None ) -> None: if ( recurrence_id - and recurrence_range == Range.THIS_AND_FUTURE - and RecurrenceId.to_value(recurrence_id) == store_item.dtstart + and recurrence_id.range == Range.THIS_AND_FUTURE + and recurrence_id.date == store_item.dtstart ): # Editing the first instance and all forward is the same as editing the # entire series so don't bother forking a new event @@ -302,18 +306,10 @@ def _apply_delete( self._items.remove(store_item) return - exdate = RecurrenceId.to_value(recurrence_id) - if recurrence_range == Range.NONE: + exdate = recurrence_id.date + if recurrence_id.range == Range.NONE: # A single recurrence instance is removed. Add an exclusion to # to the event. - # RecurrenceId does not support timezone information. The exclusion - # must have the same timezone as the item to compare. - if ( - isinstance(exdate, datetime.datetime) - and isinstance(store_item.dtstart, datetime.datetime) - and store_item.dtstart.tzinfo - ): - exdate = exdate.replace(tzinfo=store_item.dtstart.tzinfo) store_item.exdate.append(exdate) return @@ -338,7 +334,11 @@ def edit( self, uid: str, item: _T, - recurrence_id: str | None = None, + recurrence_id: RecurrenceId + | datetime.datetime + | datetime.date + | str + | None = None, recurrence_range: Range = Range.NONE, ) -> None: """Update the item with the specified uid. @@ -359,9 +359,12 @@ def edit( `ical.timezone.Timezone` needed to fully specify the item time information when encoded. """ + recurrence = None + if recurrence_id: + recurrence = RecurrenceId.from_value(recurrence_id, recurrence_range) + items_to_edit: list[tuple[int, _T]] = [ - (index, item) - for index, item in _match_items(self._items, uid, recurrence_id) + (index, item) for index, item in _match_items(self._items, uid, recurrence) ] if not items_to_edit: raise self._exc( @@ -369,29 +372,26 @@ def edit( ) for store_index, store_item in items_to_edit: - self._apply_edit( - store_index, store_item, item, recurrence_id, recurrence_range - ) + self._apply_edit(store_index, store_item, item, recurrence) def _apply_edit( self, store_index: int, store_item: _T, item: _T, - recurrence_id: str | None = None, - recurrence_range: Range = Range.NONE, + recurrence_id: RecurrenceId | None = None, ) -> None: if ( recurrence_id - and recurrence_range == Range.THIS_AND_FUTURE - and RecurrenceId.to_value(recurrence_id) == store_item.dtstart + and recurrence_id.range == Range.THIS_AND_FUTURE + and recurrence_id.date == store_item.dtstart ): # Editing the first instance and all forward is the same as editing the # entire series so don't bother forking a new item recurrence_id = None - update = _prepare_update(store_item, item, recurrence_id, recurrence_range) - if recurrence_range == Range.NONE: + update = _prepare_update(store_item, item, recurrence_id) + if recurrence_id and recurrence_id.range == Range.NONE: # Changing the recurrence rule of a single item in the middle of the series # is not allowed. It is allowed to convert a single instance item to recurring. if item.rrule and store_item.rrule: @@ -439,7 +439,7 @@ def _apply_edit( self.delete( store_item.uid, recurrence_id=recurrence_id, - recurrence_range=recurrence_range, + recurrence_range=recurrence_id.range if recurrence_id else None, ) self._items.insert(store_index, new_item) diff --git a/ical/types/recur.py b/ical/types/recur.py index 9bbfde3f..b068eb16 100644 --- a/ical/types/recur.py +++ b/ical/types/recur.py @@ -40,6 +40,7 @@ import re from dataclasses import dataclass from typing import Annotated, Any, Literal, Optional, Union +from zoneinfo import ZoneInfo from dateutil import rrule from pydantic import ( @@ -52,7 +53,7 @@ ) from pydantic_core import CoreSchema, core_schema -from ical.parsing.property import ParsedProperty +from ical.parsing.property import ParsedProperty, ParsedPropertyParameter from ical.util import parse_date_and_datetime from .data_types import DATA_TYPE, serialize_field @@ -137,8 +138,8 @@ class Range(str, enum.Enum): """The range of the recurrence identifier and all subsequent values.""" -@DATA_TYPE.register(disable_value_param=True) -class RecurrenceId(str): +@DATA_TYPE.register("RECURRENCE-ID", disable_value_param=True) +class RecurrenceId(BaseModel): """Identifies a specific instance of a recurring calendar component. A property type used in conjunction with the "UID" and "SEQUENCE" properties @@ -148,13 +149,66 @@ class RecurrenceId(str): recurrence id can reference a specific instance within the set. """ + date: Annotated[ + Union[datetime.date, datetime.datetime], + BeforeValidator(parse_date_and_datetime), + ] + """The date (all-day) or datetime of the recurrence to override.""" + + this_and_future: bool = False + """Whether this should affect future occurrence as well.""" + + @property + def range(self) -> Range: + return Range.THIS_AND_FUTURE if self.this_and_future else Range.NONE + + model_config = ConfigDict( + populate_by_name=True, validate_assignment=True, arbitrary_types_allowed=True + ) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, RecurrenceId): + return NotImplemented + return self.date == other.date and self.this_and_future == other.this_and_future + + def __hash__(self) -> int: + return hash((self.date, self.this_and_future)) + + serialize_fields = field_serializer("*")(serialize_field) # type: ignore[pydantic-field] + @classmethod - def to_value(cls, recurrence_id: str) -> datetime.datetime | datetime.date: + def from_value( + cls, + recurrence_id: RecurrenceId | str | datetime.date | datetime.datetime, + this_and_future: Range | bool | None = None, + timezone: datetime.tzinfo | None = None, + ) -> "RecurrenceId": + if isinstance(recurrence_id, RecurrenceId): + date = recurrence_id.date + future = recurrence_id.this_and_future + elif isinstance(recurrence_id, str): + date = cls.string_to_date(recurrence_id) + future = False + else: + date = recurrence_id + future = False + if this_and_future is not None: + if isinstance(this_and_future, Range): + future = this_and_future == Range.THIS_AND_FUTURE + else: + future = this_and_future + if timezone is not None: + if isinstance(date, datetime.datetime): + date = date.astimezone(timezone) + return RecurrenceId(date=date, this_and_future=future) + + @classmethod + def string_to_date(cls, date_str: str) -> datetime.datetime | datetime.date: """Convert a string RecurrenceId into a date or time value.""" errors = [] try: date_value = DateEncoder.__parse_property_value__( - ParsedProperty(name="", value=recurrence_id) + ParsedProperty(name="", value=date_str) ) if date_value: return date_value @@ -163,7 +217,7 @@ def to_value(cls, recurrence_id: str) -> datetime.datetime | datetime.date: try: date_time_value = DateTimeEncoder.__parse_property_value__( - ParsedProperty(name="", value=recurrence_id) + ParsedProperty(name="", value=date_str) ) if date_time_value: return date_time_value @@ -173,27 +227,60 @@ def to_value(cls, recurrence_id: str) -> datetime.datetime | datetime.date: raise ValueError(f"Unable to parse date/time value: {errors}") @classmethod - def __parse_property_value__(cls, value: Any) -> RecurrenceId: - """Parse a calendar user address.""" - if isinstance(value, ParsedProperty): - value = cls._parse_value(value.value) - if isinstance(value, str): - value = cls._parse_value(value) - if isinstance(value, datetime.datetime): - value = DateTimeEncoder.__encode_property_json__(value) - elif isinstance(value, datetime.date): - value = DateEncoder.__encode_property_json__(value) + def __encode_property__(cls, model_data: dict[str, Any]) -> ParsedProperty: + params = [] + + date_value = model_data["date"] + if isinstance(date_value, str): + prop = ParsedProperty(name="recurrence-id", value=date_value) + else: + prop = ParsedProperty(name="recurrence-id", value=date_value["VALUE"]) + if "TZID" in date_value: + params.append( + ParsedPropertyParameter(name="TZID", values=[date_value["TZID"]]) + ) + + date_type = cls.string_to_date(prop.value) + if isinstance(date_type, datetime.datetime): + params.append(ParsedPropertyParameter("VALUE", ["DATE-TIME"])) else: - value = str(value) - return RecurrenceId(value) + params.append(ParsedPropertyParameter("VALUE", ["DATE"])) + + if model_data.get("this_and_future"): + params.append( + ParsedPropertyParameter("RANGE", [Range.THIS_AND_FUTURE.value]) + ) + + if params: + prop.params = params + return prop @classmethod - def _parse_value(cls, value: str) -> datetime.datetime | datetime.date | str: - try: - return cls.to_value(value) - except ValueError: - pass - return str(value) + def __parse_property_value__( + cls, prop: ParsedProperty | dict[str, Any] | datetime.datetime | datetime.date + ) -> RecurrenceId | dict[str, Any]: + if isinstance(prop, dict): + return prop + elif isinstance(prop, datetime.datetime) or isinstance(prop, datetime.date): + return cls.from_value(prop) + elif isinstance(prop, RecurrenceId): + return prop + else: + this_and_future = False + timezone: datetime.tzinfo | None = None + if prop.params: + for param in prop.params: + if param.name == "RANGE" and Range.THIS_AND_FUTURE in param.values: + this_and_future = True + if param.name == "TZID" and param.values: + timezone_value = param.values[0] + if isinstance(timezone_value, str): + timezone = ZoneInfo(timezone_value) + else: + timezone = timezone_value + return cls.from_value( + prop.value, this_and_future=this_and_future, timezone=timezone + ) @classmethod def __get_pydantic_core_schema__( diff --git a/tests/__snapshots__/test_calendar_stream.ambr b/tests/__snapshots__/test_calendar_stream.ambr index bfc11746..0e98453f 100644 --- a/tests/__snapshots__/test_calendar_stream.ambr +++ b/tests/__snapshots__/test_calendar_stream.ambr @@ -915,7 +915,10 @@ 'dtstart': '2022-08-01', 'last_modified': '2022-07-31T19:03:39Z', 'location': '', - 'recurrence_id': '20220901', + 'recurrence_id': dict({ + 'date': '2022-09-01', + 'this_and_future': False, + }), 'rrule': dict({ 'by_month_day': list([ 1, @@ -1952,7 +1955,7 @@ DESCRIPTION: LAST-MODIFIED:20220731T190339Z LOCATION: - RECURRENCE-ID:20220901 + RECURRENCE-ID;VALUE=DATE:20220901 RRULE:FREQ=MONTHLY;BYMONTHDAY=1 SEQUENCE:0 STATUS:CONFIRMED diff --git a/tests/__snapshots__/test_store.ambr b/tests/__snapshots__/test_store.ambr index ecfbea37..107355d9 100644 --- a/tests/__snapshots__/test_store.ambr +++ b/tests/__snapshots__/test_store.ambr @@ -82,19 +82,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Daily meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-08-30T09:00:00', - 'recurrence_id': '20220830T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 30, 9, 0), + 'this_and_future': False, + }), 'summary': 'Daily meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-08-31T09:00:00', - 'recurrence_id': '20220831T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 31, 9, 0), + 'this_and_future': False, + }), 'summary': 'Daily meeting', 'uid': 'mock-uid-1', }), @@ -117,19 +126,28 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05', - 'recurrence_id': '20220905', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 5), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -139,13 +157,19 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -177,7 +201,7 @@ list([ tuple( '2024-01-09', - '20240109', + RecurrenceId(date=FakeDate(2024, 1, 9), this_and_future=False), None, list([ ]), @@ -196,7 +220,7 @@ list([ tuple( '2024-01-09', - '20240109', + RecurrenceId(date=FakeDate(2024, 1, 9), this_and_future=False), None, list([ ]), @@ -216,7 +240,10 @@ list([ dict({ 'due': '2024-01-10', - 'recurrence_id': '20240109', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 9), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -227,7 +254,10 @@ list([ dict({ 'due': '2024-01-12', - 'recurrence_id': '20240111', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 11), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -238,7 +268,10 @@ list([ dict({ 'due': '2024-01-13', - 'recurrence_id': '20240112', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 12), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -262,19 +295,28 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'summary': 'Mondays', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05', - 'recurrence_id': '20220905', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 5), + 'this_and_future': False, + }), 'summary': 'Mondays', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'summary': 'Mondays', 'uid': 'mock-uid-1', }), @@ -284,19 +326,28 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'summary': 'Mondays', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05', - 'recurrence_id': '20220905', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 5), + 'this_and_future': False, + }), 'summary': 'Mondays', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'summary': 'Mondays', 'uid': 'mock-uid-1', }), @@ -306,19 +357,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -328,19 +388,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -350,19 +419,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-26T09:00:00', - 'recurrence_id': '20220926T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 26, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -372,19 +450,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-26T09:00:00', - 'recurrence_id': '20220926T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 26, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -421,19 +508,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-19T09:00:00', - 'recurrence_id': '20220919T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 19, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), @@ -443,19 +539,28 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'summary': 'Mondays [edit]', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'summary': 'Mondays [edit]', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-26', - 'recurrence_id': '20220926', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 26), + 'this_and_future': False, + }), 'summary': 'Mondays [edit]', 'uid': 'mock-uid-1', }), @@ -465,19 +570,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-26T09:00:00', - 'recurrence_id': '20220926T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 26, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), @@ -487,21 +601,30 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'sequence': 0, 'summary': 'Monday event', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-06', - 'recurrence_id': '20220905', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 5), + 'this_and_future': False, + }), 'sequence': 1, 'summary': 'Tuesday event', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'sequence': 0, 'summary': 'Monday event', 'uid': 'mock-uid-1', @@ -512,21 +635,30 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'sequence': 0, 'summary': 'Monday event', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-06', - 'recurrence_id': '20220905', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 5), + 'this_and_future': False, + }), 'sequence': 1, 'summary': 'Tuesday event', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'sequence': 0, 'summary': 'Monday event', 'uid': 'mock-uid-1', @@ -537,19 +669,28 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'summary': 'Monday', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05', - 'recurrence_id': '20220905', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 5), + 'this_and_future': False, + }), 'summary': 'Mondays [edit]', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'summary': 'Mondays [edit]', 'uid': 'mock-uid-1', }), @@ -559,19 +700,28 @@ list([ dict({ 'dtstart': '2022-08-29', - 'recurrence_id': '20220829', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 8, 29), + 'this_and_future': False, + }), 'summary': 'Monday', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05', - 'recurrence_id': '20220905', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 5), + 'this_and_future': False, + }), 'summary': 'Mondays [edit]', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12', - 'recurrence_id': '20220912', + 'recurrence_id': dict({ + 'date': FakeDate(2022, 9, 12), + 'this_and_future': False, + }), 'summary': 'Mondays [edit]', 'uid': 'mock-uid-1', }), @@ -581,19 +731,28 @@ list([ dict({ 'dtstart': '2022-08-30T09:00:00', - 'recurrence_id': '20220830T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 30, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-06T09:00:00', - 'recurrence_id': '20220906T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 6, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-13T09:00:00', - 'recurrence_id': '20220913T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 13, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), @@ -603,19 +762,28 @@ list([ dict({ 'dtstart': '2022-08-30T09:00:00', - 'recurrence_id': '20220830T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 30, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-06T09:00:00', - 'recurrence_id': '20220906T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 6, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-13T09:00:00', - 'recurrence_id': '20220913T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 13, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), @@ -625,19 +793,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-06T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -647,19 +824,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-06T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -669,19 +855,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), @@ -691,19 +886,28 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Team meeting', 'uid': 'mock-uid-1', }), @@ -713,13 +917,19 @@ list([ dict({ 'dtstart': '2022-08-30T09:00:00', - 'recurrence_id': '20220830T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 30, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-06T09:00:00', - 'recurrence_id': '20220906T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 6, 9, 0), + 'this_and_future': False, + }), 'summary': 'Tuesday meeting', 'uid': 'mock-uid-1', }), @@ -850,7 +1060,10 @@ list([ dict({ 'due': '2024-01-07', - 'recurrence_id': '20240106', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 6), + 'this_and_future': False, + }), 'status': , 'summary': 'Wash car (Sa)', 'uid': 'mock-uid-1', @@ -861,7 +1074,10 @@ list([ dict({ 'due': '2024-01-07', - 'recurrence_id': '20240106', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 6), + 'this_and_future': False, + }), 'status': , 'summary': 'Wash car (Sa)', 'uid': 'mock-uid-1', @@ -872,7 +1088,10 @@ list([ dict({ 'due': '2024-01-14', - 'recurrence_id': '20240113', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 13), + 'this_and_future': False, + }), 'status': , 'summary': 'Wash car (Sa)', 'uid': 'mock-uid-1', @@ -883,7 +1102,10 @@ list([ dict({ 'due': '2024-01-22', - 'recurrence_id': '20240121', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 21), + 'this_and_future': False, + }), 'status': , 'summary': 'Wash car (Su)', 'uid': 'mock-uid-1', @@ -902,7 +1124,7 @@ DTSTART:20240121 DUE:20240122 LAST-MODIFIED:20220903T093805Z - RECURRENCE-ID:20240120 + RECURRENCE-ID;VALUE=DATE;RANGE=THISANDFUTURE:20240120 RRULE:FREQ=WEEKLY;COUNT=8;BYDAY=SU SEQUENCE:1 STATUS:NEEDS-ACTION @@ -927,31 +1149,46 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-19T09:00:00', - 'recurrence_id': '20220919T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 19, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-26T09:00:00', - 'recurrence_id': '20220926T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 26, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -961,31 +1198,46 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-19T09:00:00', - 'recurrence_id': '20220919T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 19, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-26T09:00:00', - 'recurrence_id': '20220926T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 26, 9, 0), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -995,31 +1247,46 @@ list([ dict({ 'dtstart': '2022-08-29T09:00:00-07:00', - 'recurrence_id': '20220829T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 8, 29, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-05T09:00:00-07:00', - 'recurrence_id': '20220905T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 5, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-12T09:00:00-07:00', - 'recurrence_id': '20220912T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 12, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-19T09:00:00-07:00', - 'recurrence_id': '20220919T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 19, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), dict({ 'dtstart': '2022-09-26T09:00:00-07:00', - 'recurrence_id': '20220926T090000', + 'recurrence_id': dict({ + 'date': FakeDatetime(2022, 9, 26, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), + 'this_and_future': False, + }), 'summary': 'Monday meeting', 'uid': 'mock-uid-1', }), @@ -1050,7 +1317,10 @@ list([ dict({ 'due': '2024-01-10', - 'recurrence_id': '20240109', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 9), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -1061,7 +1331,10 @@ list([ dict({ 'due': '2024-01-10', - 'recurrence_id': '20240109', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 9), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -1072,7 +1345,10 @@ list([ dict({ 'due': '2024-01-11', - 'recurrence_id': '20240110', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 10), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -1083,7 +1359,10 @@ list([ dict({ 'due': '2024-01-10', - 'recurrence_id': '20240109', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 9), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -1102,7 +1381,10 @@ list([ dict({ 'due': '2024-01-10', - 'recurrence_id': '20240109', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 9), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -1113,7 +1395,10 @@ list([ dict({ 'due': '2024-01-11', - 'recurrence_id': '20240110', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 10), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -1124,7 +1409,10 @@ list([ dict({ 'due': '2024-01-10', - 'recurrence_id': '20240110', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 10), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk cat', 'uid': 'mock-uid-1', @@ -1135,7 +1423,10 @@ list([ dict({ 'due': '2024-01-10', - 'recurrence_id': '20240109', + 'recurrence_id': dict({ + 'date': FakeDate(2024, 1, 9), + 'this_and_future': False, + }), 'status': , 'summary': 'Walk dog', 'uid': 'mock-uid-1', @@ -1155,7 +1446,7 @@ DTSTART:20240109 DUE:20240110 LAST-MODIFIED:20240109T100005Z - RECURRENCE-ID:20240109 + RECURRENCE-ID;VALUE=DATE:20240109 SEQUENCE:1 STATUS:COMPLETED SUMMARY:Walk dog @@ -1189,7 +1480,7 @@ DTSTART:20240109 DUE:20240110 LAST-MODIFIED:20240109T100005Z - RECURRENCE-ID:20240109 + RECURRENCE-ID;VALUE=DATE:20240109 SEQUENCE:1 STATUS:COMPLETED SUMMARY:Walk dog @@ -1202,7 +1493,7 @@ DTSTART:20240109 DUE:20240110 LAST-MODIFIED:20240110T100000Z - RECURRENCE-ID:20240110 + RECURRENCE-ID;VALUE=DATE:20240110 EXDATE:20240109 SEQUENCE:1 STATUS:COMPLETED @@ -1237,7 +1528,7 @@ DTSTART:20240109 DUE:20240110 LAST-MODIFIED:20240109T100005Z - RECURRENCE-ID:20240109 + RECURRENCE-ID;VALUE=DATE:20240109 SEQUENCE:1 STATUS:COMPLETED SUMMARY:Walk dog @@ -1250,7 +1541,7 @@ DTSTART:20240109 DUE:20240110 LAST-MODIFIED:20240110T100000Z - RECURRENCE-ID:20240110 + RECURRENCE-ID;VALUE=DATE:20240110 EXDATE:20240109 SEQUENCE:2 STATUS:COMPLETED diff --git a/tests/examples/__snapshots__/test_ics_examples.ambr b/tests/examples/__snapshots__/test_ics_examples.ambr index 1b3689bc..0639232f 100644 --- a/tests/examples/__snapshots__/test_ics_examples.ambr +++ b/tests/examples/__snapshots__/test_ics_examples.ambr @@ -2,10 +2,10 @@ # name: test_iterate_events[apple_ical] list([ Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 15, 45, tzinfo=datetime.timezone.utc), uid='A409C8CF-31E9-4234-BE8A-6CE2B6BB875A', dtstart=datetime.datetime(2022, 9, 12, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 12, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='New Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 43, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 25, 19, 15, 43, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=None, related_to=[], related=[], resources=[], rrule=None, rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 13, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 13, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20220913T090000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 14, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 14, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20220914T090000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 15, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 15, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20220915T090000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 16, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 16, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20220916T090000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 13, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 13, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2022, 9, 13, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 14, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 14, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2022, 9, 14, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 15, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 15, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2022, 9, 15, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2022, 9, 25, 19, 22, 30, tzinfo=datetime.timezone.utc), uid='E53B06A1-9F72-41D9-9446-68E335D2D4F4', dtstart=datetime.datetime(2022, 9, 16, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), dtend=datetime.datetime(2022, 9, 16, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), duration=None, summary='Daily', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2022, 9, 25, 19, 15, 59, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2022, 9, 24, 17, 6, 16, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2022, 9, 16, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2022, 9, 26, 15, 59, 59, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=1, status=None, transparency=None, url=None, extras=[], alarm=[]), ]) # --- # name: test_iterate_events[extended_values] @@ -14,11 +14,11 @@ # --- # name: test_iterate_events[google_calendar_invalid_offset] list([ - Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 3, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 3, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20030203T170000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 4, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 4, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20030204T170000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 5, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 5, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20030205T170000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 6, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 6, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20030206T170000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 7, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 7, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20030207T170000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 3, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 3, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2003, 2, 3, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 4, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 4, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2003, 2, 4, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 5, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 5, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2003, 2, 5, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 6, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 6, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2003, 2, 6, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 11, 23, 39, 45, tzinfo=datetime.timezone.utc), uid='KOrganizer-854889822.731//199594j26139acq39nj78tbhac@group.calendar.google.com', dtstart=datetime.datetime(2003, 2, 7, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), dtend=datetime.datetime(2003, 2, 7, 19, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), duration=None, summary='Event Summary', attendees=[], categories=[], classification=, comment=[], contacts=[], created=datetime.datetime(2006, 3, 15, 21, 4, 21, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2023, 2, 20, 3, 40, 45, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2003, 2, 7, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=datetime.datetime(2003, 2, 13, 16, 0, tzinfo=datetime.timezone.utc), count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[datetime.datetime(2003, 2, 8, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')), datetime.datetime(2003, 2, 9, 17, 0, tzinfo=zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires'))], request_status=None, sequence=3, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), ]) # --- # name: test_iterate_events[google_calendar_public_holidays] @@ -32,27 +32,27 @@ # --- # name: test_iterate_events[recurring_event] list([ - Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 5), dtend=datetime.date(2025, 5, 6), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20250505', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 6), dtend=datetime.date(2025, 5, 7), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20250506', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 7), dtend=datetime.date(2025, 5, 8), duration=None, summary='Some Modified Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 38, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20250507', related_to=[], related=[], resources=[], rrule=None, rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 8), dtend=datetime.date(2025, 5, 9), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20250508', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 9), dtend=datetime.date(2025, 5, 10), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20250509', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 5), dtend=datetime.date(2025, 5, 6), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.date(2025, 5, 5), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 6), dtend=datetime.date(2025, 5, 7), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.date(2025, 5, 6), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 7), dtend=datetime.date(2025, 5, 8), duration=None, summary='Some Modified Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 38, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.date(2025, 5, 7), this_and_future=False), related_to=[], related=[], resources=[], rrule=None, rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 8), dtend=datetime.date(2025, 5, 9), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.date(2025, 5, 8), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2025, 5, 4, 15, 1, 36, tzinfo=datetime.timezone.utc), uid='v8rukz8ier7ijmn@google.com', dtstart=datetime.date(2025, 5, 9), dtend=datetime.date(2025, 5, 10), duration=None, summary='Some Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2025, 5, 4, 14, 56, 10, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2025, 5, 4, 14, 57, 29, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.date(2025, 5, 9), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='TRANSPARENT', url=None, extras=[], alarm=[]), ]) # --- # name: test_iterate_events[recurring_with_single_change] list([ - Event(dtstamp=datetime.datetime(2026, 2, 2, 1, 0, 20, tzinfo=datetime.timezone.utc), uid='5st6kahlb53s6sdmrgkldms9k2@google.com', dtstart=datetime.datetime(2026, 2, 1, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), dtend=datetime.datetime(2026, 2, 1, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), duration=None, summary='Initial Title', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2026, 2, 2, 0, 57, 49, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2026, 2, 2, 0, 57, 57, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20260201T100000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=3, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2026, 2, 2, 1, 0, 20, tzinfo=datetime.timezone.utc), uid='5st6kahlb53s6sdmrgkldms9k2@google.com', dtstart=datetime.datetime(2026, 2, 2, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), dtend=datetime.datetime(2026, 2, 2, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), duration=None, summary='Edited Title', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2026, 2, 2, 0, 57, 49, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2026, 2, 2, 0, 57, 57, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20260202T100000', related_to=[], related=[], resources=[], rrule=None, rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2026, 2, 2, 1, 0, 20, tzinfo=datetime.timezone.utc), uid='5st6kahlb53s6sdmrgkldms9k2@google.com', dtstart=datetime.datetime(2026, 2, 3, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), dtend=datetime.datetime(2026, 2, 3, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), duration=None, summary='Initial Title', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2026, 2, 2, 0, 57, 49, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2026, 2, 2, 0, 57, 57, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id='20260203T100000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=3, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2026, 2, 2, 1, 0, 20, tzinfo=datetime.timezone.utc), uid='5st6kahlb53s6sdmrgkldms9k2@google.com', dtstart=datetime.datetime(2026, 2, 1, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), dtend=datetime.datetime(2026, 2, 1, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), duration=None, summary='Initial Title', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2026, 2, 2, 0, 57, 49, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2026, 2, 2, 0, 57, 57, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2026, 2, 1, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=3, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2026, 2, 2, 1, 0, 20, tzinfo=datetime.timezone.utc), uid='5st6kahlb53s6sdmrgkldms9k2@google.com', dtstart=datetime.datetime(2026, 2, 2, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), dtend=datetime.datetime(2026, 2, 2, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), duration=None, summary='Edited Title', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2026, 2, 2, 0, 57, 49, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2026, 2, 2, 0, 57, 57, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2026, 2, 2, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), this_and_future=False), related_to=[], related=[], resources=[], rrule=None, rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2026, 2, 2, 1, 0, 20, tzinfo=datetime.timezone.utc), uid='5st6kahlb53s6sdmrgkldms9k2@google.com', dtstart=datetime.datetime(2026, 2, 3, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), dtend=datetime.datetime(2026, 2, 3, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), duration=None, summary='Initial Title', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=datetime.datetime(2026, 2, 2, 0, 57, 49, tzinfo=datetime.timezone.utc), description=None, geo=None, last_modified=datetime.datetime(2026, 2, 2, 0, 57, 57, tzinfo=datetime.timezone.utc), location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2026, 2, 3, 10, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=3, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency='OPAQUE', url=None, extras=[], alarm=[]), ]) # --- # name: test_iterate_events[store_edit_bugs] list([ - Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 4, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 4, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id='20210904T110000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 11, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 11, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id='20210911T110000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 18, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 18, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id='20210918T110000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 25, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 25, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id='20210925T110000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), - Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 10, 2, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 10, 2, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id='20211002T110000', related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 4, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 4, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2021, 9, 4, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 11, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 11, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2021, 9, 11, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 18, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 18, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2021, 9, 18, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 9, 25, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 9, 25, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2021, 9, 25, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), + Event(dtstamp=datetime.datetime(2021, 8, 5, 16, 33, 53, tzinfo=datetime.timezone.utc), uid='HX7KXxJM9ZyvyH8D00BMVP-K9drT@proton.me', dtstart=datetime.datetime(2021, 10, 2, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), dtend=datetime.datetime(2021, 10, 2, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), duration=None, summary='My Event', attendees=[], categories=[], classification=None, comment=[], contacts=[], created=None, description='Example', geo=None, last_modified=None, location=None, organizer=None, priority=None, recurrence_id=RecurrenceId(date=datetime.datetime(2021, 10, 2, 11, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Amsterdam')), this_and_future=False), related_to=[], related=[], resources=[], rrule=Recur(freq=, until=None, count=None, interval=1, by_weekday=[], by_month_day=[], by_month=[], by_setpos=[], wkst=None), rdate=[], exdate=[], request_status=None, sequence=0, status=, transparency=None, url=None, extras=[], alarm=[]), ]) # --- # name: test_parse[apple_ical] @@ -2010,7 +2010,7 @@ SUMMARY:Some Modified Event CREATED:20250504T145610Z LAST-MODIFIED:20250504T145738Z - RECURRENCE-ID:20250507 + RECURRENCE-ID;VALUE=DATE:20250507 SEQUENCE:0 STATUS:CONFIRMED TRANSP:TRANSPARENT @@ -2035,7 +2035,7 @@ SUMMARY:Edited Title CREATED:20260202T005749Z LAST-MODIFIED:20260202T005757Z - RECURRENCE-ID:20260202T100000 + RECURRENCE-ID;TZID=America/New_York;VALUE=DATE-TIME:20260202T100000 SEQUENCE:0 STATUS:CONFIRMED TRANSP:OPAQUE diff --git a/tests/examples/test_ics_examples.py b/tests/examples/test_ics_examples.py index bf2fb3d8..09f182aa 100644 --- a/tests/examples/test_ics_examples.py +++ b/tests/examples/test_ics_examples.py @@ -9,6 +9,7 @@ from syrupy import SnapshotAssertion from ical.calendar_stream import IcsCalendarStream +from ical.types import RecurrenceId TEST_DIR = Path("tests/examples") TEST_FILES = sorted(list(TEST_DIR.glob("testdata/*.ics"))) @@ -44,15 +45,27 @@ def test_recurring_with_single_change() -> None: # Feb 1 - original title assert events[0].summary == "Initial Title" - assert events[0].recurrence_id == "20260201T100000" + assert events[0].recurrence_id == RecurrenceId( + date=datetime.datetime( + 2026, 2, 1, 10, 0, tzinfo=zoneinfo.ZoneInfo(key="America/New_York") + ) + ) assert events[0].dtstart == datetime.datetime(2026, 2, 1, 10, 0, tzinfo=tz) # Feb 2 - edited instance with RECURRENCE-ID assert events[1].summary == "Edited Title" - assert events[1].recurrence_id == "20260202T100000" + assert events[1].recurrence_id == RecurrenceId( + date=datetime.datetime( + 2026, 2, 2, 10, 0, tzinfo=zoneinfo.ZoneInfo(key="America/New_York") + ) + ) assert events[1].dtstart == datetime.datetime(2026, 2, 2, 10, 0, tzinfo=tz) # Feb 3 - original title assert events[2].summary == "Initial Title" - assert events[2].recurrence_id == "20260203T100000" + assert events[2].recurrence_id == RecurrenceId( + date=datetime.datetime( + 2026, 2, 3, 10, 0, tzinfo=zoneinfo.ZoneInfo(key="America/New_York") + ) + ) assert events[2].dtstart == datetime.datetime(2026, 2, 3, 10, 0, tzinfo=tz) diff --git a/tests/test_list.py b/tests/test_list.py index 2a76fb03..013d528c 100644 --- a/tests/test_list.py +++ b/tests/test_list.py @@ -7,7 +7,7 @@ from ical.list import todo_list_view from ical.todo import Todo -from ical.types.recur import Recur +from ical.types.recur import Recur, RecurrenceId def test_empty_list() -> None: @@ -39,7 +39,7 @@ def test_daily_recurring_item_due_today_incomplete(status: str) -> None: assert view[0].summary == todo.summary assert view[0].dtstart == datetime.date(2024, 1, 10) assert view[0].due == datetime.date(2024, 1, 11) - assert view[0].recurrence_id == "20240110" + assert view[0].recurrence_id == RecurrenceId(date=datetime.date(2024, 1, 10)) @pytest.mark.parametrize( @@ -65,7 +65,7 @@ def test_daily_recurring_item_due_tomorrow(status: str) -> None: assert view[0].summary == todo.summary assert view[0].dtstart == datetime.date(2024, 1, 10) assert view[0].due == datetime.date(2024, 1, 11) - assert view[0].recurrence_id == "20240110" + assert view[0].recurrence_id == RecurrenceId(date=datetime.date(2024, 1, 10)) @pytest.mark.parametrize( @@ -93,7 +93,7 @@ def test_daily_recurring_item_due_yesterday(status: str) -> None: assert view[0].summary == todo.summary assert view[0].dtstart == datetime.date(2024, 1, 10) assert view[0].due == datetime.date(2024, 1, 11) - assert view[0].recurrence_id == "20240110" + assert view[0].recurrence_id == RecurrenceId(date=datetime.date(2024, 1, 10)) assert view[0].status == status with freezegun.freeze_time("2024-01-11T08:05:00-05:00"): @@ -103,5 +103,5 @@ def test_daily_recurring_item_due_yesterday(status: str) -> None: assert view[0].summary == todo.summary assert view[0].dtstart == datetime.date(2024, 1, 11) assert view[0].due == datetime.date(2024, 1, 12) - assert view[0].recurrence_id == "20240111" + assert view[0].recurrence_id == RecurrenceId(date=datetime.date(2024, 1, 11)) assert view[0].status == status diff --git a/tests/test_store.py b/tests/test_store.py index 79a4950a..eab580b4 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -22,7 +22,7 @@ from ical.event import Event from ical.todo import Todo from ical.store import EventStore, TodoStore, StoreError -from ical.types.recur import Range, Recur +from ical.types.recur import Range, Recur, RecurrenceId from ical.types import RelationshipType, RelatedTo TZ = zoneinfo.ZoneInfo("America/Los_Angeles") @@ -1605,17 +1605,29 @@ def test_store_edit_year_overrun_edit_once( # Pick an arbitrary event in the series iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 5, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T110000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 12, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event2.dtstart == datetime.datetime( 2024, 10, 12, 11, 0, 0, tzinfo=calendar_tz ) event3 = next(iter) - assert event3.recurrence_id == "20241019T110000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 19, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event3.dtstart == datetime.datetime( 2024, 10, 19, 11, 0, 0, tzinfo=calendar_tz ) @@ -1642,22 +1654,38 @@ def test_store_edit_year_overrun_edit_once( timeline = calendar.timeline_tz(tzinfo=viewer_tz) iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 5, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T110000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 12, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event2.dtstart == datetime.datetime( 2024, 10, 12, 10, 0, 0, tzinfo=calendar_tz ) event3 = next(iter) - assert event3.recurrence_id == "20241019T110000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 19, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event3.dtstart == datetime.datetime( 2024, 10, 19, 11, 0, 0, tzinfo=calendar_tz ) event4 = next(iter) - assert event4.recurrence_id == "20241026T110000" + assert event4.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 26, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event4.dtstart == datetime.datetime( 2024, 10, 26, 11, 0, 0, tzinfo=calendar_tz ) @@ -1683,22 +1711,38 @@ def test_store_edit_year_overrun_edit_once( timeline = calendar.timeline_tz(tzinfo=viewer_tz) iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 5, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T110000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 12, 11, 0, tzinfo=zoneinfo.ZoneInfo(key="Europe/Amsterdam") + ) + ) assert event2.dtstart == datetime.datetime( 2024, 10, 12, 10, 0, 0, tzinfo=calendar_tz ) event3 = next(iter) - assert event3.recurrence_id == "20241019T043000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 19, 4, 30, tzinfo=zoneinfo.ZoneInfo(key="America/New_York") + ) + ) assert event3.dtstart == datetime.datetime( 2024, 10, 19, 10, 30, 0, tzinfo=calendar_tz ) event4 = next(iter) - assert event4.recurrence_id == "20241026T043000" + assert event4.recurrence_id == RecurrenceId( + date=datetime.datetime( + 2024, 10, 26, 4, 30, tzinfo=zoneinfo.ZoneInfo(key="America/New_York") + ) + ) assert event4.dtstart == datetime.datetime( 2024, 10, 26, 10, 30, 0, tzinfo=calendar_tz ) @@ -1732,17 +1776,23 @@ def test_store_edit_year_overrun_edit_this_and_future( # Pick an arbitrary event in the series iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 5, 11, 0, tzinfo=calendar_tz) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T110000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 12, 11, 0, tzinfo=calendar_tz) + ) assert event2.dtstart == datetime.datetime( 2024, 10, 12, 11, 0, 0, tzinfo=calendar_tz ) event3 = next(iter) - assert event3.recurrence_id == "20241019T110000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 19, 11, 0, tzinfo=calendar_tz) + ) assert event3.dtstart == datetime.datetime( 2024, 10, 19, 11, 0, 0, tzinfo=calendar_tz ) @@ -1770,17 +1820,23 @@ def test_store_edit_year_overrun_edit_this_and_future( timeline = calendar.timeline_tz(tzinfo=viewer_tz) iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 5, 11, 0, tzinfo=calendar_tz) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T040000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 12, 4, 0, tzinfo=viewer_tz) + ) assert event2.dtstart == datetime.datetime( 2024, 10, 12, 10, 0, 0, tzinfo=calendar_tz ) event3 = next(iter) - assert event3.recurrence_id == "20241019T040000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 19, 4, 0, tzinfo=viewer_tz) + ) assert event3.dtstart == datetime.datetime( 2024, 10, 19, 10, 0, 0, tzinfo=calendar_tz ) @@ -1814,17 +1870,23 @@ def test_store_edit_year_override_set_floating_dates( # Pick an arbitrary event in the series iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 5, 11, 0, tzinfo=calendar_tz) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T110000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 12, 11, 0, tzinfo=calendar_tz) + ) assert event2.dtstart == datetime.datetime( 2024, 10, 12, 11, 0, 0, tzinfo=calendar_tz ) event3 = next(iter) - assert event3.recurrence_id == "20241019T110000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 19, 11, 0, tzinfo=calendar_tz) + ) assert event3.dtstart == datetime.datetime( 2024, 10, 19, 11, 0, 0, tzinfo=calendar_tz ) @@ -1856,12 +1918,16 @@ def test_store_edit_year_override_set_floating_dates( timeline = calendar.timeline_tz(tzinfo=viewer_tz) iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 5, 11, 0, tzinfo=calendar_tz) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T110000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 12, 11, 0, tzinfo=calendar_tz) + ) assert event2.dtstart == datetime.datetime( 2024, 10, @@ -1871,12 +1937,16 @@ def test_store_edit_year_override_set_floating_dates( 0, ) event3 = next(iter) - assert event3.recurrence_id == "20241019T110000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 19, 11, 0, tzinfo=calendar_tz) + ) assert event3.dtstart == datetime.datetime( 2024, 10, 19, 11, 0, 0, tzinfo=calendar_tz ) event4 = next(iter) - assert event4.recurrence_id == "20241026T110000" + assert event4.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 26, 11, 0, tzinfo=calendar_tz) + ) assert event4.dtstart == datetime.datetime( 2024, 10, 26, 11, 0, 0, tzinfo=calendar_tz ) @@ -1903,18 +1973,26 @@ def test_store_edit_year_override_set_floating_dates( timeline = calendar.timeline_tz(tzinfo=viewer_tz) iter = timeline.active_after(datetime.datetime(2024, 10, 1, tzinfo=viewer_tz)) event1 = next(iter) - assert event1.recurrence_id == "20241005T110000" + assert event1.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 5, 11, 0, tzinfo=calendar_tz) + ) assert event1.dtstart == datetime.datetime( 2024, 10, 5, 11, 0, 0, tzinfo=calendar_tz ) event2 = next(iter) - assert event2.recurrence_id == "20241012T110000" + assert event2.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 12, 11, 0, tzinfo=calendar_tz) + ) assert event2.dtstart == datetime.datetime(2024, 10, 12, 4, 0, 0) event3 = next(iter) - assert event3.recurrence_id == "20241019T043000" + assert event3.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 19, 4, 30) + ) assert event3.dtstart == datetime.datetime(2024, 10, 19, 4, 30, 0) event4 = next(iter) - assert event4.recurrence_id == "20241026T043000" + assert event4.recurrence_id == RecurrenceId( + date=datetime.datetime(2024, 10, 26, 4, 30) + ) assert event4.dtstart == datetime.datetime(2024, 10, 26, 4, 30, 0) diff --git a/tests/test_timeline.py b/tests/test_timeline.py index ca321340..42b5025c 100644 --- a/tests/test_timeline.py +++ b/tests/test_timeline.py @@ -13,7 +13,7 @@ from ical.compat import enable_compat_mode from ical.event import Event from ical.journal import Journal -from ical.types.recur import Recur +from ical.types.recur import Recur, RecurrenceId from ical.timeline import generic_timeline, materialize_timeline, calendar_timeline TZ = zoneinfo.ZoneInfo("America/Regina") @@ -81,31 +81,37 @@ def test_journal_timeline() -> None: ): timeline = generic_timeline([journal], TZ) assert list(timeline) == [ - Journal.model_copy(journal, update={"recurrence_id": "20220807"}), + Journal.model_copy( + journal, + update={"recurrence_id": RecurrenceId(date=datetime.date(2022, 8, 7))}, + ), Journal.model_copy( journal, update={ "dtstart": datetime.date(2022, 8, 8), - "recurrence_id": "20220808", + "recurrence_id": RecurrenceId(date=datetime.date(2022, 8, 8)), }, ), Journal.model_copy( journal, update={ "dtstart": datetime.date(2022, 8, 9), - "recurrence_id": "20220809", + "recurrence_id": RecurrenceId(date=datetime.date(2022, 8, 9)), }, ), ] assert list( timeline.overlapping(datetime.date(2022, 8, 7), datetime.date(2022, 8, 9)) ) == [ - Journal.model_copy(journal, update={"recurrence_id": "20220807"}), + Journal.model_copy( + journal, + update={"recurrence_id": RecurrenceId(date=datetime.date(2022, 8, 7))}, + ), Journal.model_copy( journal, update={ "dtstart": datetime.date(2022, 8, 8), - "recurrence_id": "20220808", + "recurrence_id": RecurrenceId(date=datetime.date(2022, 8, 8)), }, ), ] diff --git a/tests/types/test_recur.py b/tests/types/test_recur.py index 1ab38294..d4b13773 100644 --- a/tests/types/test_recur.py +++ b/tests/types/test_recur.py @@ -40,7 +40,9 @@ def test_recurrence_id_datetime() -> None: ] } ) - assert model.recurrence_id == "20220724T120000" + assert model.recurrence_id == RecurrenceId( + date=datetime.datetime(2022, 7, 24, 12, 0) + ) def test_recurrence_id_date() -> None: @@ -49,7 +51,7 @@ def test_recurrence_id_date() -> None: model = FakeModel.model_validate( {"recurrence_id": [ParsedProperty(name="recurrence_id", value="20220724")]} ) - assert model.recurrence_id == "20220724" + assert model.recurrence_id == RecurrenceId(date=datetime.date(2022, 7, 24)) def test_recurrence_id_ignore_params() -> None: @@ -68,15 +70,17 @@ def test_recurrence_id_ignore_params() -> None: ] } ) - assert model.recurrence_id == "20220724T120000" + assert model.recurrence_id == RecurrenceId( + date=datetime.datetime(2022, 7, 24, 12, 0), this_and_future=True + ) def test_invalid_recurrence_id() -> None: """Test for a recurrence id that is not a valid DATE or DATE-TIME string.""" - model = FakeModel.model_validate( - {"recurrence_id": [ParsedProperty(name="recurrence_id", value="invalid")]} - ) - assert model.recurrence_id == "invalid" + with pytest.raises(CalendarParseError): + FakeModel.model_validate( + {"recurrence_id": [ParsedProperty(name="recurrence_id", value="invalid")]} + ) @pytest.mark.parametrize(