1
0
mirror of https://github.com/moparisthebest/SickRage synced 2024-08-13 16:53:54 -04:00

Fix for incorrect date being set inside SickRage due to date not probally being converted back to local timezone.

This commit is contained in:
echel0n 2014-05-15 04:40:30 -07:00
parent c1360717fd
commit 79b239bbce
10 changed files with 343 additions and 244 deletions

View File

@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
""" """
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net> Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard Python
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" __author__ = "Tomi Pieviläinen <tomi.pievilainen@iki.fi>"
__license__ = "PSF License" __license__ = "Simplified BSD"
__version__ = "1.5" __version__ = "2.2"

View File

@ -1,11 +1,10 @@
""" """
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net> Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard Python
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" __license__ = "Simplified BSD"
__license__ = "PSF License"
import datetime import datetime
@ -52,7 +51,7 @@ def easter(year, method=EASTER_WESTERN):
""" """
if not (1 <= method <= 3): if not (1 <= method <= 3):
raise ValueError, "invalid method" raise ValueError("invalid method")
# g - Golden year - 1 # g - Golden year - 1
# c - Century # c - Century
@ -88,5 +87,5 @@ def easter(year, method=EASTER_WESTERN):
p = i-j+e p = i-j+e
d = 1+(p+27+(p+6)//40)%31 d = 1+(p+27+(p+6)//40)%31
m = 3+(p+26)//30 m = 3+(p+26)//30
return datetime.date(int(y),int(m),int(d)) return datetime.date(int(y), int(m), int(d))

View File

@ -2,25 +2,29 @@
""" """
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net> Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard Python
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" from __future__ import unicode_literals
__license__ = "PSF License" __license__ = "Simplified BSD"
import datetime import datetime
import string import string
import time import time
import sys import sys
import os import os
import collections
try: try:
from cStringIO import StringIO from io import StringIO
except ImportError: except ImportError:
from StringIO import StringIO from io import StringIO
import relativedelta from six import text_type, binary_type, integer_types
import tz
from . import relativedelta
from . import tz
__all__ = ["parse", "parserinfo"] __all__ = ["parse", "parserinfo"]
@ -39,7 +43,7 @@ __all__ = ["parse", "parserinfo"]
class _timelex(object): class _timelex(object):
def __init__(self, instream): def __init__(self, instream):
if isinstance(instream, basestring): if isinstance(instream, text_type):
instream = StringIO(instream) instream = StringIO(instream)
self.instream = instream self.instream = instream
self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
@ -133,12 +137,15 @@ class _timelex(object):
def __iter__(self): def __iter__(self):
return self return self
def next(self): def __next__(self):
token = self.get_token() token = self.get_token()
if token is None: if token is None:
raise StopIteration raise StopIteration
return token return token
def next(self):
return self.__next__() # Python 2.x support
def split(cls, s): def split(cls, s):
return list(cls(s)) return list(cls(s))
split = classmethod(split) split = classmethod(split)
@ -155,7 +162,7 @@ class _resultbase(object):
for attr in self.__slots__: for attr in self.__slots__:
value = getattr(self, attr) value = getattr(self, attr)
if value is not None: if value is not None:
l.append("%s=%s" % (attr, `value`)) l.append("%s=%s" % (attr, repr(value)))
return "%s(%s)" % (classname, ", ".join(l)) return "%s(%s)" % (classname, ", ".join(l))
def __repr__(self): def __repr__(self):
@ -184,7 +191,7 @@ class parserinfo(object):
("Jun", "June"), ("Jun", "June"),
("Jul", "July"), ("Jul", "July"),
("Aug", "August"), ("Aug", "August"),
("Sep", "September"), ("Sep", "Sept", "September"),
("Oct", "October"), ("Oct", "October"),
("Nov", "November"), ("Nov", "November"),
("Dec", "December")] ("Dec", "December")]
@ -298,9 +305,12 @@ class parser(object):
if not default: if not default:
default = datetime.datetime.now().replace(hour=0, minute=0, default = datetime.datetime.now().replace(hour=0, minute=0,
second=0, microsecond=0) second=0, microsecond=0)
res = self._parse(timestr, **kwargs)
res, skipped_tokens = self._parse(timestr, **kwargs)
if res is None: if res is None:
raise ValueError, "unknown string format" raise ValueError("unknown string format")
repl = {} repl = {}
for attr in ["year", "month", "day", "hour", for attr in ["year", "month", "day", "hour",
"minute", "second", "microsecond"]: "minute", "second", "microsecond"]:
@ -311,20 +321,20 @@ class parser(object):
if res.weekday is not None and not res.day: if res.weekday is not None and not res.day:
ret = ret+relativedelta.relativedelta(weekday=res.weekday) ret = ret+relativedelta.relativedelta(weekday=res.weekday)
if not ignoretz: if not ignoretz:
if callable(tzinfos) or tzinfos and res.tzname in tzinfos: if isinstance(tzinfos, collections.Callable) or tzinfos and res.tzname in tzinfos:
if callable(tzinfos): if isinstance(tzinfos, collections.Callable):
tzdata = tzinfos(res.tzname, res.tzoffset) tzdata = tzinfos(res.tzname, res.tzoffset)
else: else:
tzdata = tzinfos.get(res.tzname) tzdata = tzinfos.get(res.tzname)
if isinstance(tzdata, datetime.tzinfo): if isinstance(tzdata, datetime.tzinfo):
tzinfo = tzdata tzinfo = tzdata
elif isinstance(tzdata, basestring): elif isinstance(tzdata, text_type):
tzinfo = tz.tzstr(tzdata) tzinfo = tz.tzstr(tzdata)
elif isinstance(tzdata, int): elif isinstance(tzdata, integer_types):
tzinfo = tz.tzoffset(res.tzname, tzdata) tzinfo = tz.tzoffset(res.tzname, tzdata)
else: else:
raise ValueError, "offset must be tzinfo subclass, " \ raise ValueError("offset must be tzinfo subclass, " \
"tz string, or int offset" "tz string, or int offset")
ret = ret.replace(tzinfo=tzinfo) ret = ret.replace(tzinfo=tzinfo)
elif res.tzname and res.tzname in time.tzname: elif res.tzname and res.tzname in time.tzname:
ret = ret.replace(tzinfo=tz.tzlocal()) ret = ret.replace(tzinfo=tz.tzlocal())
@ -332,6 +342,10 @@ class parser(object):
ret = ret.replace(tzinfo=tz.tzutc()) ret = ret.replace(tzinfo=tz.tzutc())
elif res.tzoffset: elif res.tzoffset:
ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset))
if skipped_tokens:
return ret, skipped_tokens
return ret return ret
class _result(_resultbase): class _result(_resultbase):
@ -339,7 +353,10 @@ class parser(object):
"hour", "minute", "second", "microsecond", "hour", "minute", "second", "microsecond",
"tzname", "tzoffset"] "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 info = self.info
if dayfirst is None: if dayfirst is None:
dayfirst = info.dayfirst dayfirst = info.dayfirst
@ -347,6 +364,13 @@ class parser(object):
yearfirst = info.yearfirst yearfirst = info.yearfirst
res = self._result() res = self._result()
l = _timelex.split(timestr) 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: try:
# year/month/day list # year/month/day list
@ -441,6 +465,17 @@ class parser(object):
newidx = info.hms(l[i]) newidx = info.hms(l[i])
if newidx is not None: if newidx is not None:
idx = newidx 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] == ':': elif i+1 < len_l and l[i] == ':':
# HH:MM[:SS[.ss]] # HH:MM[:SS[.ss]]
res.hour = int(value) res.hour = int(value)
@ -585,7 +620,7 @@ class parser(object):
# Check for a numbered timezone # Check for a numbered timezone
if res.hour is not None and l[i] in ('+', '-'): if res.hour is not None and l[i] in ('+', '-'):
signal = (-1,1)[l[i] == '+'] signal = (-1, 1)[l[i] == '+']
i += 1 i += 1
len_li = len(l[i]) len_li = len(l[i])
if len_li == 4: if len_li == 4:
@ -618,6 +653,13 @@ class parser(object):
if not (info.jump(l[i]) or fuzzy): if not (info.jump(l[i]) or fuzzy):
return None 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 i += 1
# Process year/month/day # Process year/month/day
@ -687,10 +729,19 @@ class parser(object):
if not info.validate(res): if not info.validate(res):
return None return None
return res
if fuzzy_with_tokens:
return res, tuple(skipped_tokens)
return res, None
DEFAULTPARSER = parser() DEFAULTPARSER = parser()
def parse(timestr, parserinfo=None, **kwargs): 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: if parserinfo:
return parser(parserinfo).parse(timestr, **kwargs) return parser(parserinfo).parse(timestr, **kwargs)
else: else:
@ -743,7 +794,7 @@ class _tzparser(object):
if l[i] in ('+', '-'): if l[i] in ('+', '-'):
# Yes, that's right. See the TZ variable # Yes, that's right. See the TZ variable
# documentation. # documentation.
signal = (1,-1)[l[i] == '+'] signal = (1, -1)[l[i] == '+']
i += 1 i += 1
else: else:
signal = -1 signal = -1
@ -801,15 +852,15 @@ class _tzparser(object):
x.time = int(l[i]) x.time = int(l[i])
i += 2 i += 2
if i < len_l: if i < len_l:
if l[i] in ('-','+'): if l[i] in ('-', '+'):
signal = (-1,1)[l[i] == "+"] signal = (-1, 1)[l[i] == "+"]
i += 1 i += 1
else: else:
signal = 1 signal = 1
res.dstoffset = (res.stdoffset+int(l[i]))*signal res.dstoffset = (res.stdoffset+int(l[i]))*signal
elif (l.count(',') == 2 and l[i:].count('/') <= 2 and 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 y in x if y not in "0123456789"]):
for x in (res.start, res.end): for x in (res.start, res.end):
if l[i] == 'J': if l[i] == 'J':

