mirror of
https://github.com/python/cpython.git
synced 2025-10-17 20:28:43 +00:00
bpo-40536: Add zoneinfo.available_timezones (GH-20158)
This was not specified in the PEP, but it will likely be a frequently requested feature if it's not included. This includes only the "canonical" zones, not a simple listing of every valid value of `key` that can be passed to `Zoneinfo`, because it seems likely that that's what people will want.
This commit is contained in:
parent
9681953c99
commit
e527ec8abe
6 changed files with 230 additions and 2 deletions
|
@ -102,6 +102,71 @@ def _validate_tzfile_path(path, _base=_TEST_PATH):
|
|||
del _TEST_PATH
|
||||
|
||||
|
||||
def available_timezones():
|
||||
"""Returns a set containing all available time zones.
|
||||
|
||||
.. caution::
|
||||
|
||||
This may attempt to open a large number of files, since the best way to
|
||||
determine if a given file on the time zone search path is to open it
|
||||
and check for the "magic string" at the beginning.
|
||||
"""
|
||||
from importlib import resources
|
||||
|
||||
valid_zones = set()
|
||||
|
||||
# Start with loading from the tzdata package if it exists: this has a
|
||||
# pre-assembled list of zones that only requires opening one file.
|
||||
try:
|
||||
with resources.open_text("tzdata", "zones") as f:
|
||||
for zone in f:
|
||||
zone = zone.strip()
|
||||
if zone:
|
||||
valid_zones.add(zone)
|
||||
except (ImportError, FileNotFoundError):
|
||||
pass
|
||||
|
||||
def valid_key(fpath):
|
||||
try:
|
||||
with open(fpath, "rb") as f:
|
||||
return f.read(4) == b"TZif"
|
||||
except Exception: # pragma: nocover
|
||||
return False
|
||||
|
||||
for tz_root in TZPATH:
|
||||
if not os.path.exists(tz_root):
|
||||
continue
|
||||
|
||||
for root, dirnames, files in os.walk(tz_root):
|
||||
if root == tz_root:
|
||||
# right/ and posix/ are special directories and shouldn't be
|
||||
# included in the output of available zones
|
||||
if "right" in dirnames:
|
||||
dirnames.remove("right")
|
||||
if "posix" in dirnames:
|
||||
dirnames.remove("posix")
|
||||
|
||||
for file in files:
|
||||
fpath = os.path.join(root, file)
|
||||
|
||||
key = os.path.relpath(fpath, start=tz_root)
|
||||
if os.sep != "/": # pragma: nocover
|
||||
key = key.replace(os.sep, "/")
|
||||
|
||||
if not key or key in valid_zones:
|
||||
continue
|
||||
|
||||
if valid_key(fpath):
|
||||
valid_zones.add(key)
|
||||
|
||||
if "posixrules" in valid_zones:
|
||||
# posixrules is a special symlink-only time zone where it exists, it
|
||||
# should not be included in the output
|
||||
valid_zones.remove("posixrules")
|
||||
|
||||
return valid_zones
|
||||
|
||||
|
||||
class InvalidTZPathWarning(RuntimeWarning):
|
||||
"""Warning raised if an invalid path is specified in PYTHONTZPATH."""
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue