Skip to content

Commit c76f0fa

Browse files
Merge pull request #811 from niccokunzmann/issue-218
Fix broken ical with a date as DTSTART in timezone definition
2 parents 4f53a30 + ee54bd5 commit c76f0fa

File tree

5 files changed

+70
-1
lines changed

5 files changed

+70
-1
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ New features:
3030

3131
Bug fixes:
3232

33+
- Fix ``STANDARD`` and ``DAYLIGHT`` components that have a date as ``DTSTART``. See `Issue 218 <https://github.com/collective/icalendar/issues/218>`_
3334
- Move import at the end of ``icalendar.parser`` into a function to mitigate import errors, see `Issue 781 <https://github.com/collective/icalendar/issues/781>`_.
3435
- ``ALTREP``, ``DELEGATED-FROM``, ``DELEGATED-TO``, ``DIR``, ``MEMBER``, and ``SENT-BY`` require double quotes. These are now always added.
3536
- Classify ``CATEGORIES`` as multiple in ``VEVENT``. See `PR 808 <https://github.com/collective/icalendar/pull/808>`_.

src/icalendar/cal.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
vUTCOffset,
4141
)
4242
from icalendar.timezone import TZP, tzp
43-
from icalendar.tools import is_date
43+
from icalendar.tools import is_date, to_datetime
4444

4545
if TYPE_CHECKING:
4646
from icalendar.alarms import Alarms
@@ -1232,6 +1232,8 @@ class Timezone(Component):
12321232
way in which a time zone changes its offset from UTC over time.
12331233
"""
12341234

1235+
subcomponents: list[TimezoneStandard|TimezoneDaylight]
1236+
12351237
name = "VTIMEZONE"
12361238
canonical_order = ("TZID",)
12371239
required = ("TZID",) # it also requires one of components DAYLIGHT and STANDARD
@@ -1359,6 +1361,8 @@ def get_transitions(
13591361
for component in self.walk():
13601362
if type(component) == Timezone:
13611363
continue
1364+
if is_date(component["DTSTART"].dt):
1365+
component.DTSTART = to_datetime(component["DTSTART"].dt)
13621366
assert isinstance(
13631367
component["DTSTART"].dt, datetime
13641368
), "VTIMEZONEs sub-components' DTSTART must be of type datetime, not date"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
BEGIN:VCALENDAR
2+
VERSION:2.0
3+
PRODID:-//TEST//TEST//EN
4+
BEGIN:VTIMEZONE
5+
TZID:UTC+11
6+
BEGIN:STANDARD
7+
DTSTART;VALUE=DATE:20170101
8+
TZNAME:UTC+11
9+
TZOFFSETFROM:+1100
10+
TZOFFSETTO:+1100
11+
END:STANDARD
12+
END:VTIMEZONE
13+
BEGIN:VEVENT
14+
DESCRIPTION:TESTING
15+
DTEND;TZID=UTC+11:20170228T233000
16+
DTSTAMP:20170227T064302Z
17+
DTSTART;TZID=UTC+11:20170228T230000
18+
RESOURCES:Court 4
19+
SEQUENCE:0
20+
STATUS:Confirmed
21+
SUMMARY:TESTIN
22+
UID:1961094_636238800000000000
23+
END:VEVENT
24+
END:VCALENDAR
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Parse the calendar and make sure the timezone is used.
2+
3+
See https://github.com/collective/icalendar/issues/218
4+
"""
5+
from __future__ import annotations
6+
7+
from datetime import datetime, timedelta
8+
from typing import TYPE_CHECKING
9+
10+
import pytest
11+
12+
from icalendar.timezone.tzid import tzid_from_dt
13+
14+
if TYPE_CHECKING:
15+
from icalendar import Event
16+
17+
@pytest.fixture()
18+
def event(calendars) -> Event:
19+
"""The event to check."""
20+
return calendars.issue_218_bad_tzid.events[0]
21+
22+
23+
def test_event_has_start_and_end(event : Event):
24+
"""The calendar should be parsed and the start and end have a timezone."""
25+
assert event.start.replace(tzinfo=None) == datetime(2017, 2, 28, 23, 00)
26+
assert event.end.replace(tzinfo=None) == datetime(2017, 2, 28, 23, 30)
27+
28+
def test_timezone(event:Event):
29+
"""The event uses a timezone."""
30+
assert event.start.tzinfo == event.end.tzinfo
31+
assert tzid_from_dt(event.start) == "UTC+11"
32+
assert event.start.utcoffset() == timedelta(hours=11)

src/icalendar/timezone/zoneinfo.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
from icalendar.tools import is_date, to_datetime
6+
57
try:
68
import zoneinfo
79
except ImportError:
@@ -21,6 +23,7 @@
2123

2224
if TYPE_CHECKING:
2325
from icalendar import cal, prop
26+
from icalendar.prop import vDDDTypes
2427

2528

2629
class ZONEINFO(TZProvider):
@@ -72,6 +75,11 @@ def create_timezone(self, tz: cal.Timezone) -> tzinfo:
7275
for attr in list(sub.keys()):
7376
if attr.lower().startswith("x-"):
7477
sub.pop(attr)
78+
for sub in tz.subcomponents:
79+
start : vDDDTypes = sub.get("DTSTART")
80+
if start and is_date(start.dt):
81+
# ValueError: Unsupported DTSTART param in VTIMEZONE: VALUE=DATE
82+
sub.DTSTART = to_datetime(start.dt)
7583
return self._create_timezone(tz)
7684

7785
def _create_timezone(self, tz: cal.Timezone) -> tzinfo:

0 commit comments

Comments
 (0)