tzwin.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # This code was originally contributed by Jeffrey Harris.
  2. import datetime
  3. import struct
  4. from six.moves import winreg
  5. __all__ = ["tzwin", "tzwinlocal"]
  6. ONEWEEK = datetime.timedelta(7)
  7. TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
  8. TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
  9. TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
  10. def _settzkeyname():
  11. handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
  12. try:
  13. winreg.OpenKey(handle, TZKEYNAMENT).Close()
  14. TZKEYNAME = TZKEYNAMENT
  15. except WindowsError:
  16. TZKEYNAME = TZKEYNAME9X
  17. handle.Close()
  18. return TZKEYNAME
  19. TZKEYNAME = _settzkeyname()
  20. class tzwinbase(datetime.tzinfo):
  21. """tzinfo class based on win32's timezones available in the registry."""
  22. def utcoffset(self, dt):
  23. if self._isdst(dt):
  24. return datetime.timedelta(minutes=self._dstoffset)
  25. else:
  26. return datetime.timedelta(minutes=self._stdoffset)
  27. def dst(self, dt):
  28. if self._isdst(dt):
  29. minutes = self._dstoffset - self._stdoffset
  30. return datetime.timedelta(minutes=minutes)
  31. else:
  32. return datetime.timedelta(0)
  33. def tzname(self, dt):
  34. if self._isdst(dt):
  35. return self._dstname
  36. else:
  37. return self._stdname
  38. def list():
  39. """Return a list of all time zones known to the system."""
  40. handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
  41. tzkey = winreg.OpenKey(handle, TZKEYNAME)
  42. result = [winreg.EnumKey(tzkey, i)
  43. for i in range(winreg.QueryInfoKey(tzkey)[0])]
  44. tzkey.Close()
  45. handle.Close()
  46. return result
  47. list = staticmethod(list)
  48. def display(self):
  49. return self._display
  50. def _isdst(self, dt):
  51. if not self._dstmonth:
  52. # dstmonth == 0 signals the zone has no daylight saving time
  53. return False
  54. dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek,
  55. self._dsthour, self._dstminute,
  56. self._dstweeknumber)
  57. dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek,
  58. self._stdhour, self._stdminute,
  59. self._stdweeknumber)
  60. if dston < dstoff:
  61. return dston <= dt.replace(tzinfo=None) < dstoff
  62. else:
  63. return not dstoff <= dt.replace(tzinfo=None) < dston
  64. class tzwin(tzwinbase):
  65. def __init__(self, name):
  66. self._name = name
  67. # multiple contexts only possible in 2.7 and 3.1, we still support 2.6
  68. with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
  69. with winreg.OpenKey(handle,
  70. "%s\%s" % (TZKEYNAME, name)) as tzkey:
  71. keydict = valuestodict(tzkey)
  72. self._stdname = keydict["Std"].encode("iso-8859-1")
  73. self._dstname = keydict["Dlt"].encode("iso-8859-1")
  74. self._display = keydict["Display"]
  75. # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
  76. tup = struct.unpack("=3l16h", keydict["TZI"])
  77. self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
  78. self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1
  79. # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
  80. # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
  81. (self._stdmonth,
  82. self._stddayofweek, # Sunday = 0
  83. self._stdweeknumber, # Last = 5
  84. self._stdhour,
  85. self._stdminute) = tup[4:9]
  86. (self._dstmonth,
  87. self._dstdayofweek, # Sunday = 0
  88. self._dstweeknumber, # Last = 5
  89. self._dsthour,
  90. self._dstminute) = tup[12:17]
  91. def __repr__(self):
  92. return "tzwin(%s)" % repr(self._name)
  93. def __reduce__(self):
  94. return (self.__class__, (self._name,))
  95. class tzwinlocal(tzwinbase):
  96. def __init__(self):
  97. with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
  98. with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
  99. keydict = valuestodict(tzlocalkey)
  100. self._stdname = keydict["StandardName"].encode("iso-8859-1")
  101. self._dstname = keydict["DaylightName"].encode("iso-8859-1")
  102. try:
  103. with winreg.OpenKey(
  104. handle, "%s\%s" % (TZKEYNAME, self._stdname)) as tzkey:
  105. _keydict = valuestodict(tzkey)
  106. self._display = _keydict["Display"]
  107. except OSError:
  108. self._display = None
  109. self._stdoffset = -keydict["Bias"]-keydict["StandardBias"]
  110. self._dstoffset = self._stdoffset-keydict["DaylightBias"]
  111. # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
  112. tup = struct.unpack("=8h", keydict["StandardStart"])
  113. (self._stdmonth,
  114. self._stddayofweek, # Sunday = 0
  115. self._stdweeknumber, # Last = 5
  116. self._stdhour,
  117. self._stdminute) = tup[1:6]
  118. tup = struct.unpack("=8h", keydict["DaylightStart"])
  119. (self._dstmonth,
  120. self._dstdayofweek, # Sunday = 0
  121. self._dstweeknumber, # Last = 5
  122. self._dsthour,
  123. self._dstminute) = tup[1:6]
  124. def __reduce__(self):
  125. return (self.__class__, ())
  126. def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
  127. """dayofweek == 0 means Sunday, whichweek 5 means last instance"""
  128. first = datetime.datetime(year, month, 1, hour, minute)
  129. weekdayone = first.replace(day=((dayofweek-first.isoweekday()) % 7+1))
  130. for n in range(whichweek):
  131. dt = weekdayone+(whichweek-n)*ONEWEEK
  132. if dt.month == month:
  133. return dt
  134. def valuestodict(key):
  135. """Convert a registry key's values to a dictionary."""
  136. dict = {}
  137. size = winreg.QueryInfoKey(key)[1]
  138. for i in range(size):
  139. data = winreg.EnumValue(key, i)
  140. dict[data[0]] = data[1]
  141. return dict