Just some glue logic to query progress and results from benchmark. Needed to move files around, so oth standalone and addon are happy.
333 lines
11 KiB
Python
333 lines
11 KiB
Python
# This code was originally contributed by Jeffrey Harris.
|
|
import datetime
|
|
import struct
|
|
|
|
from six.moves import winreg
|
|
from six import text_type
|
|
|
|
try:
|
|
import ctypes
|
|
from ctypes import wintypes
|
|
except ValueError:
|
|
# ValueError is raised on non-Windows systems for some horrible reason.
|
|
raise ImportError("Running tzwin on non-Windows system")
|
|
|
|
from ._common import tzrangebase
|
|
|
|
__all__ = ["tzwin", "tzwinlocal", "tzres"]
|
|
|
|
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 tzres(object):
|
|
"""
|
|
Class for accessing `tzres.dll`, which contains timezone name related
|
|
resources.
|
|
|
|
.. versionadded:: 2.5.0
|
|
"""
|
|
p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char
|
|
|
|
def __init__(self, tzres_loc='tzres.dll'):
|
|
# Load the user32 DLL so we can load strings from tzres
|
|
user32 = ctypes.WinDLL('user32')
|
|
|
|
# Specify the LoadStringW function
|
|
user32.LoadStringW.argtypes = (wintypes.HINSTANCE,
|
|
wintypes.UINT,
|
|
wintypes.LPWSTR,
|
|
ctypes.c_int)
|
|
|
|
self.LoadStringW = user32.LoadStringW
|
|
self._tzres = ctypes.WinDLL(tzres_loc)
|
|
self.tzres_loc = tzres_loc
|
|
|
|
def load_name(self, offset):
|
|
"""
|
|
Load a timezone name from a DLL offset (integer).
|
|
|
|
>>> from dateutil.tzwin import tzres
|
|
>>> tzr = tzres()
|
|
>>> print(tzr.load_name(112))
|
|
'Eastern Standard Time'
|
|
|
|
:param offset:
|
|
A positive integer value referring to a string from the tzres dll.
|
|
|
|
..note:
|
|
Offsets found in the registry are generally of the form
|
|
`@tzres.dll,-114`. The offset in this case if 114, not -114.
|
|
|
|
"""
|
|
resource = self.p_wchar()
|
|
lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
|
|
nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0)
|
|
return resource[:nchar]
|
|
|
|
def name_from_string(self, tzname_str):
|
|
"""
|
|
Parse strings as returned from the Windows registry into the time zone
|
|
name as defined in the registry.
|
|
|
|
>>> from dateutil.tzwin import tzres
|
|
>>> tzr = tzres()
|
|
>>> print(tzr.name_from_string('@tzres.dll,-251'))
|
|
'Dateline Daylight Time'
|
|
>>> print(tzr.name_from_string('Eastern Standard Time'))
|
|
'Eastern Standard Time'
|
|
|
|
:param tzname_str:
|
|
A timezone name string as returned from a Windows registry key.
|
|
|
|
:return:
|
|
Returns the localized timezone string from tzres.dll if the string
|
|
is of the form `@tzres.dll,-offset`, else returns the input string.
|
|
"""
|
|
if not tzname_str.startswith('@'):
|
|
return tzname_str
|
|
|
|
name_splt = tzname_str.split(',-')
|
|
try:
|
|
offset = int(name_splt[1])
|
|
except:
|
|
raise ValueError("Malformed timezone string.")
|
|
|
|
return self.load_name(offset)
|
|
|
|
|
|
class tzwinbase(tzrangebase):
|
|
"""tzinfo class based on win32's timezones available in the registry."""
|
|
def __init__(self):
|
|
raise NotImplementedError('tzwinbase is an abstract base class')
|
|
|
|
def __eq__(self, other):
|
|
# Compare on all relevant dimensions, including name.
|
|
if not isinstance(other, tzwinbase):
|
|
return NotImplemented
|
|
|
|
return (self._std_offset == other._std_offset and
|
|
self._dst_offset == other._dst_offset and
|
|
self._stddayofweek == other._stddayofweek and
|
|
self._dstdayofweek == other._dstdayofweek and
|
|
self._stdweeknumber == other._stdweeknumber and
|
|
self._dstweeknumber == other._dstweeknumber and
|
|
self._stdhour == other._stdhour and
|
|
self._dsthour == other._dsthour and
|
|
self._stdminute == other._stdminute and
|
|
self._dstminute == other._dstminute and
|
|
self._std_abbr == other._std_abbr and
|
|
self._dst_abbr == other._dst_abbr)
|
|
|
|
@staticmethod
|
|
def list():
|
|
"""Return a list of all time zones known to the system."""
|
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
|
with winreg.OpenKey(handle, TZKEYNAME) as tzkey:
|
|
result = [winreg.EnumKey(tzkey, i)
|
|
for i in range(winreg.QueryInfoKey(tzkey)[0])]
|
|
return result
|
|
|
|
def display(self):
|
|
return self._display
|
|
|
|
def transitions(self, year):
|
|
"""
|
|
For a given year, get the DST on and off transition times, expressed
|
|
always on the standard time side. For zones with no transitions, this
|
|
function returns ``None``.
|
|
|
|
:param year:
|
|
The year whose transitions you would like to query.
|
|
|
|
:return:
|
|
Returns a :class:`tuple` of :class:`datetime.datetime` objects,
|
|
``(dston, dstoff)`` for zones with an annual DST transition, or
|
|
``None`` for fixed offset zones.
|
|
"""
|
|
|
|
if not self.hasdst:
|
|
return None
|
|
|
|
dston = picknthweekday(year, self._dstmonth, self._dstdayofweek,
|
|
self._dsthour, self._dstminute,
|
|
self._dstweeknumber)
|
|
|
|
dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek,
|
|
self._stdhour, self._stdminute,
|
|
self._stdweeknumber)
|
|
|
|
# Ambiguous dates default to the STD side
|
|
dstoff -= self._dst_base_offset
|
|
|
|
return dston, dstoff
|
|
|
|
def _get_hasdst(self):
|
|
return self._dstmonth != 0
|
|
|
|
@property
|
|
def _dst_base_offset(self):
|
|
return self._dst_base_offset_
|
|
|
|
|
|
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:
|
|
tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name)
|
|
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
|
keydict = valuestodict(tzkey)
|
|
|
|
self._std_abbr = keydict["Std"]
|
|
self._dst_abbr = keydict["Dlt"]
|
|
|
|
self._display = keydict["Display"]
|
|
|
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
|
tup = struct.unpack("=3l16h", keydict["TZI"])
|
|
stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
|
dstoffset = stdoffset-tup[2] # + DaylightBias * -1
|
|
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
|
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
|
|
|
# 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]
|
|
|
|
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
|
self.hasdst = self._get_hasdst()
|
|
|
|
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._std_abbr = keydict["StandardName"]
|
|
self._dst_abbr = keydict["DaylightName"]
|
|
|
|
try:
|
|
tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME,
|
|
sn=self._std_abbr)
|
|
with winreg.OpenKey(handle, tzkeyname) as tzkey:
|
|
_keydict = valuestodict(tzkey)
|
|
self._display = _keydict["Display"]
|
|
except OSError:
|
|
self._display = None
|
|
|
|
stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
|
dstoffset = stdoffset-keydict["DaylightBias"]
|
|
|
|
self._std_offset = datetime.timedelta(minutes=stdoffset)
|
|
self._dst_offset = datetime.timedelta(minutes=dstoffset)
|
|
|
|
# For reasons unclear, in this particular key, the day of week has been
|
|
# moved to the END of the SYSTEMTIME structure.
|
|
tup = struct.unpack("=8h", keydict["StandardStart"])
|
|
|
|
(self._stdmonth,
|
|
self._stdweeknumber, # Last = 5
|
|
self._stdhour,
|
|
self._stdminute) = tup[1:5]
|
|
|
|
self._stddayofweek = tup[7]
|
|
|
|
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
|
|
|
(self._dstmonth,
|
|
self._dstweeknumber, # Last = 5
|
|
self._dsthour,
|
|
self._dstminute) = tup[1:5]
|
|
|
|
self._dstdayofweek = tup[7]
|
|
|
|
self._dst_base_offset_ = self._dst_offset - self._std_offset
|
|
self.hasdst = self._get_hasdst()
|
|
|
|
def __repr__(self):
|
|
return "tzwinlocal()"
|
|
|
|
def __str__(self):
|
|
# str will return the standard name, not the daylight name.
|
|
return "tzwinlocal(%s)" % repr(self._std_abbr)
|
|
|
|
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)
|
|
|
|
# This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6),
|
|
# Because 7 % 7 = 0
|
|
weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1)
|
|
wd = weekdayone + ((whichweek - 1) * ONEWEEK)
|
|
if (wd.month != month):
|
|
wd -= ONEWEEK
|
|
|
|
return wd
|
|
|
|
|
|
def valuestodict(key):
|
|
"""Convert a registry key's values to a dictionary."""
|
|
dout = {}
|
|
size = winreg.QueryInfoKey(key)[1]
|
|
tz_res = None
|
|
|
|
for i in range(size):
|
|
key_name, value, dtype = winreg.EnumValue(key, i)
|
|
if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN:
|
|
# If it's a DWORD (32-bit integer), it's stored as unsigned - convert
|
|
# that to a proper signed integer
|
|
if value & (1 << 31):
|
|
value = value - (1 << 32)
|
|
elif dtype == winreg.REG_SZ:
|
|
# If it's a reference to the tzres DLL, load the actual string
|
|
if value.startswith('@tzres'):
|
|
tz_res = tz_res or tzres()
|
|
value = tz_res.name_from_string(value)
|
|
|
|
value = value.rstrip('\x00') # Remove trailing nulls
|
|
|
|
dout[key_name] = value
|
|
|
|
return dout
|