Skip to content

Commit 8976633

Browse files
committed
Fixes local timezone loading for unix systems
1 parent afee386 commit 8976633

File tree

8 files changed

+53
-9
lines changed

8 files changed

+53
-9
lines changed

pendulum/tz/loader.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ def load(cls, name):
3838
except _compat.FileNotFoundError:
3939
raise ValueError('Unknown timezone [{}]'.format(name))
4040

41+
@classmethod
42+
def load_from_file(cls, filepath):
43+
try:
44+
with open(filepath, 'rb') as f:
45+
return cls._load(f)
46+
except _compat.FileNotFoundError:
47+
raise ValueError('Unable to load file [{}]'.format(filepath))
48+
4149
@classmethod
4250
def _load(cls, fp):
4351
head_fmt = '>4s c 15x 6l'

pendulum/tz/local_timezone.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,14 @@ def get_tz_name_for_windows(cls):
8686
return get_localzone_name()
8787

8888
@classmethod
89-
def get_tz_name_for_unix(cls):
89+
def get_tz_name_for_unix(cls, _root='/'):
9090
tzenv = os.environ.get('TZ')
9191
if tzenv:
9292
try:
9393
return _tz_from_env(tzenv)
9494
except ValueError:
9595
pass
9696

97-
_root = '/'
98-
9997
# Now look for distribution specific configuration files
10098
# that contain the timezone name.
10199
tzpath = os.path.join(_root, 'etc/timezone')
@@ -150,22 +148,23 @@ def get_tz_name_for_unix(cls):
150148
tzpath = os.path.join(_root, 'etc/localtime')
151149
if os.path.exists(tzpath) and os.path.islink(tzpath):
152150
tzpath = os.path.realpath(tzpath)
153-
start = tzpath.find("/") + 1
154-
while start is not 0:
155-
tzpath = tzpath[start:]
151+
parts = tzpath.split('/')[-2:]
152+
while parts:
153+
tzpath = '/'.join(parts)
156154
try:
157155
return Timezone.load(tzpath)
158-
except ValueError:
156+
except (ValueError, IOError, OSError):
159157
pass
160-
start = tzpath.find("/") + 1
158+
159+
parts.pop(0)
161160

162161
# No explicit setting existed. Use localtime
163162
for filename in ('etc/localtime', 'usr/local/etc/localtime'):
164163
tzpath = os.path.join(_root, filename)
165164

166165
if not os.path.exists(tzpath):
167166
continue
168-
return Timezone('', *Loader.load(tzpath))
167+
return Timezone('', *Loader.load_from_file(tzpath))
169168

170169
raise RuntimeError('Can not find any timezone configuration')
171170

tests/fixtures/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# -*- coding: utf-8 -*-
2+

tests/fixtures/tz/Paris

2.9 KB
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../usr/share/zoneinfo/Europe/Paris
2.9 KB
Binary file not shown.

tests/tz_tests/test_loader.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3+
import os
34
from .. import AbstractTestCase
45
from pendulum.tz.loader import Loader
56

@@ -11,3 +12,19 @@ def test_load_bad_timezone(self):
1112

1213
def test_load_valid(self):
1314
self.assertTrue(Loader.load('America/Toronto'))
15+
16+
def test_load_from_file(self):
17+
local_path = os.path.join(os.path.split(__file__)[0], '..')
18+
tz_file = os.path.join(local_path, 'fixtures', 'tz', 'Paris')
19+
(transitions,
20+
transition_types,
21+
default_transition_type) = Loader.load_from_file(tz_file)
22+
23+
self.assertGreater(len(transitions), 0)
24+
self.assertGreater(len(transition_types), 0)
25+
self.assertIsNotNone(default_transition_type)
26+
27+
def test_load_from_file_invalid(self):
28+
local_path = os.path.join(os.path.split(__file__)[0], '..')
29+
tz_file = os.path.join(local_path, 'fixtures', 'tz', 'NOT_A_TIMEZONE')
30+
self.assertRaises(ValueError, Loader.load_from_file, tz_file)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import os
4+
from .. import AbstractTestCase
5+
from pendulum.tz import LocalTimezone
6+
7+
8+
class LocalTimezoneTest(AbstractTestCase):
9+
10+
def test_unix_symlink(self):
11+
# A ZONE setting in the target path of a symbolic linked localtime, f ex systemd distributions
12+
local_path = os.path.join(os.path.split(__file__)[0], '..')
13+
tz = LocalTimezone.get_tz_name_for_unix(
14+
_root=os.path.join(local_path, 'fixtures', 'tz', 'symlink')
15+
)
16+
17+
self.assertEqual(tz.name, 'Europe/Paris')

0 commit comments

Comments
 (0)