aboutsummaryrefslogtreecommitdiffstats
path: root/nikola/packages/tzlocal/unix.py
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/packages/tzlocal/unix.py')
-rw-r--r--nikola/packages/tzlocal/unix.py184
1 files changed, 99 insertions, 85 deletions
diff --git a/nikola/packages/tzlocal/unix.py b/nikola/packages/tzlocal/unix.py
index 8f7fc84..086ab7c 100644
--- a/nikola/packages/tzlocal/unix.py
+++ b/nikola/packages/tzlocal/unix.py
@@ -1,115 +1,128 @@
-"""tzlocal for UNIX."""
-
-from __future__ import with_statement
+"""Unix support for tzlocal."""
import os
import re
+
import dateutil.tz
_cache_tz = None
-def _get_localzone():
- """Try to find the local timezone configuration.
+def _try_tz_from_env():
+ tzenv = os.environ.get("TZ")
+ if tzenv and tzenv[0] == ":":
+ tzenv = tzenv[1:]
+ try:
+ if tzenv:
+ dateutil.tz.gettz(tzenv)
+ return tzenv
+ except Exception:
+ pass
+
- This method prefers finding the timezone name and passing that to pytz,
- over passing in the localtime file, as in the later case the zoneinfo
- name is unknown.
+def _get_localzone(_root="/"):
+ """Try to find the local timezone configuration.
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.
"""
- tz = os.environ.get('TZ')
- if tz and tz[0] == ':':
- tz = tz[1:]
- try:
- if tz:
- dateutil.tz.gettz(tz)
- return tz
- except:
- pass
+ tzenv = _try_tz_from_env()
+ if tzenv:
+ return tzenv
- try:
- # link will be something like /usr/share/zoneinfo/America/Los_Angeles.
- link = os.readlink('/etc/localtime')
- tz = link.split('zoneinfo/')[-1]
+ # Are we under Termux on Android?
+ if os.path.exists("/system/bin/getprop"):
+ import subprocess
- if tz:
- dateutil.tz.gettz(tz)
- return tz
- except:
- return None
+ androidtz = (
+ subprocess.check_output(["getprop", "persist.sys.timezone"])
+ .strip()
+ .decode()
+ )
+ return androidtz
# Now look for distribution specific configuration files
# that contain the timezone name.
- tzpath = os.path.join('/etc/timezone')
- if os.path.exists(tzpath):
- with open(tzpath, 'rb') as tzfile:
- data = tzfile.read()
-
- # Issue #3 was that /etc/timezone was a zoneinfo file.
- # That's a misconfiguration, but we need to handle it gracefully:
- if data[:5] != 'TZif2':
+ for configfile in ("etc/timezone", "var/db/zoneinfo"):
+ tzpath = os.path.join(_root, configfile)
+ try:
+ with open(tzpath, "rb") as tzfile:
+ data = tzfile.read()
+
+ # Issue #3 was that /etc/timezone was a zoneinfo file.
+ # That's a misconfiguration, but we need to handle it gracefully:
+ if data[:5] == b"TZif2":
+ continue
+
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)
- tz = etctz.replace(' ', '_')
- try:
- if tz:
- dateutil.tz.gettz(tz)
- return tz
- except:
- pass
+ if not etctz:
+ # Empty file, skip
+ continue
+ for etctz in data.decode().splitlines():
+ # Get rid of host definitions and comments:
+ if " " in etctz:
+ etctz, dummy = etctz.split(" ", 1)
+ if "#" in etctz:
+ etctz, dummy = etctz.split("#", 1)
+ if not etctz:
+ continue
+ tz = etctz.replace(" ", "_")
+ return tz
+
+ except IOError:
+ # File doesn't exist or is a directory
+ continue
# 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:
- zone_re = re.compile('\s*ZONE\s*=\s*\"')
- timezone_re = re.compile('\s*TIMEZONE\s*=\s*\"')
- end_re = re.compile('\"')
-
- for tzpath in ('/etc/sysconfig/clock', '/etc/conf.d/clock'):
- if not os.path.exists(tzpath):
+ zone_re = re.compile(r"\s*ZONE\s*=\s*\"")
+ timezone_re = re.compile(r"\s*TIMEZONE\s*=\s*\"")
+ end_re = re.compile('"')
+
+ for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"):
+ tzpath = os.path.join(_root, filename)
+ try:
+ with open(tzpath, "rt") as tzfile:
+ data = tzfile.readlines()
+
+ for line in data:
+ # Look for the ZONE= setting.
+ match = zone_re.match(line)
+ if match is None:
+ # No ZONE= setting. Look for the TIMEZONE= setting.
+ match = timezone_re.match(line)
+ if match is not None:
+ # Some setting existed
+ line = line[match.end():]
+ etctz = line[: end_re.search(line).start()]
+
+ # We found a timezone
+ tz = etctz.replace(" ", "_")
+ return tz
+
+ except IOError:
+ # File doesn't exist or is a directory
continue
- with open(tzpath, 'rt') as tzfile:
- data = tzfile.readlines()
-
- for line in data:
- # Look for the ZONE= setting.
- match = zone_re.match(line)
- if match is None:
- # No ZONE= setting. Look for the TIMEZONE= setting.
- match = timezone_re.match(line)
- if match is not None:
- # Some setting existed
- line = line[match.end():]
- etctz = line[:end_re.search(line).start()]
-
- # We found a timezone
- tz = etctz.replace(' ', '_')
- try:
- if tz:
- dateutil.tz.gettz(tz)
- return tz
- except:
- pass
-
- # Nikola cannot use this thing below...
-
- # 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
- # with open(tzpath, 'rb') as tzfile:
- # return pytz.tzfile.build_tzinfo('local', tzfile)
+ # systemd distributions use symlinks that include the zone name,
+ # see manpage of localtime(5) and timedatectl(1)
+ tzpath = os.path.join(_root, "etc/localtime")
+ if os.path.exists(tzpath) and os.path.islink(tzpath):
+ tzpath = os.path.realpath(tzpath)
+ start = tzpath.find("/") + 1
+ while start != 0:
+ tzpath = tzpath[start:]
+ try:
+ dateutil.tz.gettz(tzpath)
+ return tzpath
+ except Exception:
+ pass
+ start = tzpath.find("/") + 1
+
+ # Nothing found, return UTC
return None
@@ -118,6 +131,7 @@ def get_localzone():
global _cache_tz
if _cache_tz is None:
_cache_tz = _get_localzone()
+
return _cache_tz