105 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			105 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								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')
							 |