diff --git a/lib/dateutil/__init__.py b/lib/dateutil/__init__.py index 290814cf..1020e729 100644 --- a/lib/dateutil/__init__.py +++ b/lib/dateutil/__init__.py @@ -1,9 +1,10 @@ +# -*- coding: utf-8 -*- """ Copyright (c) 2003-2010 Gustavo Niemeyer -This module offers extensions to the standard python 2.3+ +This module offers extensions to the standard Python datetime module. """ -__author__ = "Gustavo Niemeyer " -__license__ = "PSF License" -__version__ = "1.5" +__author__ = "Tomi Pieviläinen " +__license__ = "Simplified BSD" +__version__ = "2.2" diff --git a/lib/dateutil/easter.py b/lib/dateutil/easter.py index d7944104..d8a38844 100644 --- a/lib/dateutil/easter.py +++ b/lib/dateutil/easter.py @@ -1,11 +1,10 @@ """ Copyright (c) 2003-2007 Gustavo Niemeyer -This module offers extensions to the standard python 2.3+ +This module offers extensions to the standard Python datetime module. """ -__author__ = "Gustavo Niemeyer " -__license__ = "PSF License" +__license__ = "Simplified BSD" import datetime @@ -52,7 +51,7 @@ def easter(year, method=EASTER_WESTERN): """ if not (1 <= method <= 3): - raise ValueError, "invalid method" + raise ValueError("invalid method") # g - Golden year - 1 # c - Century @@ -88,5 +87,5 @@ def easter(year, method=EASTER_WESTERN): p = i-j+e d = 1+(p+27+(p+6)//40)%31 m = 3+(p+26)//30 - return datetime.date(int(y),int(m),int(d)) + return datetime.date(int(y), int(m), int(d)) diff --git a/lib/dateutil/parser.py b/lib/dateutil/parser.py index 5d824e41..aef83623 100644 --- a/lib/dateutil/parser.py +++ b/lib/dateutil/parser.py @@ -2,25 +2,29 @@ """ Copyright (c) 2003-2007 Gustavo Niemeyer -This module offers extensions to the standard python 2.3+ +This module offers extensions to the standard Python datetime module. """ -__author__ = "Gustavo Niemeyer " -__license__ = "PSF License" +from __future__ import unicode_literals +__license__ = "Simplified BSD" + import datetime import string import time import sys import os +import collections try: - from cStringIO import StringIO + from io import StringIO except ImportError: - from StringIO import StringIO + from io import StringIO -import relativedelta -import tz +from six import text_type, binary_type, integer_types + +from . import relativedelta +from . import tz __all__ = ["parse", "parserinfo"] @@ -39,7 +43,7 @@ __all__ = ["parse", "parserinfo"] class _timelex(object): def __init__(self, instream): - if isinstance(instream, basestring): + if isinstance(instream, text_type): instream = StringIO(instream) self.instream = instream self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' @@ -133,12 +137,15 @@ class _timelex(object): def __iter__(self): return self - def next(self): + def __next__(self): token = self.get_token() if token is None: raise StopIteration return token + def next(self): + return self.__next__() # Python 2.x support + def split(cls, s): return list(cls(s)) split = classmethod(split) @@ -155,7 +162,7 @@ class _resultbase(object): for attr in self.__slots__: value = getattr(self, attr) if value is not None: - l.append("%s=%s" % (attr, `value`)) + l.append("%s=%s" % (attr, repr(value))) return "%s(%s)" % (classname, ", ".join(l)) def __repr__(self): @@ -167,7 +174,7 @@ class parserinfo(object): # m from a.m/p.m, t from ISO T separator JUMP = [" ", ".", ",", ";", "-", "/", "'", "at", "on", "and", "ad", "m", "t", "of", - "st", "nd", "rd", "th"] + "st", "nd", "rd", "th"] WEEKDAYS = [("Mon", "Monday"), ("Tue", "Tuesday"), @@ -184,7 +191,7 @@ class parserinfo(object): ("Jun", "June"), ("Jul", "July"), ("Aug", "August"), - ("Sep", "September"), + ("Sep", "Sept", "September"), ("Oct", "October"), ("Nov", "November"), ("Dec", "December")] @@ -298,9 +305,12 @@ class parser(object): if not default: default = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) - res = self._parse(timestr, **kwargs) + + + res, skipped_tokens = self._parse(timestr, **kwargs) + if res is None: - raise ValueError, "unknown string format" + raise ValueError("unknown string format") repl = {} for attr in ["year", "month", "day", "hour", "minute", "second", "microsecond"]: @@ -311,20 +321,20 @@ class parser(object): if res.weekday is not None and not res.day: ret = ret+relativedelta.relativedelta(weekday=res.weekday) if not ignoretz: - if callable(tzinfos) or tzinfos and res.tzname in tzinfos: - if callable(tzinfos): + if isinstance(tzinfos, collections.Callable) or tzinfos and res.tzname in tzinfos: + if isinstance(tzinfos, collections.Callable): tzdata = tzinfos(res.tzname, res.tzoffset) else: tzdata = tzinfos.get(res.tzname) if isinstance(tzdata, datetime.tzinfo): tzinfo = tzdata - elif isinstance(tzdata, basestring): + elif isinstance(tzdata, text_type): tzinfo = tz.tzstr(tzdata) - elif isinstance(tzdata, int): + elif isinstance(tzdata, integer_types): tzinfo = tz.tzoffset(res.tzname, tzdata) else: - raise ValueError, "offset must be tzinfo subclass, " \ - "tz string, or int offset" + raise ValueError("offset must be tzinfo subclass, " \ + "tz string, or int offset") ret = ret.replace(tzinfo=tzinfo) elif res.tzname and res.tzname in time.tzname: ret = ret.replace(tzinfo=tz.tzlocal()) @@ -332,6 +342,10 @@ class parser(object): ret = ret.replace(tzinfo=tz.tzutc()) elif res.tzoffset: ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + + if skipped_tokens: + return ret, skipped_tokens + return ret class _result(_resultbase): @@ -339,7 +353,10 @@ class parser(object): "hour", "minute", "second", "microsecond", "tzname", "tzoffset"] - def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False): + def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, fuzzy_with_tokens=False): + if fuzzy_with_tokens: + fuzzy = True + info = self.info if dayfirst is None: dayfirst = info.dayfirst @@ -347,6 +364,13 @@ class parser(object): yearfirst = info.yearfirst res = self._result() l = _timelex.split(timestr) + + + # keep up with the last token skipped so we can recombine + # consecutively skipped tokens (-2 for when i begins at 0). + last_skipped_token_i = -2 + skipped_tokens = list() + try: # year/month/day list @@ -380,7 +404,7 @@ class parser(object): res.minute = int(s[2:]) elif len_li == 6 or (len_li > 6 and l[i-1].find('.') == 6): # YYMMDD or HHMMSS[.ss] - s = l[i-1] + s = l[i-1] if not ymd and l[i-1].find('.') == -1: ymd.append(info.convertyear(int(s[:2]))) ymd.append(int(s[2:4])) @@ -441,6 +465,17 @@ class parser(object): newidx = info.hms(l[i]) if newidx is not None: idx = newidx + elif i == len_l and l[i-2] == ' ' and info.hms(l[i-3]) is not None: + # X h MM or X m SS + idx = info.hms(l[i-3]) + 1 + if idx == 1: + res.minute = int(value) + if value%1: + res.second = int(60*(value%1)) + elif idx == 2: + res.second, res.microsecond = \ + _parsems(value_repr) + i += 1 elif i+1 < len_l and l[i] == ':': # HH:MM[:SS[.ss]] res.hour = int(value) @@ -585,7 +620,7 @@ class parser(object): # Check for a numbered timezone if res.hour is not None and l[i] in ('+', '-'): - signal = (-1,1)[l[i] == '+'] + signal = (-1, 1)[l[i] == '+'] i += 1 len_li = len(l[i]) if len_li == 4: @@ -618,6 +653,13 @@ class parser(object): if not (info.jump(l[i]) or fuzzy): return None + if last_skipped_token_i == i - 1: + # recombine the tokens + skipped_tokens[-1] += l[i] + else: + # just append + skipped_tokens.append(l[i]) + last_skipped_token_i = i i += 1 # Process year/month/day @@ -687,10 +729,19 @@ class parser(object): if not info.validate(res): return None - return res + + if fuzzy_with_tokens: + return res, tuple(skipped_tokens) + + return res, None DEFAULTPARSER = parser() def parse(timestr, parserinfo=None, **kwargs): + # Python 2.x support: datetimes return their string presentation as + # bytes in 2.x and unicode in 3.x, so it's reasonable to expect that + # the parser will get both kinds. Internally we use unicode only. + if isinstance(timestr, binary_type): + timestr = timestr.decode() if parserinfo: return parser(parserinfo).parse(timestr, **kwargs) else: @@ -743,7 +794,7 @@ class _tzparser(object): if l[i] in ('+', '-'): # Yes, that's right. See the TZ variable # documentation. - signal = (1,-1)[l[i] == '+'] + signal = (1, -1)[l[i] == '+'] i += 1 else: signal = -1 @@ -801,15 +852,15 @@ class _tzparser(object): x.time = int(l[i]) i += 2 if i < len_l: - if l[i] in ('-','+'): - signal = (-1,1)[l[i] == "+"] + if l[i] in ('-', '+'): + signal = (-1, 1)[l[i] == "+"] i += 1 else: signal = 1 res.dstoffset = (res.stdoffset+int(l[i]))*signal elif (l.count(',') == 2 and l[i:].count('/') <= 2 and - not [y for x in l[i:] if x not in (',','/','J','M', - '.','-',':') + not [y for x in l[i:] if x not in (',', '/', 'J', 'M', + '.', '-', ':') for y in x if y not in "0123456789"]): for x in (res.start, res.end): if l[i] == 'J': @@ -865,7 +916,7 @@ class _tzparser(object): except (IndexError, ValueError, AssertionError): return None - + return res diff --git a/lib/dateutil/relativedelta.py b/lib/dateutil/relativedelta.py index 0c72a818..4393bcbc 100644 --- a/lib/dateutil/relativedelta.py +++ b/lib/dateutil/relativedelta.py @@ -1,15 +1,16 @@ """ Copyright (c) 2003-2010 Gustavo Niemeyer -This module offers extensions to the standard python 2.3+ +This module offers extensions to the standard Python datetime module. """ -__author__ = "Gustavo Niemeyer " -__license__ = "PSF License" +__license__ = "Simplified BSD" import datetime import calendar +from six import integer_types + __all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] class weekday(object): @@ -42,7 +43,7 @@ class weekday(object): MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) -class relativedelta: +class relativedelta(object): """ The relativedelta type is based on the specification of the excelent work done by M.-A. Lemburg in his mx.DateTime extension. However, @@ -113,10 +114,9 @@ Here is the behavior of operations with relativedelta: yearday=None, nlyearday=None, hour=None, minute=None, second=None, microsecond=None): if dt1 and dt2: - if not isinstance(dt1, datetime.date) or \ - not isinstance(dt2, datetime.date): - raise TypeError, "relativedelta only diffs datetime/date" - if type(dt1) is not type(dt2): + if (not isinstance(dt1, datetime.date)) or (not isinstance(dt2, datetime.date)): + raise TypeError("relativedelta only diffs datetime/date") + if not type(dt1) == type(dt2): #isinstance(dt1, type(dt2)): if not isinstance(dt1, datetime.datetime): dt1 = datetime.datetime.fromordinal(dt1.toordinal()) elif not isinstance(dt2, datetime.datetime): @@ -172,7 +172,7 @@ Here is the behavior of operations with relativedelta: self.second = second self.microsecond = microsecond - if type(weekday) is int: + if isinstance(weekday, integer_types): self.weekday = weekdays[weekday] else: self.weekday = weekday @@ -185,7 +185,7 @@ Here is the behavior of operations with relativedelta: if yearday > 59: self.leapdays = -1 if yday: - ydayidx = [31,59,90,120,151,181,212,243,273,304,334,366] + ydayidx = [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366] for idx, ydays in enumerate(ydayidx): if yday <= ydays: self.month = idx+1 @@ -195,7 +195,7 @@ Here is the behavior of operations with relativedelta: self.day = yday-ydayidx[idx-1] break else: - raise ValueError, "invalid year day (%d)" % yday + raise ValueError("invalid year day (%d)" % yday) self._fix() @@ -242,9 +242,26 @@ Here is the behavior of operations with relativedelta: else: self.years = 0 - def __radd__(self, other): + def __add__(self, other): + if isinstance(other, relativedelta): + return relativedelta(years=other.years+self.years, + months=other.months+self.months, + days=other.days+self.days, + hours=other.hours+self.hours, + minutes=other.minutes+self.minutes, + seconds=other.seconds+self.seconds, + microseconds=other.microseconds+self.microseconds, + leapdays=other.leapdays or self.leapdays, + year=other.year or self.year, + month=other.month or self.month, + day=other.day or self.day, + weekday=other.weekday or self.weekday, + hour=other.hour or self.hour, + minute=other.minute or self.minute, + second=other.second or self.second, + microsecond=other.microsecond or self.microsecond) if not isinstance(other, datetime.date): - raise TypeError, "unsupported type for add operation" + raise TypeError("unsupported type for add operation") elif self._has_time and not isinstance(other, datetime.datetime): other = datetime.datetime.fromordinal(other.toordinal()) year = (self.year or other.year)+self.years @@ -285,48 +302,31 @@ Here is the behavior of operations with relativedelta: ret += datetime.timedelta(days=jumpdays) return ret + def __radd__(self, other): + return self.__add__(other) + def __rsub__(self, other): return self.__neg__().__radd__(other) - def __add__(self, other): - if not isinstance(other, relativedelta): - raise TypeError, "unsupported type for add operation" - return relativedelta(years=other.years+self.years, - months=other.months+self.months, - days=other.days+self.days, - hours=other.hours+self.hours, - minutes=other.minutes+self.minutes, - seconds=other.seconds+self.seconds, - microseconds=other.microseconds+self.microseconds, - leapdays=other.leapdays or self.leapdays, - year=other.year or self.year, - month=other.month or self.month, - day=other.day or self.day, - weekday=other.weekday or self.weekday, - hour=other.hour or self.hour, - minute=other.minute or self.minute, - second=other.second or self.second, - microsecond=other.second or self.microsecond) - def __sub__(self, other): if not isinstance(other, relativedelta): - raise TypeError, "unsupported type for sub operation" - return relativedelta(years=other.years-self.years, - months=other.months-self.months, - days=other.days-self.days, - hours=other.hours-self.hours, - minutes=other.minutes-self.minutes, - seconds=other.seconds-self.seconds, - microseconds=other.microseconds-self.microseconds, - leapdays=other.leapdays or self.leapdays, - year=other.year or self.year, - month=other.month or self.month, - day=other.day or self.day, - weekday=other.weekday or self.weekday, - hour=other.hour or self.hour, - minute=other.minute or self.minute, - second=other.second or self.second, - microsecond=other.second or self.microsecond) + raise TypeError("unsupported type for sub operation") + return relativedelta(years=self.years-other.years, + months=self.months-other.months, + days=self.days-other.days, + hours=self.hours-other.hours, + minutes=self.minutes-other.minutes, + seconds=self.seconds-other.seconds, + microseconds=self.microseconds-other.microseconds, + leapdays=self.leapdays or other.leapdays, + year=self.year or other.year, + month=self.month or other.month, + day=self.day or other.day, + weekday=self.weekday or other.weekday, + hour=self.hour or other.hour, + minute=self.minute or other.minute, + second=self.second or other.second, + microsecond=self.microsecond or other.microsecond) def __neg__(self): return relativedelta(years=-self.years, @@ -346,7 +346,7 @@ Here is the behavior of operations with relativedelta: second=self.second, microsecond=self.microsecond) - def __nonzero__(self): + def __bool__(self): return not (not self.years and not self.months and not self.days and @@ -366,13 +366,13 @@ Here is the behavior of operations with relativedelta: def __mul__(self, other): f = float(other) - return relativedelta(years=self.years*f, - months=self.months*f, - days=self.days*f, - hours=self.hours*f, - minutes=self.minutes*f, - seconds=self.seconds*f, - microseconds=self.microseconds*f, + return relativedelta(years=int(self.years*f), + months=int(self.months*f), + days=int(self.days*f), + hours=int(self.hours*f), + minutes=int(self.minutes*f), + seconds=int(self.seconds*f), + microseconds=int(self.microseconds*f), leapdays=self.leapdays, year=self.year, month=self.month, @@ -383,6 +383,8 @@ Here is the behavior of operations with relativedelta: second=self.second, microsecond=self.microsecond) + __rmul__ = __mul__ + def __eq__(self, other): if not isinstance(other, relativedelta): return False @@ -415,6 +417,8 @@ Here is the behavior of operations with relativedelta: def __div__(self, other): return self.__mul__(1/float(other)) + __truediv__ = __div__ + def __repr__(self): l = [] for attr in ["years", "months", "days", "leapdays", @@ -426,7 +430,7 @@ Here is the behavior of operations with relativedelta: "hour", "minute", "second", "microsecond"]: value = getattr(self, attr) if value is not None: - l.append("%s=%s" % (attr, `value`)) + l.append("%s=%s" % (attr, repr(value))) return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) # vim:ts=4:sw=4:et diff --git a/lib/dateutil/rrule.py b/lib/dateutil/rrule.py index 6bd83cad..ad4d3ba7 100644 --- a/lib/dateutil/rrule.py +++ b/lib/dateutil/rrule.py @@ -1,18 +1,22 @@ """ Copyright (c) 2003-2010 Gustavo Niemeyer -This module offers extensions to the standard python 2.3+ +This module offers extensions to the standard Python datetime module. """ -__author__ = "Gustavo Niemeyer " -__license__ = "PSF License" +__license__ = "Simplified BSD" import itertools import datetime import calendar -import thread +try: + import _thread +except ImportError: + import thread as _thread import sys +from six import advance_iterator, integer_types + __all__ = ["rrule", "rruleset", "rrulestr", "YEARLY", "MONTHLY", "WEEKLY", "DAILY", "HOURLY", "MINUTELY", "SECONDLY", @@ -22,15 +26,15 @@ __all__ = ["rrule", "rruleset", "rrulestr", M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30+ [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) M365MASK = list(M366MASK) -M29, M30, M31 = range(1,30), range(1,31), range(1,32) +M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) MDAY365MASK = list(MDAY366MASK) -M29, M30, M31 = range(-29,0), range(-30,0), range(-31,0) +M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) NMDAY365MASK = list(NMDAY366MASK) -M366RANGE = (0,31,60,91,121,152,182,213,244,274,305,335,366) -M365RANGE = (0,31,59,90,120,151,181,212,243,273,304,334,365) -WDAYMASK = [0,1,2,3,4,5,6]*55 +M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) +M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) +WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] MDAY365MASK = tuple(MDAY365MASK) M365MASK = tuple(M365MASK) @@ -41,7 +45,7 @@ M365MASK = tuple(M365MASK) DAILY, HOURLY, MINUTELY, - SECONDLY) = range(7) + SECONDLY) = list(range(7)) # Imported on demand. easter = None @@ -52,7 +56,7 @@ class weekday(object): def __init__(self, weekday, n=None): if n == 0: - raise ValueError, "Can't create weekday with n == 0" + raise ValueError("Can't create weekday with n == 0") self.weekday = weekday self.n = n @@ -79,11 +83,11 @@ class weekday(object): MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) -class rrulebase: +class rrulebase(object): def __init__(self, cache=False): if cache: self._cache = [] - self._cache_lock = thread.allocate_lock() + self._cache_lock = _thread.allocate_lock() self._cache_gen = self._iter() self._cache_complete = False else: @@ -112,7 +116,7 @@ class rrulebase: break try: for j in range(10): - cache.append(gen.next()) + cache.append(advance_iterator(gen)) except StopIteration: self._cache_gen = gen = None self._cache_complete = True @@ -133,13 +137,13 @@ class rrulebase: else: return list(itertools.islice(self, item.start or 0, - item.stop or sys.maxint, + item.stop or sys.maxsize, item.step or 1)) elif item >= 0: gen = iter(self) try: for i in range(item+1): - res = gen.next() + res = advance_iterator(gen) except StopIteration: raise IndexError return res @@ -232,7 +236,7 @@ class rrule(rrulebase): byweekno=None, byweekday=None, byhour=None, byminute=None, bysecond=None, cache=False): - rrulebase.__init__(self, cache) + super(rrule, self).__init__(cache) global easter if not dtstart: dtstart = datetime.datetime.now().replace(microsecond=0) @@ -250,13 +254,13 @@ class rrule(rrulebase): self._until = until if wkst is None: self._wkst = calendar.firstweekday() - elif type(wkst) is int: + elif isinstance(wkst, integer_types): self._wkst = wkst else: self._wkst = wkst.weekday if bysetpos is None: self._bysetpos = None - elif type(bysetpos) is int: + elif isinstance(bysetpos, integer_types): if bysetpos == 0 or not (-366 <= bysetpos <= 366): raise ValueError("bysetpos must be between 1 and 366, " "or between -366 and -1") @@ -280,14 +284,14 @@ class rrule(rrulebase): # bymonth if not bymonth: self._bymonth = None - elif type(bymonth) is int: + elif isinstance(bymonth, integer_types): self._bymonth = (bymonth,) else: self._bymonth = tuple(bymonth) # byyearday if not byyearday: self._byyearday = None - elif type(byyearday) is int: + elif isinstance(byyearday, integer_types): self._byyearday = (byyearday,) else: self._byyearday = tuple(byyearday) @@ -295,7 +299,7 @@ class rrule(rrulebase): if byeaster is not None: if not easter: from dateutil import easter - if type(byeaster) is int: + if isinstance(byeaster, integer_types): self._byeaster = (byeaster,) else: self._byeaster = tuple(byeaster) @@ -305,7 +309,7 @@ class rrule(rrulebase): if not bymonthday: self._bymonthday = () self._bynmonthday = () - elif type(bymonthday) is int: + elif isinstance(bymonthday, integer_types): if bymonthday < 0: self._bynmonthday = (bymonthday,) self._bymonthday = () @@ -318,7 +322,7 @@ class rrule(rrulebase): # byweekno if byweekno is None: self._byweekno = None - elif type(byweekno) is int: + elif isinstance(byweekno, integer_types): self._byweekno = (byweekno,) else: self._byweekno = tuple(byweekno) @@ -326,7 +330,7 @@ class rrule(rrulebase): if byweekday is None: self._byweekday = None self._bynweekday = None - elif type(byweekday) is int: + elif isinstance(byweekday, integer_types): self._byweekday = (byweekday,) self._bynweekday = None elif hasattr(byweekday, "n"): @@ -340,7 +344,7 @@ class rrule(rrulebase): self._byweekday = [] self._bynweekday = [] for wday in byweekday: - if type(wday) is int: + if isinstance(wday, integer_types): self._byweekday.append(wday) elif not wday.n or freq > MONTHLY: self._byweekday.append(wday.weekday) @@ -358,7 +362,7 @@ class rrule(rrulebase): self._byhour = (dtstart.hour,) else: self._byhour = None - elif type(byhour) is int: + elif isinstance(byhour, integer_types): self._byhour = (byhour,) else: self._byhour = tuple(byhour) @@ -368,7 +372,7 @@ class rrule(rrulebase): self._byminute = (dtstart.minute,) else: self._byminute = None - elif type(byminute) is int: + elif isinstance(byminute, integer_types): self._byminute = (byminute,) else: self._byminute = tuple(byminute) @@ -378,7 +382,7 @@ class rrule(rrulebase): self._bysecond = (dtstart.second,) else: self._bysecond = None - elif type(bysecond) is int: + elif isinstance(bysecond, integer_types): self._bysecond = (bysecond,) else: self._bysecond = tuple(bysecond) @@ -716,7 +720,7 @@ class _iterinfo(object): # days from last year's last week number in # this year. if -1 not in rr._byweekno: - lyearweekday = datetime.date(year-1,1,1).weekday() + lyearweekday = datetime.date(year-1, 1, 1).weekday() lno1wkst = (7-lyearweekday+rr._wkst)%7 lyearlen = 365+calendar.isleap(year-1) if lno1wkst >= 4: @@ -768,7 +772,7 @@ class _iterinfo(object): self.lastmonth = month def ydayset(self, year, month, day): - return range(self.yearlen), 0, self.yearlen + return list(range(self.yearlen)), 0, self.yearlen def mdayset(self, year, month, day): set = [None]*self.yearlen @@ -823,27 +827,38 @@ class _iterinfo(object): class rruleset(rrulebase): - class _genitem: + class _genitem(object): def __init__(self, genlist, gen): try: - self.dt = gen() + self.dt = advance_iterator(gen) genlist.append(self) except StopIteration: pass self.genlist = genlist self.gen = gen - def next(self): + def __next__(self): try: - self.dt = self.gen() + self.dt = advance_iterator(self.gen) except StopIteration: self.genlist.remove(self) - def __cmp__(self, other): - return cmp(self.dt, other.dt) + next = __next__ + + def __lt__(self, other): + return self.dt < other.dt + + def __gt__(self, other): + return self.dt > other.dt + + def __eq__(self, other): + return self.dt == other.dt + + def __ne__(self, other): + return self.dt != other.dt def __init__(self, cache=False): - rrulebase.__init__(self, cache) + super(rruleset, self).__init__(cache) self._rrule = [] self._rdate = [] self._exrule = [] @@ -851,7 +866,7 @@ class rruleset(rrulebase): def rrule(self, rrule): self._rrule.append(rrule) - + def rdate(self, rdate): self._rdate.append(rdate) @@ -864,14 +879,14 @@ class rruleset(rrulebase): def _iter(self): rlist = [] self._rdate.sort() - self._genitem(rlist, iter(self._rdate).next) - for gen in [iter(x).next for x in self._rrule]: + self._genitem(rlist, iter(self._rdate)) + for gen in [iter(x) for x in self._rrule]: self._genitem(rlist, gen) rlist.sort() exlist = [] self._exdate.sort() - self._genitem(exlist, iter(self._exdate).next) - for gen in [iter(x).next for x in self._exrule]: + self._genitem(exlist, iter(self._exdate)) + for gen in [iter(x) for x in self._exrule]: self._genitem(exlist, gen) exlist.sort() lastdt = None @@ -880,17 +895,17 @@ class rruleset(rrulebase): ritem = rlist[0] if not lastdt or lastdt != ritem.dt: while exlist and exlist[0] < ritem: - exlist[0].next() + advance_iterator(exlist[0]) exlist.sort() if not exlist or ritem != exlist[0]: total += 1 yield ritem.dt lastdt = ritem.dt - ritem.next() + advance_iterator(ritem) rlist.sort() self._len = total -class _rrulestr: +class _rrulestr(object): _freq_map = {"YEARLY": YEARLY, "MONTHLY": MONTHLY, @@ -932,7 +947,7 @@ class _rrulestr: ignoretz=kwargs.get("ignoretz"), tzinfos=kwargs.get("tzinfos")) except ValueError: - raise ValueError, "invalid until date" + raise ValueError("invalid until date") def _handle_WKST(self, rrkwargs, name, value, **kwargs): rrkwargs["wkst"] = self._weekday_map[value] @@ -959,7 +974,7 @@ class _rrulestr: if line.find(':') != -1: name, value = line.split(':') if name != "RRULE": - raise ValueError, "unknown parameter name" + raise ValueError("unknown parameter name") else: value = line rrkwargs = {} @@ -972,9 +987,9 @@ class _rrulestr: ignoretz=ignoretz, tzinfos=tzinfos) except AttributeError: - raise ValueError, "unknown parameter '%s'" % name + raise ValueError("unknown parameter '%s'" % name) except (KeyError, ValueError): - raise ValueError, "invalid '%s': %s" % (name, value) + raise ValueError("invalid '%s': %s" % (name, value)) return rrule(dtstart=dtstart, cache=cache, **rrkwargs) def _parse_rfc(self, s, @@ -991,7 +1006,7 @@ class _rrulestr: unfold = True s = s.upper() if not s.strip(): - raise ValueError, "empty string" + raise ValueError("empty string") if unfold: lines = s.splitlines() i = 0 @@ -1026,36 +1041,36 @@ class _rrulestr: name, value = line.split(':', 1) parms = name.split(';') if not parms: - raise ValueError, "empty property name" + raise ValueError("empty property name") name = parms[0] parms = parms[1:] if name == "RRULE": for parm in parms: - raise ValueError, "unsupported RRULE parm: "+parm + raise ValueError("unsupported RRULE parm: "+parm) rrulevals.append(value) elif name == "RDATE": for parm in parms: if parm != "VALUE=DATE-TIME": - raise ValueError, "unsupported RDATE parm: "+parm + raise ValueError("unsupported RDATE parm: "+parm) rdatevals.append(value) elif name == "EXRULE": for parm in parms: - raise ValueError, "unsupported EXRULE parm: "+parm + raise ValueError("unsupported EXRULE parm: "+parm) exrulevals.append(value) elif name == "EXDATE": for parm in parms: if parm != "VALUE=DATE-TIME": - raise ValueError, "unsupported RDATE parm: "+parm + raise ValueError("unsupported RDATE parm: "+parm) exdatevals.append(value) elif name == "DTSTART": for parm in parms: - raise ValueError, "unsupported DTSTART parm: "+parm + raise ValueError("unsupported DTSTART parm: "+parm) if not parser: from dateutil import parser dtstart = parser.parse(value, ignoretz=ignoretz, tzinfos=tzinfos) else: - raise ValueError, "unsupported property: "+name + raise ValueError("unsupported property: "+name) if (forceset or len(rrulevals) > 1 or rdatevals or exrulevals or exdatevals): if not parser and (rdatevals or exdatevals): diff --git a/lib/dateutil/tz.py b/lib/dateutil/tz.py index 9aeb2b96..e849fc24 100644 --- a/lib/dateutil/tz.py +++ b/lib/dateutil/tz.py @@ -1,11 +1,12 @@ """ Copyright (c) 2003-2007 Gustavo Niemeyer -This module offers extensions to the standard python 2.3+ +This module offers extensions to the standard Python datetime module. """ -__author__ = "Gustavo Niemeyer " -__license__ = "PSF License" +__license__ = "Simplified BSD" + +from six import string_types, PY3 import datetime import struct @@ -25,6 +26,19 @@ try: except (ImportError, OSError): tzwin, tzwinlocal = None, None +def tzname_in_python2(myfunc): + """Change unicode output into bytestrings in Python 2 + + tzname() API changed in Python 3. It used to return bytes, but was changed + to unicode strings + """ + def inner_func(*args, **kwargs): + if PY3: + return myfunc(*args, **kwargs) + else: + return myfunc(*args, **kwargs).encode() + return inner_func + ZERO = datetime.timedelta(0) EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() @@ -36,6 +50,7 @@ class tzutc(datetime.tzinfo): def dst(self, dt): return ZERO + @tzname_in_python2 def tzname(self, dt): return "UTC" @@ -63,6 +78,7 @@ class tzoffset(datetime.tzinfo): def dst(self, dt): return ZERO + @tzname_in_python2 def tzname(self, dt): return self._name @@ -75,7 +91,7 @@ class tzoffset(datetime.tzinfo): def __repr__(self): return "%s(%s, %s)" % (self.__class__.__name__, - `self._name`, + repr(self._name), self._offset.days*86400+self._offset.seconds) __reduce__ = object.__reduce__ @@ -100,6 +116,7 @@ class tzlocal(datetime.tzinfo): else: return ZERO + @tzname_in_python2 def tzname(self, dt): return time.tzname[self._isdst(dt)] @@ -161,7 +178,7 @@ class _ttinfo(object): for attr in self.__slots__: value = getattr(self, attr) if value is not None: - l.append("%s=%s" % (attr, `value`)) + l.append("%s=%s" % (attr, repr(value))) return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) def __eq__(self, other): @@ -191,16 +208,16 @@ class _ttinfo(object): class tzfile(datetime.tzinfo): # http://www.twinsun.com/tz/tz-link.htm - # ftp://elsie.nci.nih.gov/pub/tz*.tar.gz + # ftp://ftp.iana.org/tz/tz*.tar.gz def __init__(self, fileobj): - if isinstance(fileobj, basestring): + if isinstance(fileobj, string_types): self._filename = fileobj - fileobj = open(fileobj) + fileobj = open(fileobj, 'rb') elif hasattr(fileobj, "name"): self._filename = fileobj.name else: - self._filename = `fileobj` + self._filename = repr(fileobj) # From tzfile(5): # @@ -212,8 +229,8 @@ class tzfile(datetime.tzinfo): # ``standard'' byte order (the high-order byte # of the value is written first). - if fileobj.read(4) != "TZif": - raise ValueError, "magic not found" + if fileobj.read(4).decode() != "TZif": + raise ValueError("magic not found") fileobj.read(16) @@ -284,7 +301,7 @@ class tzfile(datetime.tzinfo): for i in range(typecnt): ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) - abbr = fileobj.read(charcnt) + abbr = fileobj.read(charcnt).decode() # Then there are tzh_leapcnt pairs of four-byte # values, written in standard byte order; the @@ -360,7 +377,7 @@ class tzfile(datetime.tzinfo): if not self._trans_list: self._ttinfo_std = self._ttinfo_first = self._ttinfo_list[0] else: - for i in range(timecnt-1,-1,-1): + for i in range(timecnt-1, -1, -1): tti = self._trans_idx[i] if not self._ttinfo_std and not tti.isdst: self._ttinfo_std = tti @@ -448,6 +465,7 @@ class tzfile(datetime.tzinfo): # dst offset, so I belive that this wouldn't be the right # way to implement this. + @tzname_in_python2 def tzname(self, dt): if not self._ttinfo_std: return None @@ -465,11 +483,11 @@ class tzfile(datetime.tzinfo): def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, `self._filename`) + return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) def __reduce__(self): if not os.path.isfile(self._filename): - raise ValueError, "Unpickable %s class" % self.__class__.__name__ + raise ValueError("Unpickable %s class" % self.__class__.__name__) return (self.__class__, (self._filename,)) class tzrange(datetime.tzinfo): @@ -515,6 +533,7 @@ class tzrange(datetime.tzinfo): else: return ZERO + @tzname_in_python2 def tzname(self, dt): if self._isdst(dt): return self._dst_abbr @@ -524,7 +543,7 @@ class tzrange(datetime.tzinfo): def _isdst(self, dt): if not self._start_delta: return False - year = datetime.datetime(dt.year,1,1) + year = datetime.datetime(dt.year, 1, 1) start = year+self._start_delta end = year+self._end_delta dt = dt.replace(tzinfo=None) @@ -561,7 +580,7 @@ class tzstr(tzrange): res = parser._parsetz(s) if res is None: - raise ValueError, "unknown string format" + raise ValueError("unknown string format") # Here we break the compatibility with the TZ variable handling. # GMT-3 actually *means* the timezone -3. @@ -624,9 +643,9 @@ class tzstr(tzrange): return relativedelta.relativedelta(**kwargs) def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, `self._s`) + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) -class _tzicalvtzcomp: +class _tzicalvtzcomp(object): def __init__(self, tzoffsetfrom, tzoffsetto, isdst, tzname=None, rrule=None): self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) @@ -690,51 +709,52 @@ class _tzicalvtz(datetime.tzinfo): else: return ZERO + @tzname_in_python2 def tzname(self, dt): return self._find_comp(dt).tzname def __repr__(self): - return "" % `self._tzid` + return "" % repr(self._tzid) __reduce__ = object.__reduce__ -class tzical: +class tzical(object): def __init__(self, fileobj): global rrule if not rrule: from dateutil import rrule - if isinstance(fileobj, basestring): + if isinstance(fileobj, string_types): self._s = fileobj - fileobj = open(fileobj) + fileobj = open(fileobj, 'r') # ical should be encoded in UTF-8 with CRLF elif hasattr(fileobj, "name"): self._s = fileobj.name else: - self._s = `fileobj` + self._s = repr(fileobj) self._vtz = {} self._parse_rfc(fileobj.read()) def keys(self): - return self._vtz.keys() + return list(self._vtz.keys()) def get(self, tzid=None): if tzid is None: - keys = self._vtz.keys() + keys = list(self._vtz.keys()) if len(keys) == 0: - raise ValueError, "no timezones defined" + raise ValueError("no timezones defined") elif len(keys) > 1: - raise ValueError, "more than one timezone available" + raise ValueError("more than one timezone available") tzid = keys[0] return self._vtz.get(tzid) def _parse_offset(self, s): s = s.strip() if not s: - raise ValueError, "empty offset" + raise ValueError("empty offset") if s[0] in ('+', '-'): - signal = (-1,+1)[s[0]=='+'] + signal = (-1, +1)[s[0]=='+'] s = s[1:] else: signal = +1 @@ -743,12 +763,12 @@ class tzical: elif len(s) == 6: return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal else: - raise ValueError, "invalid offset: "+s + raise ValueError("invalid offset: "+s) def _parse_rfc(self, s): lines = s.splitlines() if not lines: - raise ValueError, "empty string" + raise ValueError("empty string") # Unfold i = 0 @@ -772,7 +792,7 @@ class tzical: name, value = line.split(':', 1) parms = name.split(';') if not parms: - raise ValueError, "empty property name" + raise ValueError("empty property name") name = parms[0].upper() parms = parms[1:] if invtz: @@ -781,7 +801,7 @@ class tzical: # Process component pass else: - raise ValueError, "unknown component: "+value + raise ValueError("unknown component: "+value) comptype = value founddtstart = False tzoffsetfrom = None @@ -791,27 +811,21 @@ class tzical: elif name == "END": if value == "VTIMEZONE": if comptype: - raise ValueError, \ - "component not closed: "+comptype + raise ValueError("component not closed: "+comptype) if not tzid: - raise ValueError, \ - "mandatory TZID not found" + raise ValueError("mandatory TZID not found") if not comps: - raise ValueError, \ - "at least one component is needed" + raise ValueError("at least one component is needed") # Process vtimezone self._vtz[tzid] = _tzicalvtz(tzid, comps) invtz = False elif value == comptype: if not founddtstart: - raise ValueError, \ - "mandatory DTSTART not found" + raise ValueError("mandatory DTSTART not found") if tzoffsetfrom is None: - raise ValueError, \ - "mandatory TZOFFSETFROM not found" + raise ValueError("mandatory TZOFFSETFROM not found") if tzoffsetto is None: - raise ValueError, \ - "mandatory TZOFFSETFROM not found" + raise ValueError("mandatory TZOFFSETFROM not found") # Process component rr = None if rrulelines: @@ -825,8 +839,7 @@ class tzical: comps.append(comp) comptype = None else: - raise ValueError, \ - "invalid component end: "+value + raise ValueError("invalid component end: "+value) elif comptype: if name == "DTSTART": rrulelines.append(line) @@ -835,40 +848,36 @@ class tzical: rrulelines.append(line) elif name == "TZOFFSETFROM": if parms: - raise ValueError, \ - "unsupported %s parm: %s "%(name, parms[0]) + raise ValueError("unsupported %s parm: %s "%(name, parms[0])) tzoffsetfrom = self._parse_offset(value) elif name == "TZOFFSETTO": if parms: - raise ValueError, \ - "unsupported TZOFFSETTO parm: "+parms[0] + raise ValueError("unsupported TZOFFSETTO parm: "+parms[0]) tzoffsetto = self._parse_offset(value) elif name == "TZNAME": if parms: - raise ValueError, \ - "unsupported TZNAME parm: "+parms[0] + raise ValueError("unsupported TZNAME parm: "+parms[0]) tzname = value elif name == "COMMENT": pass else: - raise ValueError, "unsupported property: "+name + raise ValueError("unsupported property: "+name) else: if name == "TZID": if parms: - raise ValueError, \ - "unsupported TZID parm: "+parms[0] + raise ValueError("unsupported TZID parm: "+parms[0]) tzid = value elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): pass else: - raise ValueError, "unsupported property: "+name + raise ValueError("unsupported property: "+name) elif name == "BEGIN" and value == "VTIMEZONE": tzid = None comps = [] invtz = True def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, `self._s`) + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) if sys.platform != "win32": TZFILES = ["/etc/localtime", "localtime"] @@ -914,7 +923,7 @@ def gettz(name=None): for path in TZPATHS: filepath = os.path.join(path, name) if not os.path.isfile(filepath): - filepath = filepath.replace(' ','_') + filepath = filepath.replace(' ', '_') if not os.path.isfile(filepath): continue try: @@ -930,7 +939,7 @@ def gettz(name=None): except OSError: pass if not tz: - from lib.dateutil.zoneinfo import gettz + from dateutil.zoneinfo import gettz tz = gettz(name) if not tz: for c in name: diff --git a/lib/dateutil/tzwin.py b/lib/dateutil/tzwin.py index 073e0ff6..041c6cc3 100644 --- a/lib/dateutil/tzwin.py +++ b/lib/dateutil/tzwin.py @@ -1,9 +1,8 @@ # This code was originally contributed by Jeffrey Harris. import datetime import struct -import _winreg +import winreg -__author__ = "Jeffrey Harris & Gustavo Niemeyer " __all__ = ["tzwin", "tzwinlocal"] @@ -15,9 +14,9 @@ TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" def _settzkeyname(): global TZKEYNAME - handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) try: - _winreg.OpenKey(handle, TZKEYNAMENT).Close() + winreg.OpenKey(handle, TZKEYNAMENT).Close() TZKEYNAME = TZKEYNAMENT except WindowsError: TZKEYNAME = TZKEYNAME9X @@ -49,10 +48,10 @@ class tzwinbase(datetime.tzinfo): def list(): """Return a list of all time zones known to the system.""" - handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) - tzkey = _winreg.OpenKey(handle, TZKEYNAME) - result = [_winreg.EnumKey(tzkey, i) - for i in range(_winreg.QueryInfoKey(tzkey)[0])] + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + tzkey = winreg.OpenKey(handle, TZKEYNAME) + result = [winreg.EnumKey(tzkey, i) + for i in range(winreg.QueryInfoKey(tzkey)[0])] tzkey.Close() handle.Close() return result @@ -79,8 +78,8 @@ class tzwin(tzwinbase): def __init__(self, name): self._name = name - handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) - tzkey = _winreg.OpenKey(handle, "%s\%s" % (TZKEYNAME, name)) + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + tzkey = winreg.OpenKey(handle, "%s\%s" % (TZKEYNAME, name)) keydict = valuestodict(tzkey) tzkey.Close() handle.Close() @@ -118,9 +117,9 @@ class tzwinlocal(tzwinbase): def __init__(self): - handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - tzlocalkey = _winreg.OpenKey(handle, TZLOCALKEYNAME) + tzlocalkey = winreg.OpenKey(handle, TZLOCALKEYNAME) keydict = valuestodict(tzlocalkey) tzlocalkey.Close() @@ -128,7 +127,7 @@ class tzwinlocal(tzwinbase): self._dstname = keydict["DaylightName"].encode("iso-8859-1") try: - tzkey = _winreg.OpenKey(handle, "%s\%s"%(TZKEYNAME, self._stdname)) + tzkey = winreg.OpenKey(handle, "%s\%s"%(TZKEYNAME, self._stdname)) _keydict = valuestodict(tzkey) self._display = _keydict["Display"] tzkey.Close() @@ -165,7 +164,7 @@ def picknthweekday(year, month, dayofweek, hour, minute, whichweek): """dayofweek == 0 means Sunday, whichweek 5 means last instance""" first = datetime.datetime(year, month, 1, hour, minute) weekdayone = first.replace(day=((dayofweek-first.isoweekday())%7+1)) - for n in xrange(whichweek): + for n in range(whichweek): dt = weekdayone+(whichweek-n)*ONEWEEK if dt.month == month: return dt @@ -173,8 +172,8 @@ def picknthweekday(year, month, dayofweek, hour, minute, whichweek): def valuestodict(key): """Convert a registry key's values to a dictionary.""" dict = {} - size = _winreg.QueryInfoKey(key)[1] + size = winreg.QueryInfoKey(key)[1] for i in range(size): - data = _winreg.EnumValue(key, i) + data = winreg.EnumValue(key, i) dict[data[0]] = data[1] return dict diff --git a/lib/dateutil/zoneinfo/.gitignore b/lib/dateutil/zoneinfo/.gitignore deleted file mode 100644 index 335ec957..00000000 --- a/lib/dateutil/zoneinfo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.tar.gz diff --git a/lib/dateutil/zoneinfo/__init__.py b/lib/dateutil/zoneinfo/__init__.py index fc9734d7..81db1405 100644 --- a/lib/dateutil/zoneinfo/__init__.py +++ b/lib/dateutil/zoneinfo/__init__.py @@ -1,15 +1,19 @@ +# -*- coding: utf-8 -*- """ Copyright (c) 2003-2005 Gustavo Niemeyer -This module offers extensions to the standard python 2.3+ +This module offers extensions to the standard Python datetime module. """ -from lib.dateutil.tz import tzfile -from tarfile import TarFile +import logging import os +from subprocess import call +from tarfile import TarFile -__author__ = "Gustavo Niemeyer " -__license__ = "PSF License" +from dateutil.tz import tzfile + +__author__ = "Tomi Pieviläinen " +__license__ = "Simplified BSD" __all__ = ["setcachesize", "gettz", "rebuild"] @@ -21,8 +25,7 @@ class tzfile(tzfile): return (gettz, (self._filename,)) def getzoneinfofile(): - filenames = os.listdir(os.path.join(os.path.dirname(__file__))) - filenames.sort() + filenames = sorted(os.listdir(os.path.join(os.path.dirname(__file__)))) filenames.reverse() for entry in filenames: if entry.startswith("zoneinfo") and ".tar." in entry: @@ -58,6 +61,11 @@ def gettz(name): return tzinfo def rebuild(filename, tag=None, format="gz"): + """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* + + filename is the timezone tarball from ftp.iana.org/tz. + + """ import tempfile, shutil tmpdir = tempfile.mkdtemp() zonedir = os.path.join(tmpdir, "zoneinfo") @@ -66,13 +74,27 @@ def rebuild(filename, tag=None, format="gz"): targetname = "zoneinfo%s.tar.%s" % (tag, format) try: tf = TarFile.open(filename) - for name in tf.getnames(): + # The "backwards" zone file contains links to other files, so must be + # processed as last + for name in sorted(tf.getnames(), + key=lambda k: k != "backward" and k or "z"): if not (name.endswith(".sh") or name.endswith(".tab") or name == "leapseconds"): tf.extract(name, tmpdir) filepath = os.path.join(tmpdir, name) - os.system("zic -d %s %s" % (zonedir, filepath)) + try: + # zic will return errors for nontz files in the package + # such as the Makefile or README, so check_call cannot + # be used (or at least extra checks would be needed) + call(["zic", "-d", zonedir, filepath]) + except OSError as e: + if e.errno == 2: + logging.error( + "Could not find zic. Perhaps you need to install " + "libc-bin or some other package that provides it, " + "or it's not in your PATH?") + raise tf.close() target = os.path.join(moduledir, targetname) for entry in os.listdir(moduledir): diff --git a/sickbeard/network_timezones.py b/sickbeard/network_timezones.py index ab5d7172..77f80fac 100644 --- a/sickbeard/network_timezones.py +++ b/sickbeard/network_timezones.py @@ -261,7 +261,7 @@ def parse_date_time(d, t, network): foreign_timezone = get_network_timezone(network, network_dict) foreign_naive = datetime.datetime(te.year, te.month, te.day, hr, m, tzinfo=foreign_timezone) try: - return foreign_naive.astimezone(sb_timezone) + return foreign_naive.replace(tzinfo=sb_timezone).astimezone(sb_timezone) except (ValueError): return foreign_naive