summaryrefslogtreecommitdiff
path: root/vobject/icalendar.py
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@debian.org>2016-09-04 16:45:20 +0000
committerJelmer Vernooij <jelmer@debian.org>2016-09-04 16:45:20 +0000
commit679a86643caa9c35ff54b06b971560823c6f5f27 (patch)
treee1dcd0d397460b2a15a7961558d51681716332b5 /vobject/icalendar.py
parentdacd5347ed80eaf24adc1790d369ee334810af40 (diff)
parentf2c6c8c4c36c6f75072e1015cd0213bde2aaa086 (diff)
Merge tag 'upstream/0.9.3'
Upstream version 0.9.3
Diffstat (limited to 'vobject/icalendar.py')
-rw-r--r--vobject/icalendar.py75
1 files changed, 58 insertions, 17 deletions
diff --git a/vobject/icalendar.py b/vobject/icalendar.py
index c49dbbb..bfb00df 100644
--- a/vobject/icalendar.py
+++ b/vobject/icalendar.py
@@ -2,18 +2,36 @@
from __future__ import print_function
+import codecs
import datetime
+import logging
import random # for generating a UID
-import six
import socket
import string
from dateutil import rrule, tz
+import six
+
+try:
+ import pytz
+except ImportError:
+ class Pytz:
+ """fake pytz module (pytz is not required)"""
+
+ class AmbiguousTimeError(Exception):
+ """pytz error for ambiguous times
+ during transition daylight->standard"""
+
+ class NonExistentTimeError(Exception):
+ """pytz error for non-existent times
+ during transition standard->daylight"""
+
+ pytz = Pytz # keeps quantifiedcode happy
from . import behavior
from .base import (VObjectError, NativeError, ValidateError, ParseError,
- Component, ContentLine, logger, registerBehavior,
- backslashEscape, foldOneLine, str_)
+ Component, ContentLine, logger, registerBehavior,
+ backslashEscape, foldOneLine, str_)
# ------------------------------- Constants ------------------------------------
@@ -59,9 +77,9 @@ def getTzid(tzid, smart=True):
tz = timezone(tzid)
registerTzid(toUnicode(tzid), tz)
except UnknownTimeZoneError as e:
- print("Error: {0}".format(e))
+ logging.error(e)
except ImportError as e:
- print("Error: {0}".format(e))
+ logging.error(e)
return tz
utc = tz.tzutc()
@@ -203,17 +221,26 @@ class TimezoneComponent(Component):
working[transitionTo] = None
else:
# an offset transition was found
- old_offset = tzinfo.utcoffset(transition - twoHours)
+ try:
+ old_offset = tzinfo.utcoffset(transition - twoHours)
+ name = tzinfo.tzname(transition)
+ offset = tzinfo.utcoffset(transition)
+ except (pytz.AmbiguousTimeError, pytz.NonExistentTimeError):
+ # guaranteed that tzinfo is a pytz timezone
+ is_dst = (transitionTo == "daylight")
+ old_offset = tzinfo.utcoffset(transition - twoHours, is_dst=is_dst)
+ name = tzinfo.tzname(transition, is_dst=is_dst)
+ offset = tzinfo.utcoffset(transition, is_dst=is_dst)
rule = {'end' : None, # None, or an integer year
'start' : transition, # the datetime of transition
'month' : transition.month,
'weekday' : transition.weekday(),
'hour' : transition.hour,
- 'name' : tzinfo.tzname(transition),
+ 'name' : name,
'plus' : int(
(transition.day - 1)/ 7 + 1), # nth week of the month
'minus' : fromLastWeek(transition), # nth from last week
- 'offset' : tzinfo.utcoffset(transition),
+ 'offset' : offset,
'offsetfrom' : old_offset}
if oldrule is None:
@@ -402,11 +429,11 @@ class RecurringComponent(Component):
dtstart = self.due.value
else:
# if there's no dtstart, just return None
- print('failed to get dtstart with VTODO')
+ logging.error('failed to get dtstart with VTODO')
return None
except (AttributeError, KeyError):
# if there's no due, just return None
- print('failed to find DUE at all.')
+ logging.error('failed to find DUE at all.')
return None
# a Ruby iCalendar library escapes semi-colons in rrules,
@@ -606,7 +633,7 @@ class TextBehavior(behavior.Behavior):
if line.encoded:
encoding = getattr(line, 'encoding_param', None)
if encoding and encoding.upper() == cls.base64string:
- line.value = line.value.decode('base64')
+ line.value = codecs.decode(self.value.encode("utf-8"), "base64").decode(encoding)
else:
line.value = stringToTextValues(line.value)[0]
line.encoded=False
@@ -619,7 +646,7 @@ class TextBehavior(behavior.Behavior):
if not line.encoded:
encoding = getattr(line, 'encoding_param', None)
if encoding and encoding.upper() == cls.base64string:
- line.value = line.value.encode('base64').replace('\n', '')
+ line.value = codecs.encode(self.value.encode(encoding), "base64").decode("utf-8")
else:
line.value = backslashEscape(str_(line.value))
line.encoded=True
@@ -1620,7 +1647,7 @@ def stringToTextValues(s, listSeparator=',', charList=None, strict=False):
if strict:
raise ParseError(msg)
else:
- print(msg)
+ logging.error(msg)
# vars which control state machine
charIterator = enumerate(s)
@@ -1672,7 +1699,8 @@ def stringToTextValues(s, listSeparator=',', charList=None, strict=False):
else:
state = "error"
- error("error: unknown state: '{0!s}' reached in {1!s}".format(state, s))
+ error("unknown state: '{0!s}' reached in {1!s}".format(state, s))
+
def stringToDurations(s, strict=False):
"""
@@ -1789,7 +1817,8 @@ def stringToDurations(s, strict=False):
else:
state = "error"
- error("error: unknown state: '{0!s}' reached in {1!s}".format(state, s))
+ error("unknown state: '{0!s}' reached in {1!s}".format(state, s))
+
def parseDtstart(contentline, allowSignatureMismatch=False):
"""
@@ -1862,9 +1891,21 @@ def getTransition(transitionTo, year, tzinfo):
assert transitionTo in ('daylight', 'standard')
if transitionTo == 'daylight':
- def test(dt): return tzinfo.dst(dt) != zeroDelta
+ def test(dt):
+ try:
+ return tzinfo.dst(dt) != zeroDelta
+ except pytz.NonExistentTimeError:
+ return True # entering daylight time
+ except pytz.AmbiguousTimeError:
+ return False # entering standard time
elif transitionTo == 'standard':
- def test(dt): return tzinfo.dst(dt) == zeroDelta
+ def test(dt):
+ try:
+ return tzinfo.dst(dt) == zeroDelta
+ except pytz.NonExistentTimeError:
+ return False # entering daylight time
+ except pytz.AmbiguousTimeError:
+ return True # entering standard time
newyear = datetime.datetime(year, 1, 1)
monthDt = firstTransition(generateDates(year), test)
if monthDt is None: