Skip to content

Commit

Permalink
Fixes local timezone loading for unix systems
Browse files Browse the repository at this point in the history
  • Loading branch information
sdispater committed Aug 31, 2016
1 parent afee386 commit 8976633
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 9 deletions.
8 changes: 8 additions & 0 deletions pendulum/tz/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ def load(cls, name):
except _compat.FileNotFoundError:
raise ValueError('Unknown timezone [{}]'.format(name))

@classmethod
def load_from_file(cls, filepath):
try:
with open(filepath, 'rb') as f:
return cls._load(f)
except _compat.FileNotFoundError:
raise ValueError('Unable to load file [{}]'.format(filepath))

@classmethod
def _load(cls, fp):
head_fmt = '>4s c 15x 6l'
Expand Down
17 changes: 8 additions & 9 deletions pendulum/tz/local_timezone.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,14 @@ def get_tz_name_for_windows(cls):
return get_localzone_name()

@classmethod
def get_tz_name_for_unix(cls):
def get_tz_name_for_unix(cls, _root='/'):
tzenv = os.environ.get('TZ')
if tzenv:
try:
return _tz_from_env(tzenv)
except ValueError:
pass

_root = '/'

# Now look for distribution specific configuration files
# that contain the timezone name.
tzpath = os.path.join(_root, 'etc/timezone')
Expand Down Expand Up @@ -150,22 +148,23 @@ def get_tz_name_for_unix(cls):
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 is not 0:
tzpath = tzpath[start:]
parts = tzpath.split('/')[-2:]
while parts:
tzpath = '/'.join(parts)
try:
return Timezone.load(tzpath)
except ValueError:
except (ValueError, IOError, OSError):
pass
start = tzpath.find("/") + 1

parts.pop(0)

# 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 Timezone('', *Loader.load(tzpath))
return Timezone('', *Loader.load_from_file(tzpath))

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

Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-

Binary file added tests/fixtures/tz/Paris
Binary file not shown.
1 change: 1 addition & 0 deletions tests/fixtures/tz/symlink/etc/localtime
Binary file not shown.
17 changes: 17 additions & 0 deletions tests/tz_tests/test_loader.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-

import os
from .. import AbstractTestCase
from pendulum.tz.loader import Loader

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

def test_load_valid(self):
self.assertTrue(Loader.load('America/Toronto'))

def test_load_from_file(self):
local_path = os.path.join(os.path.split(__file__)[0], '..')
tz_file = os.path.join(local_path, 'fixtures', 'tz', 'Paris')
(transitions,
transition_types,
default_transition_type) = Loader.load_from_file(tz_file)

self.assertGreater(len(transitions), 0)
self.assertGreater(len(transition_types), 0)
self.assertIsNotNone(default_transition_type)

def test_load_from_file_invalid(self):
local_path = os.path.join(os.path.split(__file__)[0], '..')
tz_file = os.path.join(local_path, 'fixtures', 'tz', 'NOT_A_TIMEZONE')
self.assertRaises(ValueError, Loader.load_from_file, tz_file)
17 changes: 17 additions & 0 deletions tests/tz_tests/test_local_timezone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-

import os
from .. import AbstractTestCase
from pendulum.tz import LocalTimezone


class LocalTimezoneTest(AbstractTestCase):

def test_unix_symlink(self):
# A ZONE setting in the target path of a symbolic linked localtime, f ex systemd distributions
local_path = os.path.join(os.path.split(__file__)[0], '..')
tz = LocalTimezone.get_tz_name_for_unix(
_root=os.path.join(local_path, 'fixtures', 'tz', 'symlink')
)

self.assertEqual(tz.name, 'Europe/Paris')

0 comments on commit 8976633

Please sign in to comment.