Repository: pimutils/khal
Pull request #559 (2017-01-25 01:10:21)

Fix 556
* [ ] look through khal/khalendar/utils.py if any of the usages of astimezone() are problematic (already checked occurrences in khal/khalendar/event.py


Patch:
diff --git a/.travis.yml b/.travis.yml
index 7e7d70a..9f952e6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,7 @@ python:
     - 3.3
     - 3.4
     - 3.5
+    - 3.6
 
 env:
     - BUILD=tests
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 3da9025..f1ac456 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -7,6 +7,17 @@ Package maintainers and users who have to manually update their installation
 may want to subscribe to `GitHub's tag feed
 <https://github.com/geier/khal/tags.atom>`_.
 
+0.9.1
+=====
+released 2017-01-25
+
+* FIX detecting not understood timezone information failed on python 3.6, this may lead to
+  erroneous offsets in start and end times for those events, as those datetimes
+  were treated as if they were in the system's local time, not as if they are in
+  the (possibly) configured default_timezone.
+
+* python 3.6 is now officially supported
+
 0.9.0
 =====
 released 2017-01-24
diff --git a/doc/source/news.rst b/doc/source/news.rst
index c6f927f..a5ea22f 100644
--- a/doc/source/news.rst
+++ b/doc/source/news.rst
@@ -12,6 +12,7 @@ available as an `rss feed <https://lostpackets.de/khal/index.rss>`_ |rss|.
     :title: khal news
     :link: http://lostpackets.de/khal/
 
+    news/khal091
     news/khal09
     news/khal071
     news/khal084
diff --git a/doc/source/news/khal091.rst b/doc/source/news/khal091.rst
new file mode 100644
index 0000000..bbfe56f
--- /dev/null
+++ b/doc/source/news/khal091.rst
@@ -0,0 +1,29 @@
+khal v0.9.1 released
+====================
+
+.. feed-entry::
+        :date: 2017-01-25
+
+This is a bug fix release for python 3.6.
+
+Under python 3.6, datetimes with timezone information that is missing from the
+icalendar file would be treated if they were in the system's local timezone, not
+as if they were in khal's configured default timezone. This could therefore lead
+to erroneous offsets in start and end times for those events. 
+
+To check if you are affected by this bug, delete khal's database file (usually
+:file:`~/.local/share/khal/khal.db`), rerun khal and watch for error messages
+that look like the one below:
+
+   warning: DTSTART localized in invalid or incomprehensible timezone `FOO` in
+   events/event_dt_local_missing_tz.ics. This could lead to this event being
+   wrongly displayed.
+
+
+All users (of python 3.6) are advised to upgrade as soon as possible.
+
+Get `khal v0.9.1`__ from this site, or from pypi_.
+
+__ https://lostpackets.de/khal/downloads/khal-0.9.1.tar.gz
+
+.. _pypi: https://pypi.python.org/pypi/khal/
diff --git a/khal/khalendar/event.py b/khal/khalendar/event.py
index 20958c6..353719a 100644
--- a/khal/khalendar/event.py
+++ b/khal/khalendar/event.py
@@ -29,7 +29,8 @@ import icalendar
 import pytz
 
 from ..utils import generate_random_uid, datetime_fillin
-from .utils import to_naive_utc, to_unix_time, invalid_timezone, delete_instance
+from .utils import to_naive_utc, to_unix_time, invalid_timezone, delete_instance, \
+    is_aware
 from ..log import logger
 from ..terminal import get_color
 from click import style
@@ -620,11 +621,15 @@ class LocalizedEvent(DatetimeEvent):
             endtz = starttz
         if endtz is None:
             endtz = self._locale['default_timezone']
-        try:
+
+        if is_aware(self._start):
             self._start = self._start.astimezone(starttz)
-            self._end = self._end.astimezone(endtz)
-        except ValueError:
+        else:
             self._start = starttz.localize(self._start)
+
+        if is_aware(self._end):
+            self._end = self._end.astimezone(endtz)
+        else:
             self._end = endtz.localize(self._end)
 
     @property
@@ -634,7 +639,6 @@ class LocalizedEvent(DatetimeEvent):
         """
         return self.start.astimezone(self._locale['local_timezone'])
 
-
     @property
     def end_local(self):
         """
diff --git a/khal/khalendar/utils.py b/khal/khalendar/utils.py
index 27e16bc..c7af5e5 100644
--- a/khal/khalendar/utils.py
+++ b/khal/khalendar/utils.py
@@ -327,3 +327,11 @@ def delete_instance(vevent, instance):
         _add_exdate(vevent, instance)
     elif 'RDATE' in vevent:
         _remove_instance(vevent, instance)
+
+
+def is_aware(dtime):
+    """test if a datetime instance is timezone aware"""
+    if dtime.tzinfo is not None and dtime.tzinfo.utcoffset(dtime) is not None:
+        return True
+    else:
+        return False
diff --git a/setup.py b/setup.py
index ce46d27..5e961ca 100755
--- a/setup.py
+++ b/setup.py
@@ -63,6 +63,7 @@ setup(
         "Programming Language :: Python :: 3.3",
         "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
+        "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3 :: Only",
         "Topic :: Utilities",
         "Topic :: Communications",
diff --git a/tox.ini b/tox.ini
index c57c3c7..fdf4a6a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = {py33, py34, py35}-{tests, tests-negtz, style}
+envlist = {py33, py34, py35, py36}-{tests, tests-negtz, style}
 skip_missing_interpreters = True
 
 [testenv]


Test patch:
diff --git a/tests/khalendar_aux_test.py b/tests/khalendar_aux_test.py
index 6fe9316..ad653f1 100644
--- a/tests/khalendar_aux_test.py
+++ b/tests/khalendar_aux_test.py
@@ -8,6 +8,9 @@ from .utils import _get_text, _get_vevent_file
 
 # FIXME this file is in urgent need of a clean up
 
+BERLIN = pytz.timezone('Europe/Berlin')
+BOGOTA = pytz.timezone('America/Bogota')
+
 # datetime
 event_dt = """BEGIN:VCALENDAR
 CALSCALE:GREGORIAN
@@ -771,3 +774,17 @@ class TestSanitize(object):
         assert vevent['DTEND'].dt - vevent['DTSTART'].dt == timedelta()
         vevent = utils.sanitize(vevent, berlin, '', '')
         assert vevent['DTEND'].dt - vevent['DTSTART'].dt == timedelta(hours=1)
+
+
+class TestIsAware():
+    def test_naive(self):
+        assert utils.is_aware(datetime.now()) is False
+
+    def test_berlin(self):
+        assert utils.is_aware(BERLIN.localize(datetime.now())) is True
+
+    def test_bogota(self):
+        assert utils.is_aware(BOGOTA.localize(datetime.now())) is True
+
+    def test_utc(self):
+        assert utils.is_aware(pytz.UTC.localize(datetime.now())) is True