_win32.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. from __future__ import annotations
  2. try:
  3. import winreg
  4. except ImportError:
  5. winreg = None
  6. import datetime
  7. from typing import Any, Dict, cast
  8. from babel.core import get_global
  9. from babel.localtime._helpers import _get_tzinfo_or_raise
  10. # When building the cldr data on windows this module gets imported.
  11. # Because at that point there is no global.dat yet this call will
  12. # fail. We want to catch it down in that case then and just assume
  13. # the mapping was empty.
  14. try:
  15. tz_names: dict[str, str] = cast(Dict[str, str], get_global('windows_zone_mapping'))
  16. except RuntimeError:
  17. tz_names = {}
  18. def valuestodict(key) -> dict[str, Any]:
  19. """Convert a registry key's values to a dictionary."""
  20. dict = {}
  21. size = winreg.QueryInfoKey(key)[1]
  22. for i in range(size):
  23. data = winreg.EnumValue(key, i)
  24. dict[data[0]] = data[1]
  25. return dict
  26. def get_localzone_name() -> str:
  27. # Windows is special. It has unique time zone names (in several
  28. # meanings of the word) available, but unfortunately, they can be
  29. # translated to the language of the operating system, so we need to
  30. # do a backwards lookup, by going through all time zones and see which
  31. # one matches.
  32. handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
  33. TZLOCALKEYNAME = r'SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
  34. localtz = winreg.OpenKey(handle, TZLOCALKEYNAME)
  35. keyvalues = valuestodict(localtz)
  36. localtz.Close()
  37. if 'TimeZoneKeyName' in keyvalues:
  38. # Windows 7 (and Vista?)
  39. # For some reason this returns a string with loads of NUL bytes at
  40. # least on some systems. I don't know if this is a bug somewhere, I
  41. # just work around it.
  42. tzkeyname = keyvalues['TimeZoneKeyName'].split('\x00', 1)[0]
  43. else:
  44. # Windows 2000 or XP
  45. # This is the localized name:
  46. tzwin = keyvalues['StandardName']
  47. # Open the list of timezones to look up the real name:
  48. TZKEYNAME = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones'
  49. tzkey = winreg.OpenKey(handle, TZKEYNAME)
  50. # Now, match this value to Time Zone information
  51. tzkeyname = None
  52. for i in range(winreg.QueryInfoKey(tzkey)[0]):
  53. subkey = winreg.EnumKey(tzkey, i)
  54. sub = winreg.OpenKey(tzkey, subkey)
  55. data = valuestodict(sub)
  56. sub.Close()
  57. if data.get('Std', None) == tzwin:
  58. tzkeyname = subkey
  59. break
  60. tzkey.Close()
  61. handle.Close()
  62. if tzkeyname is None:
  63. raise LookupError('Can not find Windows timezone configuration')
  64. timezone = tz_names.get(tzkeyname)
  65. if timezone is None:
  66. # Nope, that didn't work. Try adding 'Standard Time',
  67. # it seems to work a lot of times:
  68. timezone = tz_names.get(f"{tzkeyname} Standard Time")
  69. # Return what we have.
  70. if timezone is None:
  71. raise LookupError(f"Can not find timezone {tzkeyname}")
  72. return timezone
  73. def _get_localzone() -> datetime.tzinfo:
  74. if winreg is None:
  75. raise LookupError(
  76. 'Runtime support not available')
  77. return _get_tzinfo_or_raise(get_localzone_name())