diff --git a/CHANGES.rst b/CHANGES.rst index c3a86018..5d523cf1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,9 +20,14 @@ New features: Bug fixes: -- CATEGORIES field now accepts strings properly +- CATEGORIES field now accepts a string as argument Ref: #322 [jacadzaca] +- Multivalue FREEBUSY property is now parsed properly + Ref: #27 + [jacadzaca] +- Use non legacy timezone name. + Ref: #567 5.0.10 (unreleased) ------------------- diff --git a/docs/credits.rst b/docs/credits.rst index eedcc24b..e3f8c545 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -17,6 +17,7 @@ icalendar contributors - Jannis Leidel - Jeroen F.J. Laros - Jeroen van Meeuwen (Kolab Systems) +- Jochen Sprickerhof - Jordan Kiang - Klaus Klein - Laurent Lasudry diff --git a/src/icalendar/cal.py b/src/icalendar/cal.py index 2535680f..0fd8ec2b 100644 --- a/src/icalendar/cal.py +++ b/src/icalendar/cal.py @@ -375,19 +375,26 @@ def from_ical(cls, st, multiple=False): if not component: raise ValueError(f'Property "{name}" does not have a parent component.') datetime_names = ('DTSTART', 'DTEND', 'RECURRENCE-ID', 'DUE', - 'FREEBUSY', 'RDATE', 'EXDATE') + 'RDATE', 'EXDATE') try: - if name in datetime_names and 'TZID' in params: - vals = factory(factory.from_ical(vals, params['TZID'])) + if name == 'FREEBUSY': + vals = vals.split(',') + if 'TZID' in params: + parsed_components = [factory(factory.from_ical(val, params['TZID'])) for val in vals] + else: + parsed_components = [factory(factory.from_ical(val)) for val in vals] + elif name in datetime_names and 'TZID' in params: + parsed_components = [factory(factory.from_ical(vals, params['TZID']))] else: - vals = factory(factory.from_ical(vals)) + parsed_components = [factory(factory.from_ical(vals))] except ValueError as e: if not component.ignore_exceptions: raise component.errors.append((uname, str(e))) else: - vals.params = params - component.add(name, vals, encode=0) + for parsed_component in parsed_components: + parsed_component.params = params + component.add(name, parsed_component, encode=0) if multiple: return comps diff --git a/src/icalendar/prop.py b/src/icalendar/prop.py index 42cc321a..83c9048d 100644 --- a/src/icalendar/prop.py +++ b/src/icalendar/prop.py @@ -533,6 +533,11 @@ def __cmp__(self, other): f'Cannot compare vPeriod with {other!r}') return cmp((self.start, self.end), (other.start, other.end)) + def __eq__(self, other): + if not isinstance(other, vPeriod): + return False + return (self.start, self.end) == (other.start, other.end) + def overlaps(self, other): if self.start > other.start: return other.overlaps(self) diff --git a/src/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics b/src/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics new file mode 100644 index 00000000..5986b9f3 --- /dev/null +++ b/src/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_multiple_freebusies.ics @@ -0,0 +1,21 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//davmail.sf.net/NONSGML DavMail Calendar V1.1//EN +METHOD:REPLY +BEGIN:VFREEBUSY +DTSTAMP:20120131T123000Z +ORGANIZER:MAILTO:organizer@domain.tld +DTSTART:20120101T000000Z +DTEND:20120201T000000Z +UID:null +ATTENDEE:MAILTO:attendee@domain.tld +FREEBUSY;FBTYPE=BUSY:20120103T091500Z/20120103T101500Z +FREEBUSY;FBTYPE=BUSY:20120113T130000Z/20120113T150000Z +FREEBUSY;FBTYPE=BUSY:20120116T130000Z/20120116T150000Z +FREEBUSY;FBTYPE=BUSY:20120117T091500Z/20120117T101500Z +FREEBUSY;FBTYPE=BUSY:20120118T160000Z/20120118T163000Z +FREEBUSY;FBTYPE=BUSY:20120124T083000Z/20120124T093000Z +FREEBUSY;FBTYPE=BUSY:20120124T123000Z/20120124T143000Z +FREEBUSY;FBTYPE=BUSY:20120131T091500Z/20120131T101500Z +END:VFREEBUSY +END:VCALENDAR diff --git a/src/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics b/src/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics new file mode 100644 index 00000000..d4f500ba --- /dev/null +++ b/src/icalendar/tests/calendars/issue_27_multiple_periods_in_freebusy_one_freebusy.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//davmail.sf.net/NONSGML DavMail Calendar V1.1//EN +METHOD:REPLY +BEGIN:VFREEBUSY +DTSTAMP:20120131T123000Z +ORGANIZER:MAILTO:organizer@domain.tld +DTSTART:20120101T000000Z +DTEND:20120201T000000Z +UID:null +ATTENDEE:MAILTO:attendee@domain.tld +FREEBUSY;FBTYPE=BUSY:20120103T091500Z/20120103T101500Z,20120113T130000Z/20120113T150000Z,20120116T130000Z/20120116T150000Z,20120117T091500Z/20120117T101500Z,20120118T160000Z/20120118T163000Z,20120124T083000Z/20120124T093000Z,20120124T123000Z/20120124T143000Z,20120131T091500Z/20120131T101500Z +END:VFREEBUSY +END:VCALENDAR diff --git a/src/icalendar/tests/test_issue_27_period.py b/src/icalendar/tests/test_issue_27_period.py new file mode 100644 index 00000000..6d92e5fa --- /dev/null +++ b/src/icalendar/tests/test_issue_27_period.py @@ -0,0 +1,14 @@ +'''Issue #27 - multiple periods + + https://github.com/collective/icalendar/issues/27 +''' +from icalendar import Calendar + +def test_issue_27_multiple_periods(calendars): + free_busy = list(calendars.issue_27_multiple_periods_in_freebusy_multiple_freebusies.walk('VFREEBUSY'))[0] + free_busy_period = free_busy['freebusy'] + print(free_busy['freebusy']) + equivalent_way_of_defining_free_busy = list(calendars.issue_27_multiple_periods_in_freebusy_one_freebusy.walk('VFREEBUSY'))[0] + free_busy_period_equivalent = equivalent_way_of_defining_free_busy['freebusy'] + assert free_busy_period == free_busy_period_equivalent + diff --git a/src/icalendar/tests/test_unit_prop.py b/src/icalendar/tests/test_unit_prop.py index af8a3480..7197392f 100644 --- a/src/icalendar/tests/test_unit_prop.py +++ b/src/icalendar/tests/test_unit_prop.py @@ -513,7 +513,7 @@ def test_prop_TypesFactory(self): vDDDTypes_list = [ - vDDDTypes(pytz.timezone('US/Eastern').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))), + vDDDTypes(pytz.timezone('EST').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))), vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)), vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7, tzinfo=tz.UTC)), vDDDTypes(date(year=2022, month=7, day=22)), diff --git a/src/icalendar/windows_to_olson.py b/src/icalendar/windows_to_olson.py index 52d305c4..383ec7c2 100644 --- a/src/icalendar/windows_to_olson.py +++ b/src/icalendar/windows_to_olson.py @@ -17,7 +17,7 @@ 'Arab Standard Time': 'Asia/Riyadh', 'Arabian Standard Time': 'Asia/Dubai', 'Arabic Standard Time': 'Asia/Baghdad', - 'Argentina Standard Time': 'America/Buenos_Aires', + 'Argentina Standard Time': 'America/Argentina/Buenos_Aires', 'Atlantic Standard Time': 'America/Halifax', 'Azerbaijan Standard Time': 'Asia/Baku', 'Azores Standard Time': 'Atlantic/Azores', @@ -46,7 +46,7 @@ 'Eastern Standard Time (Mexico)': 'America/Cancun', 'Egypt Standard Time': 'Africa/Cairo', 'Ekaterinburg Standard Time': 'Asia/Yekaterinburg', - 'FLE Standard Time': 'Europe/Kiev', + 'FLE Standard Time': 'Europe/Kyiv', 'Fiji Standard Time': 'Pacific/Fiji', 'GMT Standard Time': 'Europe/London', 'GTB Standard Time': 'Europe/Bucharest', @@ -54,7 +54,7 @@ 'Greenland Standard Time': 'America/Godthab', 'Greenwich Standard Time': 'Atlantic/Reykjavik', 'Hawaiian Standard Time': 'Pacific/Honolulu', - 'India Standard Time': 'Asia/Calcutta', + 'India Standard Time': 'Asia/Kolkata', 'Iran Standard Time': 'Asia/Tehran', 'Israel Standard Time': 'Asia/Jerusalem', 'Jordan Standard Time': 'Asia/Amman', @@ -69,10 +69,10 @@ 'Morocco Standard Time': 'Africa/Casablanca', 'Mountain Standard Time': 'America/Denver', 'Mountain Standard Time (Mexico)': 'America/Chihuahua', - 'Myanmar Standard Time': 'Asia/Rangoon', + 'Myanmar Standard Time': 'Asia/Yangon', 'N. Central Asia Standard Time': 'Asia/Novosibirsk', 'Namibia Standard Time': 'Africa/Windhoek', - 'Nepal Standard Time': 'Asia/Katmandu', + 'Nepal Standard Time': 'Asia/Kathmandu', 'New Zealand Standard Time': 'Pacific/Auckland', 'Newfoundland Standard Time': 'America/St_Johns', 'North Asia East Standard Time': 'Asia/Irkutsk', @@ -101,7 +101,7 @@ 'Tokyo Standard Time': 'Asia/Tokyo', 'Tonga Standard Time': 'Pacific/Tongatapu', 'Turkey Standard Time': 'Europe/Istanbul', - 'US Eastern Standard Time': 'America/Indianapolis', + 'US Eastern Standard Time': 'America/Indiana/Indianapolis', 'US Mountain Standard Time': 'America/Phoenix', 'UTC': 'Etc/GMT', 'UTC+12': 'Etc/GMT-12',