View File

@ -1,15 +1,16 @@
""" """
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net> Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard Python
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" __license__ = "Simplified BSD"
__license__ = "PSF License"
import datetime import datetime
import calendar import calendar
from six import integer_types
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] __all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
class weekday(object): 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)]) 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 The relativedelta type is based on the specification of the excelent
work done by M.-A. Lemburg in his mx.DateTime extension. However, 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, yearday=None, nlyearday=None,
hour=None, minute=None, second=None, microsecond=None): hour=None, minute=None, second=None, microsecond=None):
if dt1 and dt2: if dt1 and dt2:
if not isinstance(dt1, datetime.date) or \ if (not isinstance(dt1, datetime.date)) or (not isinstance(dt2, datetime.date)):
not isinstance(dt2, datetime.date): raise TypeError("relativedelta only diffs datetime/date")
raise TypeError, "relativedelta only diffs datetime/date" if not type(dt1) == type(dt2): #isinstance(dt1, type(dt2)):
if type(dt1) is not type(dt2):
if not isinstance(dt1, datetime.datetime): if not isinstance(dt1, datetime.datetime):
dt1 = datetime.datetime.fromordinal(dt1.toordinal()) dt1 = datetime.datetime.fromordinal(dt1.toordinal())
elif not isinstance(dt2, datetime.datetime): elif not isinstance(dt2, datetime.datetime):
@ -172,7 +172,7 @@ Here is the behavior of operations with relativedelta:
self.second = second self.second = second
self.microsecond = microsecond self.microsecond = microsecond
if type(weekday) is int: if isinstance(weekday, integer_types):
self.weekday = weekdays[weekday] self.weekday = weekdays[weekday]
else: else:
self.weekday = weekday self.weekday = weekday
@ -185,7 +185,7 @@ Here is the behavior of operations with relativedelta:
if yearday > 59: if yearday > 59:
self.leapdays = -1 self.leapdays = -1
if yday: 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): for idx, ydays in enumerate(ydayidx):
if yday <= ydays: if yday <= ydays:
self.month = idx+1 self.month = idx+1
@ -195,7 +195,7 @@ Here is the behavior of operations with relativedelta:
self.day = yday-ydayidx[idx-1] self.day = yday-ydayidx[idx-1]
break break
else: else:
raise ValueError, "invalid year day (%d)" % yday raise ValueError("invalid year day (%d)" % yday)
self._fix() self._fix()
@ -242,9 +242,26 @@ Here is the behavior of operations with relativedelta:
else: else:
self.years = 0 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): 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): elif self._has_time and not isinstance(other, datetime.datetime):
other = datetime.datetime.fromordinal(other.toordinal()) other = datetime.datetime.fromordinal(other.toordinal())
year = (self.year or other.year)+self.years 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) ret += datetime.timedelta(days=jumpdays)
return ret return ret
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other): def __rsub__(self, other):
return self.__neg__().__radd__(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): def __sub__(self, other):
if not isinstance(other, relativedelta): if not isinstance(other, relativedelta):
raise TypeError, "unsupported type for sub operation" raise TypeError("unsupported type for sub operation")
return relativedelta(years=other.years-self.years, return relativedelta(years=self.years-other.years,
months=other.months-self.months, months=self.months-other.months,
days=other.days-self.days, days=self.days-other.days,
hours=other.hours-self.hours, hours=self.hours-other.hours,
minutes=other.minutes-self.minutes, minutes=self.minutes-other.minutes,
seconds=other.seconds-self.seconds, seconds=self.seconds-other.seconds,
microseconds=other.microseconds-self.microseconds, microseconds=self.microseconds-other.microseconds,
leapdays=other.leapdays or self.leapdays, leapdays=self.leapdays or other.leapdays,
year=other.year or self.year, year=self.year or other.year,
month=other.month or self.month, month=self.month or other.month,
day=other.day or self.day, day=self.day or other.day,
weekday=other.weekday or self.weekday, weekday=self.weekday or other.weekday,
hour=other.hour or self.hour, hour=self.hour or other.hour,
minute=other.minute or self.minute, minute=self.minute or other.minute,
second=other.second or self.second, second=self.second or other.second,
microsecond=other.second or self.microsecond) microsecond=self.microsecond or other.microsecond)
def __neg__(self): def __neg__(self):
return relativedelta(years=-self.years, return relativedelta(years=-self.years,
@ -346,7 +346,7 @@ Here is the behavior of operations with relativedelta:
second=self.second, second=self.second,
microsecond=self.microsecond) microsecond=self.microsecond)
def __nonzero__(self): def __bool__(self):
return not (not self.years and return not (not self.years and
not self.months and not self.months and
not self.days and not self.days and
@ -366,13 +366,13 @@ Here is the behavior of operations with relativedelta:
def __mul__(self, other): def __mul__(self, other):
f = float(other) f = float(other)
return relativedelta(years=self.years*f, return relativedelta(years=int(self.years*f),
months=self.months*f, months=int(self.months*f),
days=self.days*f, days=int(self.days*f),
hours=self.hours*f, hours=int(self.hours*f),
minutes=self.minutes*f, minutes=int(self.minutes*f),
seconds=self.seconds*f, seconds=int(self.seconds*f),
microseconds=self.microseconds*f, microseconds=int(self.microseconds*f),
leapdays=self.leapdays, leapdays=self.leapdays,
year=self.year, year=self.year,
month=self.month, month=self.month,
@ -383,6 +383,8 @@ Here is the behavior of operations with relativedelta:
second=self.second, second=self.second,
microsecond=self.microsecond) microsecond=self.microsecond)
__rmul__ = __mul__
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, relativedelta): if not isinstance(other, relativedelta):
return False return False
@ -415,6 +417,8 @@ Here is the behavior of operations with relativedelta:
def __div__(self, other): def __div__(self, other):
return self.__mul__(1/float(other)) return self.__mul__(1/float(other))
__truediv__ = __div__
def __repr__(self): def __repr__(self):
l = [] l = []
for attr in ["years", "months", "days", "leapdays", for attr in ["years", "months", "days", "leapdays",
@ -426,7 +430,7 @@ Here is the behavior of operations with relativedelta:
"hour", "minute", "second", "microsecond"]: "hour", "minute", "second", "microsecond"]:
value = getattr(self, attr) value = getattr(self, attr)
if value is not None: 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)) return "%s(%s)" % (self.__class__.__name__, ", ".join(l))
# vim:ts=4:sw=4:et # vim:ts=4:sw=4:et

