Skip to content

Commit 150aa01

Browse files
committed
Simplify and unify UNIX timezone retrival
1 parent aea611d commit 150aa01

2 files changed

Lines changed: 16 additions & 66 deletions

File tree

src/pendulum/tz/local_timezone.py

Lines changed: 16 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import contextlib
44
import os
5-
import re
65
import sys
76
import warnings
87

@@ -158,63 +157,26 @@ def _get_darwin_timezone() -> Timezone:
158157

159158

160159
def _get_unix_timezone(_root: str = "/") -> Timezone:
161-
tzenv = os.environ.get("TZ")
162-
if tzenv:
160+
if tzenv := os.environ.get("TZ"):
163161
with contextlib.suppress(ValueError):
164162
return _tz_from_env(tzenv)
165163

166-
# Now look for distribution specific configuration files
167-
# that contain the timezone name.
168-
tzpath = Path(_root) / "etc" / "timezone"
169-
if tzpath.is_file():
170-
tzfile_data = tzpath.read_bytes()
171-
# Issue #3 was that /etc/timezone was a zoneinfo file.
172-
# That's a misconfiguration, but we need to handle it gracefully:
173-
if not tzfile_data.startswith(b"TZif2"):
174-
etctz = tzfile_data.strip().decode()
175-
# Get rid of host definitions and comments:
176-
etctz, _, _ = etctz.partition(" ")
177-
etctz, _, _ = etctz.partition("#")
178-
return Timezone(etctz.replace(" ", "_"))
179-
180-
# CentOS has a ZONE setting in /etc/sysconfig/clock,
181-
# OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and
182-
# Gentoo has a TIMEZONE setting in /etc/conf.d/clock
183-
# We look through these files for a timezone:
184-
zone_re = re.compile(r'\s*(TIME)?ZONE\s*=\s*"([^"]+)?"')
185-
186-
for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"):
187-
tzpath = Path(_root) / filename
188-
if tzpath.is_file():
189-
data = tzpath.read_text().splitlines()
190-
for line in data:
191-
# Look for the ZONE= or TIMEZONE= setting.
192-
match = zone_re.match(line)
193-
if match:
194-
etctz = match.group(2)
195-
parts = list(reversed(etctz.replace(" ", "_").split(os.path.sep)))
196-
tzpath_parts: list[str] = []
197-
while parts:
198-
tzpath_parts.insert(0, parts.pop(0))
199-
with contextlib.suppress(InvalidTimezone):
200-
return Timezone(os.path.sep.join(tzpath_parts))
201-
202-
# systemd distributions use symlinks that include the zone name,
203-
# see manpage of localtime(5) and timedatectl(1)
204-
tzpath = Path(_root) / "etc" / "localtime"
205-
if tzpath.is_file() and tzpath.is_symlink():
206-
parts = [p.replace(" ", "_") for p in reversed(tzpath.resolve().parts)]
207-
tzpath_parts: list[str] = [] # type: ignore[no-redef]
208-
while parts:
209-
tzpath_parts.insert(0, parts.pop(0))
210-
with contextlib.suppress(InvalidTimezone):
211-
return Timezone(os.path.sep.join(tzpath_parts))
164+
localtime = Path(_root) / "etc" / "localtime"
212165

213-
# No explicit setting existed. Use localtime
214-
for filename in ("etc/localtime", "usr/local/etc/localtime"):
215-
tzpath = Path(_root) / filename
216-
if tzpath.is_file():
217-
with tzpath.open("rb") as f:
166+
# Symlink → IANA name
167+
if localtime.is_symlink() and localtime.is_file():
168+
parts = [p.replace(" ", "_") for p in localtime.resolve().parts]
169+
for i in range(1, len(parts) + 1):
170+
with contextlib.suppress(InvalidTimezone):
171+
return Timezone(os.path.sep.join(parts[-i:]))
172+
173+
# Fallback: read tzfile data
174+
for tzfile in (
175+
localtime,
176+
Path(_root) / "usr/local/etc/localtime",
177+
):
178+
if tzfile.is_file():
179+
with tzfile.open("rb") as f:
218180
return Timezone.from_file(f)
219181

220182
warnings.warn(

tests/tz/test_local_timezone.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,6 @@ def test_unix_symlink():
2121
assert tz.name == "Europe/Paris"
2222

2323

24-
@pytest.mark.skipif(
25-
sys.platform == "win32", reason="Test only available for UNIX systems"
26-
)
27-
def test_unix_clock():
28-
# A ZONE setting in the target path of a symbolic linked localtime,
29-
# f ex systemd distributions
30-
local_path = os.path.join(os.path.split(__file__)[0], "..")
31-
tz = _get_unix_timezone(_root=os.path.join(local_path, "fixtures", "tz", "clock"))
32-
33-
assert tz.name == "Europe/Zurich"
34-
35-
3624
@pytest.mark.skipif(sys.platform != "win32", reason="Test only available for Windows")
3725
def test_windows_timezone():
3826
timezone = _get_windows_timezone()

0 commit comments

Comments
 (0)