diff options
author | Jelmer Vernooij <jelmer@debian.org> | 2016-09-04 16:45:20 +0000 |
---|---|---|
committer | Jelmer Vernooij <jelmer@debian.org> | 2016-09-04 16:45:20 +0000 |
commit | 679a86643caa9c35ff54b06b971560823c6f5f27 (patch) | |
tree | e1dcd0d397460b2a15a7961558d51681716332b5 /vobject/icalendar.py | |
parent | dacd5347ed80eaf24adc1790d369ee334810af40 (diff) | |
parent | f2c6c8c4c36c6f75072e1015cd0213bde2aaa086 (diff) |
Merge tag 'upstream/0.9.3'
Upstream version 0.9.3
Diffstat (limited to 'vobject/icalendar.py')
-rw-r--r-- | vobject/icalendar.py | 75 |
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: |