View File

@ -1,18 +1,22 @@
""" """
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net> Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard Python
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" __license__ = "Simplified BSD"
__license__ = "PSF License"
import itertools import itertools
import datetime import datetime
import calendar import calendar
import thread try:
import _thread
except ImportError:
import thread as _thread
import sys import sys
from six import advance_iterator, integer_types
__all__ = ["rrule", "rruleset", "rrulestr", __all__ = ["rrule", "rruleset", "rrulestr",
"YEARLY", "MONTHLY", "WEEKLY", "DAILY", "YEARLY", "MONTHLY", "WEEKLY", "DAILY",
"HOURLY", "MINUTELY", "SECONDLY", "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+ 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) [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7)
M365MASK = list(M366MASK) 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]) MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7])
MDAY365MASK = list(MDAY366MASK) 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]) NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7])
NMDAY365MASK = list(NMDAY366MASK) NMDAY365MASK = list(NMDAY366MASK)
M366RANGE = (0,31,60,91,121,152,182,213,244,274,305,335,366) 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) M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
WDAYMASK = [0,1,2,3,4,5,6]*55 WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55
del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31]
MDAY365MASK = tuple(MDAY365MASK) MDAY365MASK = tuple(MDAY365MASK)
M365MASK = tuple(M365MASK) M365MASK = tuple(M365MASK)
@ -41,7 +45,7 @@ M365MASK = tuple(M365MASK)
DAILY, DAILY,
HOURLY, HOURLY,
MINUTELY, MINUTELY,
SECONDLY) = range(7) SECONDLY) = list(range(7))
# Imported on demand. # Imported on demand.
easter = None easter = None
@ -52,7 +56,7 @@ class weekday(object):
def __init__(self, weekday, n=None): def __init__(self, weekday, n=None):
if n == 0: 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.weekday = weekday
self.n = n 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)]) 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): def __init__(self, cache=False):
if cache: if cache:
self._cache = [] self._cache = []
self._cache_lock = thread.allocate_lock() self._cache_lock = _thread.allocate_lock()
self._cache_gen = self._iter() self._cache_gen = self._iter()
self._cache_complete = False self._cache_complete = False
else: else:
@ -112,7 +116,7 @@ class rrulebase:
break break
try: try:
for j in range(10): for j in range(10):
cache.append(gen.next()) cache.append(advance_iterator(gen))
except StopIteration: except StopIteration:
self._cache_gen = gen = None self._cache_gen = gen = None
self._cache_complete = True self._cache_complete = True
@ -133,13 +137,13 @@ class rrulebase:
else: else:
return list(itertools.islice(self, return list(itertools.islice(self,
item.start or 0, item.start or 0,
item.stop or sys.maxint, item.stop or sys.maxsize,
item.step or 1)) item.step or 1))
elif item >= 0: elif item >= 0:
gen = iter(self) gen = iter(self)
try: try:
for i in range(item+1): for i in range(item+1):
res = gen.next() res = advance_iterator(gen)
except StopIteration: except StopIteration:
raise IndexError raise IndexError
return res return res
@ -232,7 +236,7 @@ class rrule(rrulebase):
byweekno=None, byweekday=None, byweekno=None, byweekday=None,
byhour=None, byminute=None, bysecond=None, byhour=None, byminute=None, bysecond=None,
cache=False): cache=False):
rrulebase.__init__(self, cache) super(rrule, self).__init__(cache)
global easter global easter
if not dtstart: if not dtstart:
dtstart = datetime.datetime.now().replace(microsecond=0) dtstart = datetime.datetime.now().replace(microsecond=0)
@ -250,13 +254,13 @@ class rrule(rrulebase):
self._until = until self._until = until
if wkst is None: if wkst is None:
self._wkst = calendar.firstweekday() self._wkst = calendar.firstweekday()
elif type(wkst) is int: elif isinstance(wkst, integer_types):
self._wkst = wkst self._wkst = wkst
else: else:
self._wkst = wkst.weekday self._wkst = wkst.weekday
if bysetpos is None: if bysetpos is None:
self._bysetpos = None self._bysetpos = None
elif type(bysetpos) is int: elif isinstance(bysetpos, integer_types):
if bysetpos == 0 or not (-366 <= bysetpos <= 366): if bysetpos == 0 or not (-366 <= bysetpos <= 366):
raise ValueError("bysetpos must be between 1 and 366, " raise ValueError("bysetpos must be between 1 and 366, "
"or between -366 and -1") "or between -366 and -1")
@ -280,14 +284,14 @@ class rrule(rrulebase):
# bymonth # bymonth
if not bymonth: if not bymonth:
self._bymonth = None self._bymonth = None
elif type(bymonth) is int: elif isinstance(bymonth, integer_types):
self._bymonth = (bymonth,) self._bymonth = (bymonth,)
else: else:
self._bymonth = tuple(bymonth) self._bymonth = tuple(bymonth)
# byyearday # byyearday
if not byyearday: if not byyearday:
self._byyearday = None self._byyearday = None
elif type(byyearday) is int: elif isinstance(byyearday, integer_types):
self._byyearday = (byyearday,) self._byyearday = (byyearday,)
else: else:
self._byyearday = tuple(byyearday) self._byyearday = tuple(byyearday)
@ -295,7 +299,7 @@ class rrule(rrulebase):
if byeaster is not None: if byeaster is not None:
if not easter: if not easter:
from dateutil import easter from dateutil import easter
if type(byeaster) is int: if isinstance(byeaster, integer_types):
self._byeaster = (byeaster,) self._byeaster = (byeaster,)
else: else:
self._byeaster = tuple(byeaster) self._byeaster = tuple(byeaster)
@ -305,7 +309,7 @@ class rrule(rrulebase):
if not bymonthday: if not bymonthday:
self._bymonthday = () self._bymonthday = ()
self._bynmonthday = () self._bynmonthday = ()
elif type(bymonthday) is int: elif isinstance(bymonthday, integer_types):
if bymonthday < 0: if bymonthday < 0:
self._bynmonthday = (bymonthday,) self._bynmonthday = (bymonthday,)
self._bymonthday = () self._bymonthday = ()
@ -318,7 +322,7 @@ class rrule(rrulebase):
# byweekno # byweekno
if byweekno is None: if byweekno is None:
self._byweekno = None self._byweekno = None
elif type(byweekno) is int: elif isinstance(byweekno, integer_types):
self._byweekno = (byweekno,) self._byweekno = (byweekno,)
else: else:
self._byweekno = tuple(byweekno) self._byweekno = tuple(byweekno)
@ -326,7 +330,7 @@ class rrule(rrulebase):
if byweekday is None: if byweekday is None:
self._byweekday = None self._byweekday = None
self._bynweekday = None self._bynweekday = None
elif type(byweekday) is int: elif isinstance(byweekday, integer_types):
self._byweekday = (byweekday,) self._byweekday = (byweekday,)
self._bynweekday = None self._bynweekday = None
elif hasattr(byweekday, "n"): elif hasattr(byweekday, "n"):
@ -340,7 +344,7 @@ class rrule(rrulebase):
self._byweekday = [] self._byweekday = []
self._bynweekday = [] self._bynweekday = []
for wday in byweekday: for wday in byweekday:
if type(wday) is int: if isinstance(wday, integer_types):
self._byweekday.append(wday) self._byweekday.append(wday)
elif not wday.n or freq > MONTHLY: elif not wday.n or freq > MONTHLY:
self._byweekday.append(wday.weekday) self._byweekday.append(wday.weekday)
@ -358,7 +362,7 @@ class rrule(rrulebase):
self._byhour = (dtstart.hour,) self._byhour = (dtstart.hour,)
else: else:
self._byhour = None self._byhour = None
elif type(byhour) is int: elif isinstance(byhour, integer_types):
self._byhour = (byhour,) self._byhour = (byhour,)
else: else:
self._byhour = tuple(byhour) self._byhour = tuple(byhour)
@ -368,7 +372,7 @@ class rrule(rrulebase):
self._byminute = (dtstart.minute,) self._byminute = (dtstart.minute,)
else: else:
self._byminute = None self._byminute = None
elif type(byminute) is int: elif isinstance(byminute, integer_types):
self._byminute = (byminute,) self._byminute = (byminute,)
else: else:
self._byminute = tuple(byminute) self._byminute = tuple(byminute)
@ -378,7 +382,7 @@ class rrule(rrulebase):
self._bysecond = (dtstart.second,) self._bysecond = (dtstart.second,)
else: else:
self._bysecond = None self._bysecond = None
elif type(bysecond) is int: elif isinstance(bysecond, integer_types):
self._bysecond = (bysecond,) self._bysecond = (bysecond,)
else: else:
self._bysecond = tuple(bysecond) self._bysecond = tuple(bysecond)
@ -716,7 +720,7 @@ class _iterinfo(object):
# days from last year's last week number in # days from last year's last week number in
# this year. # this year.
if -1 not in rr._byweekno: 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 lno1wkst = (7-lyearweekday+rr._wkst)%7
lyearlen = 365+calendar.isleap(year-1) lyearlen = 365+calendar.isleap(year-1)
if lno1wkst >= 4: if lno1wkst >= 4:
@ -768,7 +772,7 @@ class _iterinfo(object):
self.lastmonth = month self.lastmonth = month
def ydayset(self, year, month, day): 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): def mdayset(self, year, month, day):
set = [None]*self.yearlen set = [None]*self.yearlen
@ -823,27 +827,38 @@ class _iterinfo(object):
class rruleset(rrulebase): class rruleset(rrulebase):
class _genitem: class _genitem(object):
def __init__(self, genlist, gen): def __init__(self, genlist, gen):
try: try:
self.dt = gen() self.dt = advance_iterator(gen)
genlist.append(self) genlist.append(self)
except StopIteration: except StopIteration:
pass pass
self.genlist = genlist self.genlist = genlist
self.gen = gen self.gen = gen
def next(self): def __next__(self):
try: try:
self.dt = self.gen() self.dt = advance_iterator(self.gen)
except StopIteration: except StopIteration:
self.genlist.remove(self) self.genlist.remove(self)
def __cmp__(self, other): next = __next__
return cmp(self.dt, other.dt)
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): def __init__(self, cache=False):
rrulebase.__init__(self, cache) super(rruleset, self).__init__(cache)
self._rrule = [] self._rrule = []
self._rdate = [] self._rdate = []
self._exrule = [] self._exrule = []
@ -864,14 +879,14 @@ class rruleset(rrulebase):
def _iter(self): def _iter(self):
rlist = [] rlist = []
self._rdate.sort() self._rdate.sort()
self._genitem(rlist, iter(self._rdate).next) self._genitem(rlist, iter(self._rdate))
for gen in [iter(x).next for x in self._rrule]: for gen in [iter(x) for x in self._rrule]:
self._genitem(rlist, gen) self._genitem(rlist, gen)
rlist.sort() rlist.sort()
exlist = [] exlist = []
self._exdate.sort() self._exdate.sort()
self._genitem(exlist, iter(self._exdate).next) self._genitem(exlist, iter(self._exdate))
for gen in [iter(x).next for x in self._exrule]: for gen in [iter(x) for x in self._exrule]:
self._genitem(exlist, gen) self._genitem(exlist, gen)
exlist.sort() exlist.sort()
lastdt = None lastdt = None
@ -880,17 +895,17 @@ class rruleset(rrulebase):
ritem = rlist[0] ritem = rlist[0]
if not lastdt or lastdt != ritem.dt: if not lastdt or lastdt != ritem.dt:
while exlist and exlist[0] < ritem: while exlist and exlist[0] < ritem:
exlist[0].next() advance_iterator(exlist[0])
exlist.sort() exlist.sort()
if not exlist or ritem != exlist[0]: if not exlist or ritem != exlist[0]:
total += 1 total += 1
yield ritem.dt yield ritem.dt
lastdt = ritem.dt lastdt = ritem.dt
ritem.next() advance_iterator(ritem)
rlist.sort() rlist.sort()
self._len = total self._len = total
class _rrulestr: class _rrulestr(object):
_freq_map = {"YEARLY": YEARLY, _freq_map = {"YEARLY": YEARLY,
"MONTHLY": MONTHLY, "MONTHLY": MONTHLY,
@ -932,7 +947,7 @@ class _rrulestr:
ignoretz=kwargs.get("ignoretz"), ignoretz=kwargs.get("ignoretz"),
tzinfos=kwargs.get("tzinfos")) tzinfos=kwargs.get("tzinfos"))
except ValueError: except ValueError:
raise ValueError, "invalid until date" raise ValueError("invalid until date")
def _handle_WKST(self, rrkwargs, name, value, **kwargs): def _handle_WKST(self, rrkwargs, name, value, **kwargs):
rrkwargs["wkst"] = self._weekday_map[value] rrkwargs["wkst"] = self._weekday_map[value]
@ -959,7 +974,7 @@ class _rrulestr:
if line.find(':') != -1: if line.find(':') != -1:
name, value = line.split(':') name, value = line.split(':')
if name != "RRULE": if name != "RRULE":
raise ValueError, "unknown parameter name" raise ValueError("unknown parameter name")
else: else:
value = line value = line
rrkwargs = {} rrkwargs = {}
@ -972,9 +987,9 @@ class _rrulestr:
ignoretz=ignoretz, ignoretz=ignoretz,
tzinfos=tzinfos) tzinfos=tzinfos)
except AttributeError: except AttributeError:
raise ValueError, "unknown parameter '%s'" % name raise ValueError("unknown parameter '%s'" % name)
except (KeyError, ValueError): except (KeyError, ValueError):
raise ValueError, "invalid '%s': %s" % (name, value) raise ValueError("invalid '%s': %s" % (name, value))
return rrule(dtstart=dtstart, cache=cache, **rrkwargs) return rrule(dtstart=dtstart, cache=cache, **rrkwargs)
def _parse_rfc(self, s, def _parse_rfc(self, s,
@ -991,7 +1006,7 @@ class _rrulestr:
unfold = True unfold = True
s = s.upper() s = s.upper()
if not s.strip(): if not s.strip():
raise ValueError, "empty string" raise ValueError("empty string")
if unfold: if unfold:
lines = s.splitlines() lines = s.splitlines()
i = 0 i = 0
@ -1026,36 +1041,36 @@ class _rrulestr:
name, value = line.split(':', 1) name, value = line.split(':', 1)
parms = name.split(';') parms = name.split(';')
if not parms: if not parms:
raise ValueError, "empty property name" raise ValueError("empty property name")
name = parms[0] name = parms[0]
parms = parms[1:] parms = parms[1:]
if name == "RRULE": if name == "RRULE":
for parm in parms: for parm in parms:
raise ValueError, "unsupported RRULE parm: "+parm raise ValueError("unsupported RRULE parm: "+parm)
rrulevals.append(value) rrulevals.append(value)
elif name == "RDATE": elif name == "RDATE":
for parm in parms: for parm in parms:
if parm != "VALUE=DATE-TIME": if parm != "VALUE=DATE-TIME":
raise ValueError, "unsupported RDATE parm: "+parm raise ValueError("unsupported RDATE parm: "+parm)
rdatevals.append(value) rdatevals.append(value)
elif name == "EXRULE": elif name == "EXRULE":
for parm in parms: for parm in parms:
raise ValueError, "unsupported EXRULE parm: "+parm raise ValueError("unsupported EXRULE parm: "+parm)
exrulevals.append(value) exrulevals.append(value)
elif name == "EXDATE": elif name == "EXDATE":
for parm in parms: for parm in parms:
if parm != "VALUE=DATE-TIME": if parm != "VALUE=DATE-TIME":
raise ValueError, "unsupported RDATE parm: "+parm raise ValueError("unsupported RDATE parm: "+parm)
exdatevals.append(value) exdatevals.append(value)
elif name == "DTSTART": elif name == "DTSTART":
for parm in parms: for parm in parms:
raise ValueError, "unsupported DTSTART parm: "+parm raise ValueError("unsupported DTSTART parm: "+parm)
if not parser: if not parser:
from dateutil import parser from dateutil import parser
dtstart = parser.parse(value, ignoretz=ignoretz, dtstart = parser.parse(value, ignoretz=ignoretz,
tzinfos=tzinfos) tzinfos=tzinfos)
else: else:
raise ValueError, "unsupported property: "+name raise ValueError("unsupported property: "+name)
if (forceset or len(rrulevals) > 1 or if (forceset or len(rrulevals) > 1 or
rdatevals or exrulevals or exdatevals): rdatevals or exrulevals or exdatevals):
if not parser and (rdatevals or exdatevals): if not parser and (rdatevals or exdatevals):

View File

@ -1,11 +1,12 @@
""" """
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net> Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard Python
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" __license__ = "Simplified BSD"
__license__ = "PSF License"
from six import string_types, PY3
import datetime import datetime
import struct import struct
@ -25,6 +26,19 @@ try:
except (ImportError, OSError): except (ImportError, OSError):
tzwin, tzwinlocal = None, None 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) ZERO = datetime.timedelta(0)
EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal()
@ -36,6 +50,7 @@ class tzutc(datetime.tzinfo):
def dst(self, dt): def dst(self, dt):
return ZERO return ZERO
@tzname_in_python2
def tzname(self, dt): def tzname(self, dt):
return "UTC" return "UTC"
@ -63,6 +78,7 @@ class tzoffset(datetime.tzinfo):
def dst(self, dt): def dst(self, dt):
return ZERO return ZERO
@tzname_in_python2
def tzname(self, dt): def tzname(self, dt):
return self._name return self._name
@ -75,7 +91,7 @@ class tzoffset(datetime.tzinfo):
def __repr__(self): def __repr__(self):
return "%s(%s, %s)" % (self.__class__.__name__, return "%s(%s, %s)" % (self.__class__.__name__,
`self._name`, repr(self._name),
self._offset.days*86400+self._offset.seconds) self._offset.days*86400+self._offset.seconds)
__reduce__ = object.__reduce__ __reduce__ = object.__reduce__
@ -100,6 +116,7 @@ class tzlocal(datetime.tzinfo):
else: else:
return ZERO return ZERO
@tzname_in_python2
def tzname(self, dt): def tzname(self, dt):
return time.tzname[self._isdst(dt)] return time.tzname[self._isdst(dt)]
@ -161,7 +178,7 @@ class _ttinfo(object):
for attr in self.__slots__: for attr in self.__slots__:
value = getattr(self, attr) value = getattr(self, attr)
if value is not None: 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)) return "%s(%s)" % (self.__class__.__name__, ", ".join(l))
def __eq__(self, other): def __eq__(self, other):
@ -191,16 +208,16 @@ class _ttinfo(object):
class tzfile(datetime.tzinfo): class tzfile(datetime.tzinfo):
# http://www.twinsun.com/tz/tz-link.htm # 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): def __init__(self, fileobj):
if isinstance(fileobj, basestring): if isinstance(fileobj, string_types):
self._filename = fileobj self._filename = fileobj
fileobj = open(fileobj) fileobj = open(fileobj, 'rb')
elif hasattr(fileobj, "name"): elif hasattr(fileobj, "name"):
self._filename = fileobj.name self._filename = fileobj.name
else: else:
self._filename = `fileobj` self._filename = repr(fileobj)
# From tzfile(5): # From tzfile(5):
# #
@ -212,8 +229,8 @@ class tzfile(datetime.tzinfo):
# ``standard'' byte order (the high-order byte # ``standard'' byte order (the high-order byte
# of the value is written first). # of the value is written first).
if fileobj.read(4) != "TZif": if fileobj.read(4).decode() != "TZif":
raise ValueError, "magic not found" raise ValueError("magic not found")
fileobj.read(16) fileobj.read(16)
@ -284,7 +301,7 @@ class tzfile(datetime.tzinfo):
for i in range(typecnt): for i in range(typecnt):
ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) 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 # Then there are tzh_leapcnt pairs of four-byte
# values, written in standard byte order; the # values, written in standard byte order; the
@ -360,7 +377,7 @@ class tzfile(datetime.tzinfo):
if not self._trans_list: if not self._trans_list:
self._ttinfo_std = self._ttinfo_first = self._ttinfo_list[0] self._ttinfo_std = self._ttinfo_first = self._ttinfo_list[0]
else: else:
for i in range(timecnt-1,-1,-1): for i in range(timecnt-1, -1, -1):
tti = self._trans_idx[i] tti = self._trans_idx[i]
if not self._ttinfo_std and not tti.isdst: if not self._ttinfo_std and not tti.isdst:
self._ttinfo_std = tti self._ttinfo_std = tti
@ -448,6 +465,7 @@ class tzfile(datetime.tzinfo):
# dst offset, so I belive that this wouldn't be the right # dst offset, so I belive that this wouldn't be the right
# way to implement this. # way to implement this.
@tzname_in_python2
def tzname(self, dt): def tzname(self, dt):
if not self._ttinfo_std: if not self._ttinfo_std:
return None return None
@ -465,11 +483,11 @@ class tzfile(datetime.tzinfo):
def __repr__(self): def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, `self._filename`) return "%s(%s)" % (self.__class__.__name__, repr(self._filename))
def __reduce__(self): def __reduce__(self):
if not os.path.isfile(self._filename): 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,)) return (self.__class__, (self._filename,))
class tzrange(datetime.tzinfo): class tzrange(datetime.tzinfo):
@ -515,6 +533,7 @@ class tzrange(datetime.tzinfo):
else: else:
return ZERO return ZERO
@tzname_in_python2
def tzname(self, dt): def tzname(self, dt):
if self._isdst(dt): if self._isdst(dt):
return self._dst_abbr return self._dst_abbr
@ -524,7 +543,7 @@ class tzrange(datetime.tzinfo):
def _isdst(self, dt): def _isdst(self, dt):
if not self._start_delta: if not self._start_delta:
return False return False
year = datetime.datetime(dt.year,1,1) year = datetime.datetime(dt.year, 1, 1)
start = year+self._start_delta start = year+self._start_delta
end = year+self._end_delta end = year+self._end_delta
dt = dt.replace(tzinfo=None) dt = dt.replace(tzinfo=None)
@ -561,7 +580,7 @@ class tzstr(tzrange):
res = parser._parsetz(s) res = parser._parsetz(s)
if res is None: if res is None:
raise ValueError, "unknown string format" raise ValueError("unknown string format")
# Here we break the compatibility with the TZ variable handling. # Here we break the compatibility with the TZ variable handling.
# GMT-3 actually *means* the timezone -3. # GMT-3 actually *means* the timezone -3.
@ -624,9 +643,9 @@ class tzstr(tzrange):
return relativedelta.relativedelta(**kwargs) return relativedelta.relativedelta(**kwargs)
def __repr__(self): 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, def __init__(self, tzoffsetfrom, tzoffsetto, isdst,
tzname=None, rrule=None): tzname=None, rrule=None):
self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom)
@ -690,51 +709,52 @@ class _tzicalvtz(datetime.tzinfo):
else: else:
return ZERO return ZERO
@tzname_in_python2
def tzname(self, dt): def tzname(self, dt):
return self._find_comp(dt).tzname return self._find_comp(dt).tzname
def __repr__(self): def __repr__(self):
return "<tzicalvtz %s>" % `self._tzid` return "<tzicalvtz %s>" % repr(self._tzid)
__reduce__ = object.__reduce__ __reduce__ = object.__reduce__
class tzical: class tzical(object):
def __init__(self, fileobj): def __init__(self, fileobj):
global rrule global rrule
if not rrule: if not rrule:
from dateutil import rrule from dateutil import rrule
if isinstance(fileobj, basestring): if isinstance(fileobj, string_types):
self._s = fileobj self._s = fileobj
fileobj = open(fileobj) fileobj = open(fileobj, 'r') # ical should be encoded in UTF-8 with CRLF
elif hasattr(fileobj, "name"): elif hasattr(fileobj, "name"):
self._s = fileobj.name self._s = fileobj.name
else: else:
self._s = `fileobj` self._s = repr(fileobj)
self._vtz = {} self._vtz = {}
self._parse_rfc(fileobj.read()) self._parse_rfc(fileobj.read())
def keys(self): def keys(self):
return self._vtz.keys() return list(self._vtz.keys())
def get(self, tzid=None): def get(self, tzid=None):
if tzid is None: if tzid is None:
keys = self._vtz.keys() keys = list(self._vtz.keys())
if len(keys) == 0: if len(keys) == 0:
raise ValueError, "no timezones defined" raise ValueError("no timezones defined")
elif len(keys) > 1: elif len(keys) > 1:
raise ValueError, "more than one timezone available" raise ValueError("more than one timezone available")
tzid = keys[0] tzid = keys[0]
return self._vtz.get(tzid) return self._vtz.get(tzid)
def _parse_offset(self, s): def _parse_offset(self, s):
s = s.strip() s = s.strip()
if not s: if not s:
raise ValueError, "empty offset" raise ValueError("empty offset")
if s[0] in ('+', '-'): if s[0] in ('+', '-'):
signal = (-1,+1)[s[0]=='+'] signal = (-1, +1)[s[0]=='+']
s = s[1:] s = s[1:]
else: else:
signal = +1 signal = +1
@ -743,12 +763,12 @@ class tzical:
elif len(s) == 6: elif len(s) == 6:
return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal
else: else:
raise ValueError, "invalid offset: "+s raise ValueError("invalid offset: "+s)
def _parse_rfc(self, s): def _parse_rfc(self, s):
lines = s.splitlines() lines = s.splitlines()
if not lines: if not lines:
raise ValueError, "empty string" raise ValueError("empty string")
# Unfold # Unfold
i = 0 i = 0
@ -772,7 +792,7 @@ class tzical:
name, value = line.split(':', 1) name, value = line.split(':', 1)
parms = name.split(';') parms = name.split(';')
if not parms: if not parms:
raise ValueError, "empty property name" raise ValueError("empty property name")
name = parms[0].upper() name = parms[0].upper()
parms = parms[1:] parms = parms[1:]
if invtz: if invtz:
@ -781,7 +801,7 @@ class tzical:
# Process component # Process component
pass pass
else: else:
raise ValueError, "unknown component: "+value raise ValueError("unknown component: "+value)
comptype = value comptype = value
founddtstart = False founddtstart = False
tzoffsetfrom = None tzoffsetfrom = None
@ -791,27 +811,21 @@ class tzical:
elif name == "END": elif name == "END":
if value == "VTIMEZONE": if value == "VTIMEZONE":
if comptype: if comptype:
raise ValueError, \ raise ValueError("component not closed: "+comptype)
"component not closed: "+comptype
if not tzid: if not tzid:
raise ValueError, \ raise ValueError("mandatory TZID not found")
"mandatory TZID not found"
if not comps: if not comps:
raise ValueError, \ raise ValueError("at least one component is needed")
"at least one component is needed"
# Process vtimezone # Process vtimezone
self._vtz[tzid] = _tzicalvtz(tzid, comps) self._vtz[tzid] = _tzicalvtz(tzid, comps)
invtz = False invtz = False
elif value == comptype: elif value == comptype:
if not founddtstart: if not founddtstart:
raise ValueError, \ raise ValueError("mandatory DTSTART not found")
"mandatory DTSTART not found"
if tzoffsetfrom is None: if tzoffsetfrom is None:
raise ValueError, \ raise ValueError("mandatory TZOFFSETFROM not found")
"mandatory TZOFFSETFROM not found"
if tzoffsetto is None: if tzoffsetto is None:
raise ValueError, \ raise ValueError("mandatory TZOFFSETFROM not found")
"mandatory TZOFFSETFROM not found"
# Process component # Process component
rr = None rr = None
if rrulelines: if rrulelines:
@ -825,8 +839,7 @@ class tzical:
comps.append(comp) comps.append(comp)
comptype = None comptype = None
else: else:
raise ValueError, \ raise ValueError("invalid component end: "+value)
"invalid component end: "+value
elif comptype: elif comptype:
if name == "DTSTART": if name == "DTSTART":
rrulelines.append(line) rrulelines.append(line)
@ -835,40 +848,36 @@ class tzical:
rrulelines.append(line) rrulelines.append(line)
elif name == "TZOFFSETFROM": elif name == "TZOFFSETFROM":
if parms: if parms:
raise ValueError, \ raise ValueError("unsupported %s parm: %s "%(name, parms[0]))
"unsupported %s parm: %s "%(name, parms[0])
tzoffsetfrom = self._parse_offset(value) tzoffsetfrom = self._parse_offset(value)
elif name == "TZOFFSETTO": elif name == "TZOFFSETTO":
if parms: if parms:
raise ValueError, \ raise ValueError("unsupported TZOFFSETTO parm: "+parms[0])
"unsupported TZOFFSETTO parm: "+parms[0]
tzoffsetto = self._parse_offset(value) tzoffsetto = self._parse_offset(value)
elif name == "TZNAME": elif name == "TZNAME":
if parms: if parms:
raise ValueError, \ raise ValueError("unsupported TZNAME parm: "+parms[0])
"unsupported TZNAME parm: "+parms[0]
tzname = value tzname = value
elif name == "COMMENT": elif name == "COMMENT":
pass pass
else: else:
raise ValueError, "unsupported property: "+name raise ValueError("unsupported property: "+name)
else: else:
if name == "TZID": if name == "TZID":
if parms: if parms:
raise ValueError, \ raise ValueError("unsupported TZID parm: "+parms[0])
"unsupported TZID parm: "+parms[0]
tzid = value tzid = value
elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"):
pass pass
else: else:
raise ValueError, "unsupported property: "+name raise ValueError("unsupported property: "+name)
elif name == "BEGIN" and value == "VTIMEZONE": elif name == "BEGIN" and value == "VTIMEZONE":
tzid = None tzid = None
comps = [] comps = []
invtz = True invtz = True
def __repr__(self): def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, `self._s`) return "%s(%s)" % (self.__class__.__name__, repr(self._s))
if sys.platform != "win32": if sys.platform != "win32":
TZFILES = ["/etc/localtime", "localtime"] TZFILES = ["/etc/localtime", "localtime"]
@ -914,7 +923,7 @@ def gettz(name=None):
for path in TZPATHS: for path in TZPATHS:
filepath = os.path.join(path, name) filepath = os.path.join(path, name)
if not os.path.isfile(filepath): if not os.path.isfile(filepath):
filepath = filepath.replace(' ','_') filepath = filepath.replace(' ', '_')
if not os.path.isfile(filepath): if not os.path.isfile(filepath):
continue continue
try: try:
@ -930,7 +939,7 @@ def gettz(name=None):
except OSError: except OSError:
pass pass
if not tz: if not tz:
from lib.dateutil.zoneinfo import gettz from dateutil.zoneinfo import gettz
tz = gettz(name) tz = gettz(name)
if not tz: if not tz:
for c in name: for c in name:

View File

@ -1,9 +1,8 @@
# This code was originally contributed by Jeffrey Harris. # This code was originally contributed by Jeffrey Harris.
import datetime import datetime
import struct import struct
import _winreg import winreg
__author__ = "Jeffrey Harris & Gustavo Niemeyer <gustavo@niemeyer.net>"
__all__ = ["tzwin", "tzwinlocal"] __all__ = ["tzwin", "tzwinlocal"]
@ -15,9 +14,9 @@ TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
def _settzkeyname(): def _settzkeyname():
global TZKEYNAME global TZKEYNAME
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
try: try:
_winreg.OpenKey(handle, TZKEYNAMENT).Close() winreg.OpenKey(handle, TZKEYNAMENT).Close()
TZKEYNAME = TZKEYNAMENT TZKEYNAME = TZKEYNAMENT
except WindowsError: except WindowsError:
TZKEYNAME = TZKEYNAME9X TZKEYNAME = TZKEYNAME9X
@ -49,10 +48,10 @@ class tzwinbase(datetime.tzinfo):
def list(): def list():
"""Return a list of all time zones known to the system.""" """Return a list of all time zones known to the system."""
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
tzkey = _winreg.OpenKey(handle, TZKEYNAME) tzkey = winreg.OpenKey(handle, TZKEYNAME)
result = [_winreg.EnumKey(tzkey, i) result = [winreg.EnumKey(tzkey, i)
for i in range(_winreg.QueryInfoKey(tzkey)[0])] for i in range(winreg.QueryInfoKey(tzkey)[0])]
tzkey.Close() tzkey.Close()
handle.Close() handle.Close()
return result return result
@ -79,8 +78,8 @@ class tzwin(tzwinbase):
def __init__(self, name): def __init__(self, name):
self._name = name self._name = name
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
tzkey = _winreg.OpenKey(handle, "%s\%s" % (TZKEYNAME, name)) tzkey = winreg.OpenKey(handle, "%s\%s" % (TZKEYNAME, name))
keydict = valuestodict(tzkey) keydict = valuestodict(tzkey)
tzkey.Close() tzkey.Close()
handle.Close() handle.Close()
@ -118,9 +117,9 @@ class tzwinlocal(tzwinbase):
def __init__(self): 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) keydict = valuestodict(tzlocalkey)
tzlocalkey.Close() tzlocalkey.Close()
@ -128,7 +127,7 @@ class tzwinlocal(tzwinbase):
self._dstname = keydict["DaylightName"].encode("iso-8859-1") self._dstname = keydict["DaylightName"].encode("iso-8859-1")
try: try:
tzkey = _winreg.OpenKey(handle, "%s\%s"%(TZKEYNAME, self._stdname)) tzkey = winreg.OpenKey(handle, "%s\%s"%(TZKEYNAME, self._stdname))
_keydict = valuestodict(tzkey) _keydict = valuestodict(tzkey)
self._display = _keydict["Display"] self._display = _keydict["Display"]
tzkey.Close() tzkey.Close()
@ -165,7 +164,7 @@ def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
"""dayofweek == 0 means Sunday, whichweek 5 means last instance""" """dayofweek == 0 means Sunday, whichweek 5 means last instance"""
first = datetime.datetime(year, month, 1, hour, minute) first = datetime.datetime(year, month, 1, hour, minute)
weekdayone = first.replace(day=((dayofweek-first.isoweekday())%7+1)) weekdayone = first.replace(day=((dayofweek-first.isoweekday())%7+1))
for n in xrange(whichweek): for n in range(whichweek):
dt = weekdayone+(whichweek-n)*ONEWEEK dt = weekdayone+(whichweek-n)*ONEWEEK
if dt.month == month: if dt.month == month:
return dt return dt
@ -173,8 +172,8 @@ def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
def valuestodict(key): def valuestodict(key):
"""Convert a registry key's values to a dictionary.""" """Convert a registry key's values to a dictionary."""
dict = {} dict = {}
size = _winreg.QueryInfoKey(key)[1] size = winreg.QueryInfoKey(key)[1]
for i in range(size): for i in range(size):
data = _winreg.EnumValue(key, i) data = winreg.EnumValue(key, i)
dict[data[0]] = data[1] dict[data[0]] = data[1]
return dict return dict

