From fb599d70ec609a0ae80e2dd9a7df73574ef9b2dc Mon Sep 17 00:00:00 2001 From: JackDandy Date: Tue, 12 Jan 2016 00:55:19 +0000 Subject: [PATCH] Update dateutil library 2.4.2 (083f666) to 2.4.2 (d4baf97). --- CHANGES.md | 1 + lib/dateutil/parser.py | 50 ++++---- lib/dateutil/relativedelta.py | 104 ++++++++--------- lib/dateutil/rrule.py | 5 +- lib/dateutil/tz/__init__.py | 20 ++++ lib/dateutil/{ => tz}/tz.py | 23 +--- lib/dateutil/tz/tzwin.py | 187 ++++++++++++++++++++++++++++++ lib/dateutil/tz/win.py | 187 ++++++++++++++++++++++++++++++ lib/dateutil/tzwin.py | 189 +------------------------------ lib/dateutil/zoneinfo/rebuild.py | 20 +++- 10 files changed, 497 insertions(+), 289 deletions(-) create mode 100644 lib/dateutil/tz/__init__.py rename lib/dateutil/{ => tz}/tz.py (98%) create mode 100644 lib/dateutil/tz/tzwin.py create mode 100644 lib/dateutil/tz/win.py diff --git a/CHANGES.md b/CHANGES.md index 6459b176..32f519e6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ * Update cachecontrol library 0.11.2 to 0.11.5 * Update Certifi to 2015.11.20.1 (385476b) * Update chardet packages 2.3.0 (26982c5) to 2.3.0 (d7fae98) +* Update dateutil library 2.4.2 (083f666) to 2.4.2 (d4baf97) ### 0.11.0 (2016-01-10 22:30:00 UTC) diff --git a/lib/dateutil/parser.py b/lib/dateutil/parser.py index 762e5db1..acfd9cfe 100644 --- a/lib/dateutil/parser.py +++ b/lib/dateutil/parser.py @@ -71,12 +71,6 @@ class _timelex(object): instream = StringIO(instream) self.instream = instream - self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_' - 'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' - 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ') - self.numchars = '0123456789' - self.whitespace = ' \t\r\n' self.charstack = [] self.tokenstack = [] self.eof = False @@ -101,9 +95,6 @@ class _timelex(object): seenletters = False token = None state = None - wordchars = self.wordchars - numchars = self.numchars - whitespace = self.whitespace while not self.eof: # We only realize that we've reached the end of a token when we @@ -124,11 +115,11 @@ class _timelex(object): # First character of the token - determines if we're starting # to parse a word, a number or something else. token = nextchar - if nextchar in wordchars: + if self.isword(nextchar): state = 'a' - elif nextchar in numchars: + elif self.isnum(nextchar): state = '0' - elif nextchar in whitespace: + elif self.isspace(nextchar): token = ' ' break # emit token else: @@ -137,7 +128,7 @@ class _timelex(object): # If we've already started reading a word, we keep reading # letters until we find something that's not part of a word. seenletters = True - if nextchar in wordchars: + if self.isword(nextchar): token += nextchar elif nextchar == '.': token += nextchar @@ -148,7 +139,7 @@ class _timelex(object): elif state == '0': # If we've already started reading a number, we keep reading # numbers until we find something that doesn't fit. - if nextchar in numchars: + if self.isnum(nextchar): token += nextchar elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): token += nextchar @@ -160,9 +151,9 @@ class _timelex(object): # If we've seen some letters and a dot separator, continue # parsing, and the tokens will be broken up later. seenletters = True - if nextchar == '.' or nextchar in wordchars: + if nextchar == '.' or self.isword(nextchar): token += nextchar - elif nextchar in numchars and token[-1] == '.': + elif self.isnum(nextchar) and token[-1] == '.': token += nextchar state = '0.' else: @@ -171,9 +162,9 @@ class _timelex(object): elif state == '0.': # If we've seen at least one dot separator, keep going, we'll # break up the tokens later. - if nextchar == '.' or nextchar in numchars: + if nextchar == '.' or self.isnum(nextchar): token += nextchar - elif nextchar in wordchars and token[-1] == '.': + elif self.isword(nextchar) and token[-1] == '.': token += nextchar state = 'a.' else: @@ -206,9 +197,24 @@ class _timelex(object): def next(self): return self.__next__() # Python 2.x support + @classmethod def split(cls, s): return list(cls(s)) - split = classmethod(split) + + @classmethod + def isword(cls, nextchar): + """ Whether or not the next character is part of a word """ + return nextchar.isalpha() + + @classmethod + def isnum(cls, nextchar): + """ Whether the next character is part of a number """ + return nextchar.isdigit() + + @classmethod + def isspace(cls, nextchar): + """ Whether the next character is whitespace """ + return nextchar.isspace() class _resultbase(object): @@ -462,7 +468,7 @@ class parser(object): :class:`tzinfo` is not in a valid format, or if an invalid date would be created. - :raises OverFlowError: + :raises OverflowError: Raised if the parsed date exceeds the largest valid C integer on your system. """ @@ -643,7 +649,7 @@ class parser(object): >>> from dateutil.parser import parse >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) - (datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) """ if fuzzy_with_tokens: @@ -1202,7 +1208,7 @@ def parse(timestr, parserinfo=None, **kwargs): :class:`tzinfo` is not in a valid format, or if an invalid date would be created. - :raises OverFlowError: + :raises OverflowError: Raised if the parsed date exceeds the largest valid C integer on your system. """ diff --git a/lib/dateutil/relativedelta.py b/lib/dateutil/relativedelta.py index 84d5f834..177b7139 100644 --- a/lib/dateutil/relativedelta.py +++ b/lib/dateutil/relativedelta.py @@ -40,75 +40,75 @@ MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) class relativedelta(object): """ -The relativedelta type is based on the specification of the excellent -work done by M.-A. Lemburg in his -`mx.DateTime `_ extension. -However, notice that this type does *NOT* implement the same algorithm as -his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. + The relativedelta type is based on the specification of the excellent + work done by M.-A. Lemburg in his + `mx.DateTime `_ extension. + However, notice that this type does *NOT* implement the same algorithm as + his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. -There are two different ways to build a relativedelta instance. The -first one is passing it two date/datetime classes:: + There are two different ways to build a relativedelta instance. The + first one is passing it two date/datetime classes:: - relativedelta(datetime1, datetime2) + relativedelta(datetime1, datetime2) -The second one is passing it any number of the following keyword arguments:: + The second one is passing it any number of the following keyword arguments:: - relativedelta(arg1=x,arg2=y,arg3=z...) + relativedelta(arg1=x,arg2=y,arg3=z...) - year, month, day, hour, minute, second, microsecond: - Absolute information (argument is singular); adding or subtracting a - relativedelta with absolute information does not perform an aritmetic - operation, but rather REPLACES the corresponding value in the - original datetime with the value(s) in relativedelta. + year, month, day, hour, minute, second, microsecond: + Absolute information (argument is singular); adding or subtracting a + relativedelta with absolute information does not perform an aritmetic + operation, but rather REPLACES the corresponding value in the + original datetime with the value(s) in relativedelta. - years, months, weeks, days, hours, minutes, seconds, microseconds: - Relative information, may be negative (argument is plural); adding - or subtracting a relativedelta with relative information performs - the corresponding aritmetic operation on the original datetime value - with the information in the relativedelta. + years, months, weeks, days, hours, minutes, seconds, microseconds: + Relative information, may be negative (argument is plural); adding + or subtracting a relativedelta with relative information performs + the corresponding aritmetic operation on the original datetime value + with the information in the relativedelta. - weekday: - One of the weekday instances (MO, TU, etc). These instances may - receive a parameter N, specifying the Nth weekday, which could - be positive or negative (like MO(+1) or MO(-2). Not specifying - it is the same as specifying +1. You can also use an integer, - where 0=MO. + weekday: + One of the weekday instances (MO, TU, etc). These instances may + receive a parameter N, specifying the Nth weekday, which could + be positive or negative (like MO(+1) or MO(-2). Not specifying + it is the same as specifying +1. You can also use an integer, + where 0=MO. - leapdays: - Will add given days to the date found, if year is a leap - year, and the date found is post 28 of february. + leapdays: + Will add given days to the date found, if year is a leap + year, and the date found is post 28 of february. - yearday, nlyearday: - Set the yearday or the non-leap year day (jump leap days). - These are converted to day/month/leapdays information. + yearday, nlyearday: + Set the yearday or the non-leap year day (jump leap days). + These are converted to day/month/leapdays information. -Here is the behavior of operations with relativedelta: + Here is the behavior of operations with relativedelta: -1. Calculate the absolute year, using the 'year' argument, or the - original datetime year, if the argument is not present. + 1. Calculate the absolute year, using the 'year' argument, or the + original datetime year, if the argument is not present. -2. Add the relative 'years' argument to the absolute year. + 2. Add the relative 'years' argument to the absolute year. -3. Do steps 1 and 2 for month/months. + 3. Do steps 1 and 2 for month/months. -4. Calculate the absolute day, using the 'day' argument, or the - original datetime day, if the argument is not present. Then, - subtract from the day until it fits in the year and month - found after their operations. + 4. Calculate the absolute day, using the 'day' argument, or the + original datetime day, if the argument is not present. Then, + subtract from the day until it fits in the year and month + found after their operations. -5. Add the relative 'days' argument to the absolute day. Notice - that the 'weeks' argument is multiplied by 7 and added to - 'days'. + 5. Add the relative 'days' argument to the absolute day. Notice + that the 'weeks' argument is multiplied by 7 and added to + 'days'. -6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds, - microsecond/microseconds. + 6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds, + microsecond/microseconds. -7. If the 'weekday' argument is present, calculate the weekday, - with the given (wday, nth) tuple. wday is the index of the - weekday (0-6, 0=Mon), and nth is the number of weeks to add - forward or backward, depending on its signal. Notice that if - the calculated date is already Monday, for example, using - (0, 1) or (0, -1) won't change the day. + 7. If the 'weekday' argument is present, calculate the weekday, + with the given (wday, nth) tuple. wday is the index of the + weekday (0-6, 0=Mon), and nth is the number of weeks to add + forward or backward, depending on its signal. Notice that if + the calculated date is already Monday, for example, using + (0, 1) or (0, -1) won't change the day. """ def __init__(self, dt1=None, dt2=None, diff --git a/lib/dateutil/rrule.py b/lib/dateutil/rrule.py index 57b034a4..4b92b68e 100644 --- a/lib/dateutil/rrule.py +++ b/lib/dateutil/rrule.py @@ -10,7 +10,10 @@ import datetime import calendar import sys -from fractions import gcd +try: + from math import gcd +except ImportError: + from fractions import gcd from six import advance_iterator, integer_types from six.moves import _thread diff --git a/lib/dateutil/tz/__init__.py b/lib/dateutil/tz/__init__.py new file mode 100644 index 00000000..389b2912 --- /dev/null +++ b/lib/dateutil/tz/__init__.py @@ -0,0 +1,20 @@ +from .tz import * +from six import PY3 + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"] + +def tzname_in_python2(namefunc): + """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 adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None and not PY3: + name = name.encode() + + return name + + return adjust_encoding \ No newline at end of file diff --git a/lib/dateutil/tz.py b/lib/dateutil/tz/tz.py similarity index 98% rename from lib/dateutil/tz.py rename to lib/dateutil/tz/tz.py index 6625d989..a8228357 100644 --- a/lib/dateutil/tz.py +++ b/lib/dateutil/tz/tz.py @@ -14,9 +14,10 @@ import sys import os from six import string_types, PY3 +from .__init__ import tzname_in_python2 try: - from dateutil.tzwin import tzwin, tzwinlocal + from .win import tzwin, tzwinlocal except ImportError: tzwin = tzwinlocal = None @@ -24,29 +25,9 @@ relativedelta = None parser = None rrule = None -__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", - "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"] - - -def tzname_in_python2(namefunc): - """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 adjust_encoding(*args, **kwargs): - name = namefunc(*args, **kwargs) - if name is not None and not PY3: - name = name.encode() - - return name - - return adjust_encoding - ZERO = datetime.timedelta(0) EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() - class tzutc(datetime.tzinfo): def utcoffset(self, dt): diff --git a/lib/dateutil/tz/tzwin.py b/lib/dateutil/tz/tzwin.py new file mode 100644 index 00000000..3003f2ff --- /dev/null +++ b/lib/dateutil/tz/tzwin.py @@ -0,0 +1,187 @@ +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct + +from six.moves import winreg + +from .__init__ import tzname_in_python2 + +__all__ = ["tzwin", "tzwinlocal"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + + +def _settzkeyname(): + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + return TZKEYNAME + +TZKEYNAME = _settzkeyname() + + +class tzwinbase(datetime.tzinfo): + """tzinfo class based on win32's timezones available in the registry.""" + + def utcoffset(self, dt): + if self._isdst(dt): + return datetime.timedelta(minutes=self._dstoffset) + else: + return datetime.timedelta(minutes=self._stdoffset) + + def dst(self, dt): + if self._isdst(dt): + minutes = self._dstoffset - self._stdoffset + return datetime.timedelta(minutes=minutes) + else: + return datetime.timedelta(0) + + @tzname_in_python2 + def tzname(self, dt): + if self._isdst(dt): + return self._dstname + else: + return self._stdname + + 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])] + tzkey.Close() + handle.Close() + return result + list = staticmethod(list) + + def display(self): + return self._display + + def _isdst(self, dt): + if not self._dstmonth: + # dstmonth == 0 signals the zone has no daylight saving time + return False + dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + if dston < dstoff: + return dston <= dt.replace(tzinfo=None) < dstoff + else: + return not dstoff <= dt.replace(tzinfo=None) < dston + + +class tzwin(tzwinbase): + + def __init__(self, name): + self._name = name + + # multiple contexts only possible in 2.7 and 3.1, we still support 2.6 + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, + "%s\%s" % (TZKEYNAME, name)) as tzkey: + keydict = valuestodict(tzkey) + + self._stdname = keydict["Std"] + self._dstname = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1 + + # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + + def __init__(self): + + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._stdname = keydict["StandardName"] + self._dstname = keydict["DaylightName"] + + try: + with winreg.OpenKey( + handle, "%s\%s" % (TZKEYNAME, self._stdname)) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + self._stdoffset = -keydict["Bias"]-keydict["StandardBias"] + self._dstoffset = self._stdoffset-keydict["DaylightBias"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:6] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:6] + + def __reduce__(self): + return (self.__class__, ()) + + +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 range(whichweek): + dt = weekdayone+(whichweek-n)*ONEWEEK + if dt.month == month: + return dt + + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dict = {} + size = winreg.QueryInfoKey(key)[1] + for i in range(size): + data = winreg.EnumValue(key, i) + dict[data[0]] = data[1] + return dict diff --git a/lib/dateutil/tz/win.py b/lib/dateutil/tz/win.py new file mode 100644 index 00000000..3003f2ff --- /dev/null +++ b/lib/dateutil/tz/win.py @@ -0,0 +1,187 @@ +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct + +from six.moves import winreg + +from .__init__ import tzname_in_python2 + +__all__ = ["tzwin", "tzwinlocal"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + + +def _settzkeyname(): + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + return TZKEYNAME + +TZKEYNAME = _settzkeyname() + + +class tzwinbase(datetime.tzinfo): + """tzinfo class based on win32's timezones available in the registry.""" + + def utcoffset(self, dt): + if self._isdst(dt): + return datetime.timedelta(minutes=self._dstoffset) + else: + return datetime.timedelta(minutes=self._stdoffset) + + def dst(self, dt): + if self._isdst(dt): + minutes = self._dstoffset - self._stdoffset + return datetime.timedelta(minutes=minutes) + else: + return datetime.timedelta(0) + + @tzname_in_python2 + def tzname(self, dt): + if self._isdst(dt): + return self._dstname + else: + return self._stdname + + 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])] + tzkey.Close() + handle.Close() + return result + list = staticmethod(list) + + def display(self): + return self._display + + def _isdst(self, dt): + if not self._dstmonth: + # dstmonth == 0 signals the zone has no daylight saving time + return False + dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + if dston < dstoff: + return dston <= dt.replace(tzinfo=None) < dstoff + else: + return not dstoff <= dt.replace(tzinfo=None) < dston + + +class tzwin(tzwinbase): + + def __init__(self, name): + self._name = name + + # multiple contexts only possible in 2.7 and 3.1, we still support 2.6 + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, + "%s\%s" % (TZKEYNAME, name)) as tzkey: + keydict = valuestodict(tzkey) + + self._stdname = keydict["Std"] + self._dstname = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1 + + # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + + def __init__(self): + + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._stdname = keydict["StandardName"] + self._dstname = keydict["DaylightName"] + + try: + with winreg.OpenKey( + handle, "%s\%s" % (TZKEYNAME, self._stdname)) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + self._stdoffset = -keydict["Bias"]-keydict["StandardBias"] + self._dstoffset = self._stdoffset-keydict["DaylightBias"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:6] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:6] + + def __reduce__(self): + return (self.__class__, ()) + + +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 range(whichweek): + dt = weekdayone+(whichweek-n)*ONEWEEK + if dt.month == month: + return dt + + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dict = {} + size = winreg.QueryInfoKey(key)[1] + for i in range(size): + data = winreg.EnumValue(key, i) + dict[data[0]] = data[1] + return dict diff --git a/lib/dateutil/tzwin.py b/lib/dateutil/tzwin.py index f4e0e248..55cd9102 100644 --- a/lib/dateutil/tzwin.py +++ b/lib/dateutil/tzwin.py @@ -1,187 +1,2 @@ -# This code was originally contributed by Jeffrey Harris. -import datetime -import struct - -from six.moves import winreg - -from .tz import tzname_in_python2 - -__all__ = ["tzwin", "tzwinlocal"] - -ONEWEEK = datetime.timedelta(7) - -TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" -TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" -TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" - - -def _settzkeyname(): - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - try: - winreg.OpenKey(handle, TZKEYNAMENT).Close() - TZKEYNAME = TZKEYNAMENT - except WindowsError: - TZKEYNAME = TZKEYNAME9X - handle.Close() - return TZKEYNAME - -TZKEYNAME = _settzkeyname() - - -class tzwinbase(datetime.tzinfo): - """tzinfo class based on win32's timezones available in the registry.""" - - def utcoffset(self, dt): - if self._isdst(dt): - return datetime.timedelta(minutes=self._dstoffset) - else: - return datetime.timedelta(minutes=self._stdoffset) - - def dst(self, dt): - if self._isdst(dt): - minutes = self._dstoffset - self._stdoffset - return datetime.timedelta(minutes=minutes) - else: - return datetime.timedelta(0) - - @tzname_in_python2 - def tzname(self, dt): - if self._isdst(dt): - return self._dstname - else: - return self._stdname - - 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])] - tzkey.Close() - handle.Close() - return result - list = staticmethod(list) - - def display(self): - return self._display - - def _isdst(self, dt): - if not self._dstmonth: - # dstmonth == 0 signals the zone has no daylight saving time - return False - dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek, - self._dsthour, self._dstminute, - self._dstweeknumber) - dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek, - self._stdhour, self._stdminute, - self._stdweeknumber) - if dston < dstoff: - return dston <= dt.replace(tzinfo=None) < dstoff - else: - return not dstoff <= dt.replace(tzinfo=None) < dston - - -class tzwin(tzwinbase): - - def __init__(self, name): - self._name = name - - # multiple contexts only possible in 2.7 and 3.1, we still support 2.6 - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - with winreg.OpenKey(handle, - "%s\%s" % (TZKEYNAME, name)) as tzkey: - keydict = valuestodict(tzkey) - - self._stdname = keydict["Std"] - self._dstname = keydict["Dlt"] - - self._display = keydict["Display"] - - # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm - tup = struct.unpack("=3l16h", keydict["TZI"]) - self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 - self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1 - - # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs - # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx - (self._stdmonth, - self._stddayofweek, # Sunday = 0 - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[4:9] - - (self._dstmonth, - self._dstdayofweek, # Sunday = 0 - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[12:17] - - def __repr__(self): - return "tzwin(%s)" % repr(self._name) - - def __reduce__(self): - return (self.__class__, (self._name,)) - - -class tzwinlocal(tzwinbase): - - def __init__(self): - - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - - with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: - keydict = valuestodict(tzlocalkey) - - self._stdname = keydict["StandardName"] - self._dstname = keydict["DaylightName"] - - try: - with winreg.OpenKey( - handle, "%s\%s" % (TZKEYNAME, self._stdname)) as tzkey: - _keydict = valuestodict(tzkey) - self._display = _keydict["Display"] - except OSError: - self._display = None - - self._stdoffset = -keydict["Bias"]-keydict["StandardBias"] - self._dstoffset = self._stdoffset-keydict["DaylightBias"] - - # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm - tup = struct.unpack("=8h", keydict["StandardStart"]) - - (self._stdmonth, - self._stddayofweek, # Sunday = 0 - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[1:6] - - tup = struct.unpack("=8h", keydict["DaylightStart"]) - - (self._dstmonth, - self._dstdayofweek, # Sunday = 0 - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[1:6] - - def __reduce__(self): - return (self.__class__, ()) - - -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 range(whichweek): - dt = weekdayone+(whichweek-n)*ONEWEEK - if dt.month == month: - return dt - - -def valuestodict(key): - """Convert a registry key's values to a dictionary.""" - dict = {} - size = winreg.QueryInfoKey(key)[1] - for i in range(size): - data = winreg.EnumValue(key, i) - dict[data[0]] = data[1] - return dict +# tzwin has moved to dateutil.tz.win +from .tz.win import * \ No newline at end of file diff --git a/lib/dateutil/zoneinfo/rebuild.py b/lib/dateutil/zoneinfo/rebuild.py index e646148c..a66c1d91 100644 --- a/lib/dateutil/zoneinfo/rebuild.py +++ b/lib/dateutil/zoneinfo/rebuild.py @@ -25,12 +25,8 @@ def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): try: check_call(["zic", "-d", zonedir] + filepaths) 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 + _print_on_nosuchfile(e) + raise # write metadata file with open(os.path.join(zonedir, METADATA_FN), 'w') as f: json.dump(metadata, f, indent=4, sort_keys=True) @@ -41,3 +37,15 @@ def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): tf.add(entrypath, entry) finally: shutil.rmtree(tmpdir) + +def _print_on_nosuchfile(e): + """Print helpful troubleshooting message + + e is an exception raised by subprocess.check_call() + + """ + 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?")