| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- import datetime
- import os
- import re
- from babel.localtime._helpers import (
- _get_tzinfo,
- _get_tzinfo_from_file,
- _get_tzinfo_or_raise,
- )
- def _tz_from_env(tzenv: str) -> datetime.tzinfo:
- if tzenv[0] == ':':
- tzenv = tzenv[1:]
- # TZ specifies a file
- if os.path.exists(tzenv):
- return _get_tzinfo_from_file(tzenv)
- # TZ specifies a zoneinfo zone.
- return _get_tzinfo_or_raise(tzenv)
- def _get_localzone(_root: str = '/') -> datetime.tzinfo:
- """Tries to find the local timezone configuration.
- This method prefers finding the timezone name and passing that to
- zoneinfo or pytz, over passing in the localtime file, as in the later
- case the zoneinfo name is unknown.
- The parameter _root makes the function look for files like /etc/localtime
- beneath the _root directory. This is primarily used by the tests.
- In normal usage you call the function without parameters.
- """
- tzenv = os.environ.get('TZ')
- if tzenv:
- return _tz_from_env(tzenv)
- # This is actually a pretty reliable way to test for the local time
- # zone on operating systems like OS X. On OS X especially this is the
- # only one that actually works.
- try:
- link_dst = os.readlink('/etc/localtime')
- except OSError:
- pass
- else:
- pos = link_dst.find('/zoneinfo/')
- if pos >= 0:
- # On occasion, the `/etc/localtime` symlink has a double slash, e.g.
- # "/usr/share/zoneinfo//UTC", which would make `zoneinfo.ZoneInfo`
- # complain (no absolute paths allowed), and we'd end up returning
- # `None` (as a fix for #1092).
- # Instead, let's just "fix" the double slash symlink by stripping
- # leading slashes before passing the assumed zone name forward.
- zone_name = link_dst[pos + 10:].lstrip("/")
- tzinfo = _get_tzinfo(zone_name)
- if tzinfo is not None:
- return tzinfo
- # Now look for distribution specific configuration files
- # that contain the timezone name.
- tzpath = os.path.join(_root, 'etc/timezone')
- if os.path.exists(tzpath):
- with open(tzpath, 'rb') as tzfile:
- data = tzfile.read()
- # Issue #3 in tzlocal was that /etc/timezone was a zoneinfo file.
- # That's a misconfiguration, but we need to handle it gracefully:
- if data[:5] != b'TZif2':
- etctz = data.strip().decode()
- # Get rid of host definitions and comments:
- if ' ' in etctz:
- etctz, dummy = etctz.split(' ', 1)
- if '#' in etctz:
- etctz, dummy = etctz.split('#', 1)
- return _get_tzinfo_or_raise(etctz.replace(' ', '_'))
- # CentOS has a ZONE setting in /etc/sysconfig/clock,
- # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and
- # Gentoo has a TIMEZONE setting in /etc/conf.d/clock
- # We look through these files for a timezone:
- timezone_re = re.compile(r'\s*(TIME)?ZONE\s*=\s*"(?P<etctz>.+)"')
- for filename in ('etc/sysconfig/clock', 'etc/conf.d/clock'):
- tzpath = os.path.join(_root, filename)
- if not os.path.exists(tzpath):
- continue
- with open(tzpath) as tzfile:
- for line in tzfile:
- match = timezone_re.match(line)
- if match is not None:
- # We found a timezone
- etctz = match.group("etctz")
- return _get_tzinfo_or_raise(etctz.replace(' ', '_'))
- # No explicit setting existed. Use localtime
- for filename in ('etc/localtime', 'usr/local/etc/localtime'):
- tzpath = os.path.join(_root, filename)
- if not os.path.exists(tzpath):
- continue
- return _get_tzinfo_from_file(tzpath)
- raise LookupError('Can not find any timezone configuration')
|