View File

@ -1 +0,0 @@
*.tar.gz

View File

@ -1,15 +1,19 @@
# -*- coding: utf-8 -*-
""" """
Copyright (c) 2003-2005 Gustavo Niemeyer <gustavo@niemeyer.net> Copyright (c) 2003-2005 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard Python
datetime module. datetime module.
""" """
from lib.dateutil.tz import tzfile import logging
from tarfile import TarFile
import os import os
from subprocess import call
from tarfile import TarFile
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" from dateutil.tz import tzfile
__license__ = "PSF License"
__author__ = "Tomi Pieviläinen <tomi.pievilainen@iki.fi>"
__license__ = "Simplified BSD"
__all__ = ["setcachesize", "gettz", "rebuild"] __all__ = ["setcachesize", "gettz", "rebuild"]
@ -21,8 +25,7 @@ class tzfile(tzfile):
return (gettz, (self._filename,)) return (gettz, (self._filename,))
def getzoneinfofile(): def getzoneinfofile():
filenames = os.listdir(os.path.join(os.path.dirname(__file__))) filenames = sorted(os.listdir(os.path.join(os.path.dirname(__file__))))
filenames.sort()
filenames.reverse() filenames.reverse()
for entry in filenames: for entry in filenames:
if entry.startswith("zoneinfo") and ".tar." in entry: if entry.startswith("zoneinfo") and ".tar." in entry:
@ -58,6 +61,11 @@ def gettz(name):
return tzinfo return tzinfo
def rebuild(filename, tag=None, format="gz"): 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 import tempfile, shutil
tmpdir = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp()
zonedir = os.path.join(tmpdir, "zoneinfo") zonedir = os.path.join(tmpdir, "zoneinfo")
@ -66,13 +74,27 @@ def rebuild(filename, tag=None, format="gz"):
targetname = "zoneinfo%s.tar.%s" % (tag, format) targetname = "zoneinfo%s.tar.%s" % (tag, format)
try: try:
tf = TarFile.open(filename) 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 if not (name.endswith(".sh") or
name.endswith(".tab") or name.endswith(".tab") or
name == "leapseconds"): name == "leapseconds"):
tf.extract(name, tmpdir) tf.extract(name, tmpdir)
filepath = os.path.join(tmpdir, name) 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() tf.close()
target = os.path.join(moduledir, targetname) target = os.path.join(moduledir, targetname)
for entry in os.listdir(moduledir): for entry in os.listdir(moduledir):

View File

@ -261,7 +261,7 @@ def parse_date_time(d, t, network):
foreign_timezone = get_network_timezone(network, network_dict) foreign_timezone = get_network_timezone(network, network_dict)
foreign_naive = datetime.datetime(te.year, te.month, te.day, hr, m, tzinfo=foreign_timezone) foreign_naive = datetime.datetime(te.year, te.month, te.day, hr, m, tzinfo=foreign_timezone)
try: try:
return foreign_naive.astimezone(sb_timezone) return foreign_naive.replace(tzinfo=sb_timezone).astimezone(sb_timezone)
except (ValueError): except (ValueError):
return foreign_naive return foreign_naive