-
Notifications
You must be signed in to change notification settings - Fork 209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] When importing events preserve VTIMEZONES #513
Changes from all commits
2b069ed
07a2b5b
fb39dd9
e550dae
59f42b0
8f66019
43a5388
67bcf1e
74434bd
8ca650d
83be6b5
7385239
7b360dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
khal v0.8.4 released | ||
==================== | ||
|
||
.. feed-entry:: | ||
:date: 2016-10-06 | ||
|
||
`khal v0.8.4`_ (pypi_) is a bugfix release that fixes a **critical bug** in `khal | ||
import`. **All users are advised to upgrade as soon as possible**. | ||
|
||
Details | ||
~~~~~~~ | ||
If importing events from `.ics` files, any VTIMEZONEs (specifications of the | ||
timezone) would *not* be imported with those events. | ||
As khal understands Olson DB timezone specifiers (such as "Europe/Berlin" or | ||
"America/New_York", events using those timezones are displayed in the correct | ||
timezone, but all other events are displayed as if they were in the configured | ||
*default timezone*. | ||
**This can lead to imported events being shown at wrong times!** | ||
|
||
|
||
Solution | ||
~~~~~~~~ | ||
First, please upgrade khal to either v0.8.4 or, if you are using a version of khal directly | ||
from the git repository, upgrade to the latest version from github_. | ||
|
||
To see if you are affected by this bug, delete your local khal caching db, | ||
(usually `~/.local/share/khal/khal.db`), re-run khal and watch out for lines | ||
looking like this: | ||
``warning: $PROPERTY has invalid or incomprehensible timezone information in | ||
$long_uid.ics in $my_collection``. | ||
You will then need to edit these files by hand and either replace the timezone | ||
identifiers with the corresponding one from the Olson DB (e.g., change | ||
`Europe_Berlin` to `Europe/Berlin`) or copy original VTIMZONE definition in. | ||
|
||
If you have any problems with this, please either open an `issue at github`_ or come into | ||
our `irc channel`_ (`#pimutils` on Freenode). | ||
|
||
We are sorry for any inconveniences this is causing you! | ||
|
||
|
||
.. _khal v0.8.4: https://lostpackets.de/khal/downloads/khal-0.8.4.tar.gz | ||
.. _github: https://github.com/pimutils/khal/ | ||
.. _issue at github: https://github.com/pimutils/khal/issues | ||
.. _pypi: https://pypi.python.org/pypi/khal/ | ||
.. _irc channel: irc://#pimutils@Freenode |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
strings to date(time) or event objects""" | ||
|
||
from calendar import isleap | ||
from collections import defaultdict | ||
from datetime import date, datetime, timedelta, time | ||
import random | ||
import string | ||
|
@@ -37,12 +38,14 @@ | |
|
||
|
||
def timefstr(dtime_list, timeformat): | ||
"""converts a time (as a string) to a datetimeobject | ||
"""converts the first item of a list (a time as a string) to a datetimeobject | ||
|
||
the date is today | ||
where the date is today and the time is given by the a string | ||
removes "used" elements of list | ||
|
||
:returns: datetimeobject | ||
:type dtime_list: list(str) | ||
:type timeformat: str | ||
:rtype: datetime.datetime | ||
""" | ||
if len(dtime_list) == 0: | ||
raise ValueError() | ||
|
@@ -590,19 +593,85 @@ def new_event(locale, dtstart=None, dtend=None, summary=None, timezone=None, | |
return event | ||
|
||
|
||
def ics_from_list(vevent, random_uid=False): | ||
"""convert an iterable of icalendar.Event to an icalendar.Calendar | ||
def split_ics(ics, random_uid=False): | ||
"""split an ics string into several according to VEVENT's UIDs | ||
|
||
and sort the right VTIMEZONEs accordingly | ||
ignores all other ics components | ||
:type ics: str | ||
:param random_uid: assign random uids to all events | ||
:type random_uid: bool | ||
:rtype list: | ||
""" | ||
cal = icalendar.Calendar.from_ical(ics) | ||
tzs = {item['TZID']: item for item in cal.walk() if item.name == 'VTIMEZONE'} | ||
|
||
events_grouped = defaultdict(list) | ||
for item in cal.walk(): | ||
if item.name == 'VEVENT': | ||
events_grouped[item['UID']].append(item) | ||
else: | ||
continue | ||
return [ics_from_list(events, tzs, random_uid) for uid, events in | ||
sorted(events_grouped.items())] | ||
|
||
|
||
def ics_from_list(events, tzs, random_uid=False): | ||
"""convert an iterable of icalendar.Events to an icalendar.Calendar | ||
|
||
:param random_uid: asign the same random UID to all events | ||
:params events: list of events all with the same uid | ||
:type events: list(icalendar.cal.Event) | ||
:param random_uid: assign random uids to all events | ||
:type random_uid: bool | ||
:param tzs: collection of timezones | ||
:type tzs: dict(icalendar.cal.Vtimzone | ||
""" | ||
calendar = icalendar.Calendar() | ||
calendar.add('version', '2.0') | ||
calendar.add('prodid', '-//CALENDARSERVER.ORG//NONSGML Version 1//EN') | ||
|
||
if random_uid: | ||
new_uid = icalendar.vText(generate_random_uid()) | ||
for sub_event in vevent: | ||
new_uid = generate_random_uid() | ||
|
||
needed_tz, missing_tz = set(), set() | ||
for sub_event in events: | ||
if random_uid: | ||
sub_event['uid'] = new_uid | ||
sub_event['UID'] = new_uid | ||
# icalendar round-trip converts `TZID=a b` to `TZID="a b"` investigate, file bug XXX | ||
for prop in ['DTSTART', 'DTEND', 'DUE', 'EXDATE', 'RDATE', 'RECURRENCE-ID', 'DUE']: | ||
if isinstance(sub_event.get(prop), list): | ||
items = sub_event.get(prop) | ||
else: | ||
items = [sub_event.get(prop)] | ||
|
||
for item in items: | ||
if not (hasattr(item, 'dt') or hasattr(item, 'dts')): | ||
continue | ||
# if prop is a list, all items have the same parameters | ||
datetime_ = item.dts[0].dt if hasattr(item, 'dts') else item.dt | ||
|
||
if not hasattr(datetime_, 'tzinfo'): | ||
continue | ||
|
||
# check for datetimes' timezones which are not understood by | ||
# icalendar | ||
if datetime_.tzinfo is None and 'TZID' in item.params and \ | ||
item.params['TZID'] not in missing_tz: | ||
logger.warn( | ||
'Cannot find timezone `{}` in .ics file, using default timezone. ' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think khal should print the event and ask for the tz There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably a good idea, not sure if we need to address it with this PR though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be useful as a separate command, like I agree that it's best left outside the scope of this issue (so we can close this bug faster). |
||
'This can lead to erroneous time shifts'.format(item.params['TZID']) | ||
) | ||
missing_tz.add(item.params['TZID']) | ||
elif datetime_.tzinfo != pytz.UTC: | ||
needed_tz.add(datetime_.tzinfo) | ||
|
||
for tzid in needed_tz: | ||
if str(tzid) in tzs: | ||
calendar.add_component(tzs[str(tzid)]) | ||
else: | ||
logger.warn( | ||
'Cannot find timezone `{}` in .ics file, this could be a bug, ' | ||
'please report this issue at http://github.com/pimutils/khal/.'.format(tzid)) | ||
for sub_event in events: | ||
calendar.add_component(sub_event) | ||
return calendar | ||
return calendar.to_ical().decode('utf-8') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated change, also why is that API designed like that? Why not take just a time as a string? Why does it delete the item from the list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for consistency reason with the other
xfstr()
s