Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

win.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. # This code was originally contributed by Jeffrey Harris.
  2. import datetime
  3. import struct
  4. from six.moves import winreg
  5. from six import text_type
  6. try:
  7. import ctypes
  8. from ctypes import wintypes
  9. except ValueError:
  10. # ValueError is raised on non-Windows systems for some horrible reason.
  11. raise ImportError("Running tzwin on non-Windows system")
  12. from ._common import tzrangebase
  13. __all__ = ["tzwin", "tzwinlocal", "tzres"]
  14. ONEWEEK = datetime.timedelta(7)
  15. TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
  16. TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
  17. TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
  18. def _settzkeyname():
  19. handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
  20. try:
  21. winreg.OpenKey(handle, TZKEYNAMENT).Close()
  22. TZKEYNAME = TZKEYNAMENT
  23. except WindowsError:
  24. TZKEYNAME = TZKEYNAME9X
  25. handle.Close()
  26. return TZKEYNAME
  27. TZKEYNAME = _settzkeyname()
  28. class tzres(object):
  29. """
  30. Class for accessing `tzres.dll`, which contains timezone name related
  31. resources.
  32. .. versionadded:: 2.5.0
  33. """
  34. p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char
  35. def __init__(self, tzres_loc='tzres.dll'):
  36. # Load the user32 DLL so we can load strings from tzres
  37. user32 = ctypes.WinDLL('user32')
  38. # Specify the LoadStringW function
  39. user32.LoadStringW.argtypes = (wintypes.HINSTANCE,
  40. wintypes.UINT,
  41. wintypes.LPWSTR,
  42. ctypes.c_int)
  43. self.LoadStringW = user32.LoadStringW
  44. self._tzres = ctypes.WinDLL(tzres_loc)
  45. self.tzres_loc = tzres_loc
  46. def load_name(self, offset):
  47. """
  48. Load a timezone name from a DLL offset (integer).
  49. >>> from dateutil.tzwin import tzres
  50. >>> tzr = tzres()
  51. >>> print(tzr.load_name(112))
  52. 'Eastern Standard Time'
  53. :param offset:
  54. A positive integer value referring to a string from the tzres dll.
  55. ..note:
  56. Offsets found in the registry are generally of the form
  57. `@tzres.dll,-114`. The offset in this case if 114, not -114.
  58. """
  59. resource = self.p_wchar()
  60. lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
  61. nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0)
  62. return resource[:nchar]
  63. def name_from_string(self, tzname_str):
  64. """
  65. Parse strings as returned from the Windows registry into the time zone
  66. name as defined in the registry.
  67. >>> from dateutil.tzwin import tzres
  68. >>> tzr = tzres()
  69. >>> print(tzr.name_from_string('@tzres.dll,-251'))
  70. 'Dateline Daylight Time'
  71. >>> print(tzr.name_from_string('Eastern Standard Time'))
  72. 'Eastern Standard Time'
  73. :param tzname_str:
  74. A timezone name string as returned from a Windows registry key.
  75. :return:
  76. Returns the localized timezone string from tzres.dll if the string
  77. is of the form `@tzres.dll,-offset`, else returns the input string.
  78. """
  79. if not tzname_str.startswith('@'):
  80. return tzname_str
  81. name_splt = tzname_str.split(',-')
  82. try:
  83. offset = int(name_splt[1])
  84. except:
  85. raise ValueError("Malformed timezone string.")
  86. return self.load_name(offset)
  87. class tzwinbase(tzrangebase):
  88. """tzinfo class based on win32's timezones available in the registry."""
  89. def __init__(self):
  90. raise NotImplementedError('tzwinbase is an abstract base class')
  91. def __eq__(self, other):
  92. # Compare on all relevant dimensions, including name.
  93. if not isinstance(other, tzwinbase):
  94. return NotImplemented
  95. return (self._std_offset == other._std_offset and
  96. self._dst_offset == other._dst_offset and
  97. self._stddayofweek == other._stddayofweek and
  98. self._dstdayofweek == other._dstdayofweek and
  99. self._stdweeknumber == other._stdweeknumber and
  100. self._dstweeknumber == other._dstweeknumber and
  101. self._stdhour == other._stdhour and
  102. self._dsthour == other._dsthour and
  103. self._stdminute == other._stdminute and
  104. self._dstminute == other._dstminute and
  105. self._std_abbr == other._std_abbr and
  106. self._dst_abbr == other._dst_abbr)
  107. @staticmethod
  108. def list():
  109. """Return a list of all time zones known to the system."""
  110. with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
  111. with winreg.OpenKey(handle, TZKEYNAME) as tzkey:
  112. result = [winreg.EnumKey(tzkey, i)
  113. for i in range(winreg.QueryInfoKey(tzkey)[0])]
  114. return result
  115. def display(self):
  116. return self._display
  117. def transitions(self, year):
  118. """
  119. For a given year, get the DST on and off transition times, expressed
  120. always on the standard time side. For zones with no transitions, this
  121. function returns ``None``.
  122. :param year:
  123. The year whose transitions you would like to query.
  124. :return:
  125. Returns a :class:`tuple` of :class:`datetime.datetime` objects,
  126. ``(dston, dstoff)`` for zones with an annual DST transition, or
  127. ``None`` for fixed offset zones.
  128. """
  129. if not self.hasdst:
  130. return None
  131. dston = picknthweekday(year, self._dstmonth, self._dstdayofweek,
  132. self._dsthour, self._dstminute,
  133. self._dstweeknumber)
  134. dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek,
  135. self._stdhour, self._stdminute,
  136. self._stdweeknumber)
  137. # Ambiguous dates default to the STD side
  138. dstoff -= self._dst_base_offset
  139. return dston, dstoff
  140. def _get_hasdst(self):
  141. return self._dstmonth != 0
  142. @property
  143. def _dst_base_offset(self):
  144. return self._dst_base_offset_
  145. class tzwin(tzwinbase):
  146. def __init__(self, name):
  147. self._name = name
  148. with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
  149. tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name)
  150. with winreg.OpenKey(handle, tzkeyname) as tzkey:
  151. keydict = valuestodict(tzkey)
  152. self._std_abbr = keydict["Std"]
  153. self._dst_abbr = keydict["Dlt"]
  154. self._display = keydict["Display"]
  155. # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
  156. tup = struct.unpack("=3l16h", keydict["TZI"])
  157. stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
  158. dstoffset = stdoffset-tup[2] # + DaylightBias * -1
  159. self._std_offset = datetime.timedelta(minutes=stdoffset)
  160. self._dst_offset = datetime.timedelta(minutes=dstoffset)
  161. # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
  162. # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
  163. (self._stdmonth,
  164. self._stddayofweek, # Sunday = 0
  165. self._stdweeknumber, # Last = 5
  166. self._stdhour,
  167. self._stdminute) = tup[4:9]
  168. (self._dstmonth,
  169. self._dstdayofweek, # Sunday = 0
  170. self._dstweeknumber, # Last = 5
  171. self._dsthour,
  172. self._dstminute) = tup[12:17]
  173. self._dst_base_offset_ = self._dst_offset - self._std_offset
  174. self.hasdst = self._get_hasdst()
  175. def __repr__(self):
  176. return "tzwin(%s)" % repr(self._name)
  177. def __reduce__(self):
  178. return (self.__class__, (self._name,))
  179. class tzwinlocal(tzwinbase):
  180. def __init__(self):
  181. with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
  182. with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
  183. keydict = valuestodict(tzlocalkey)
  184. self._std_abbr = keydict["StandardName"]
  185. self._dst_abbr = keydict["DaylightName"]
  186. try:
  187. tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME,
  188. sn=self._std_abbr)
  189. with winreg.OpenKey(handle, tzkeyname) as tzkey:
  190. _keydict = valuestodict(tzkey)
  191. self._display = _keydict["Display"]
  192. except OSError:
  193. self._display = None
  194. stdoffset = -keydict["Bias"]-keydict["StandardBias"]
  195. dstoffset = stdoffset-keydict["DaylightBias"]
  196. self._std_offset = datetime.timedelta(minutes=stdoffset)
  197. self._dst_offset = datetime.timedelta(minutes=dstoffset)
  198. # For reasons unclear, in this particular key, the day of week has been
  199. # moved to the END of the SYSTEMTIME structure.
  200. tup = struct.unpack("=8h", keydict["StandardStart"])
  201. (self._stdmonth,
  202. self._stdweeknumber, # Last = 5
  203. self._stdhour,
  204. self._stdminute) = tup[1:5]
  205. self._stddayofweek = tup[7]
  206. tup = struct.unpack("=8h", keydict["DaylightStart"])
  207. (self._dstmonth,
  208. self._dstweeknumber, # Last = 5
  209. self._dsthour,
  210. self._dstminute) = tup[1:5]
  211. self._dstdayofweek = tup[7]
  212. self._dst_base_offset_ = self._dst_offset - self._std_offset
  213. self.hasdst = self._get_hasdst()
  214. def __repr__(self):
  215. return "tzwinlocal()"
  216. def __str__(self):
  217. # str will return the standard name, not the daylight name.
  218. return "tzwinlocal(%s)" % repr(self._std_abbr)
  219. def __reduce__(self):
  220. return (self.__class__, ())
  221. def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
  222. """ dayofweek == 0 means Sunday, whichweek 5 means last instance """
  223. first = datetime.datetime(year, month, 1, hour, minute)
  224. # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6),
  225. # Because 7 % 7 = 0
  226. weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1)
  227. wd = weekdayone + ((whichweek - 1) * ONEWEEK)
  228. if (wd.month != month):
  229. wd -= ONEWEEK
  230. return wd
  231. def valuestodict(key):
  232. """Convert a registry key's values to a dictionary."""
  233. dout = {}
  234. size = winreg.QueryInfoKey(key)[1]
  235. tz_res = None
  236. for i in range(size):
  237. key_name, value, dtype = winreg.EnumValue(key, i)
  238. if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN:
  239. # If it's a DWORD (32-bit integer), it's stored as unsigned - convert
  240. # that to a proper signed integer
  241. if value & (1 << 31):
  242. value = value - (1 << 32)
  243. elif dtype == winreg.REG_SZ:
  244. # If it's a reference to the tzres DLL, load the actual string
  245. if value.startswith('@tzres'):
  246. tz_res = tz_res or tzres()
  247. value = tz_res.name_from_string(value)
  248. value = value.rstrip('\x00') # Remove trailing nulls
  249. dout[key_name] = value
  250. return dout