text: # -*- coding: utf-8 -*-
  """
  This module provides an interface to the native time zone data on Windows,
  including :py:class:`datetime.tzinfo` implementations.
  
  Attempting to import this module on a non-Windows platform will raise an
  :py:obj:`ImportError`.
  """
  # 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 is 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 the display name of the time zone.
          """
          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):
      """
      Time zone object created from the zone info in the Windows registry
  
      These are similar to :py:class:`dateutil.tz.tzrange` objects in that
      the time zone data is provided in the format of a single offset rule
      for either 0 or 2 time zone transitions per year.
  
      :param: name
          The name of a Windows time zone key, e.g. "Eastern Standard Time".
          The full list of keys can be retrieved with :func:`tzwin.list`.
      """
  
      def __init__(self, name):
          self._name = name
  
          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):
      """
      Class representing the local time zone information in the Windows registry
  
      While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time`
      module) to retrieve time zone information, ``tzwinlocal`` retrieves the
      rules directly from the Windows registry and creates an object like
      :class:`dateutil.tz.tzwin`.
  
      Because Windows does not have an equivalent of :func:`time.tzset`, on
      Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the
      time zone settings *at the time that the process was started*, meaning
      changes to the machine's time zone settings during the run of a program
      on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`.
      Because ``tzwinlocal`` reads the registry directly, it is unaffected by
      this issue.
      """
  
      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
  