Hello community, here is the log from the commit of package python-tzlocal for openSUSE:Factory checked in at 2019-08-07 13:53:56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tzlocal (Old) and /work/SRC/openSUSE:Factory/.python-tzlocal.new.9556 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-tzlocal" Wed Aug 7 13:53:56 2019 rev:9 rq:720064 version:2.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-tzlocal/python-tzlocal.changes 2019-03-19 09:59:10.920083937 +0100 +++ /work/SRC/openSUSE:Factory/.python-tzlocal.new.9556/python-tzlocal.changes 2019-08-07 13:53:58.720857778 +0200 @@ -1,0 +2,26 @@ +Wed Jul 31 12:31:37 UTC 2019 - [email protected] + +- version update to 2.0.0 + Major differences since 1.5.1 + ............................. + - When no time zone configuration can be find, tzlocal now return UTC. + This is a major difference from 1.x, where an exception would be raised. + This change is because Docker images often have no configuration at all, + and the unix utilities will then default to UTC, so we follow that. + - If tzlocal on Unix finds a timezone name in a /etc config file, then + tzlocal now verifies that the timezone it fouds has the same offset as + the local computer is configured with. If it doesn't, something is + configured incorrectly. (Victor Torres, regebro) + - Get timezone via Termux `getprop` wrapper on Android. It's not officially + supported because we can't test it, but at least we make an effort. + (Jean Jordaan) + Minor differences and bug fixes + ............................... + - Skip comment lines when parsing /etc/timezone. (Edward Betts) + - Don't load timezone from current directory. (Gabriel Corona) + - Now verifies that the config files actually contain something before + reading them. (Zackary Welch, regebro) + - Got rid of a BytesWarning (Mickaël Schoentgen) + - Now handles if config file paths exists, but are directories. + +------------------------------------------------------------------- Old: ---- 1.5.1.tar.gz New: ---- 2.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tzlocal.spec ++++++ --- /var/tmp/diff_new_pack.Kx6syU/_old 2019-08-07 13:53:59.188857773 +0200 +++ /var/tmp/diff_new_pack.Kx6syU/_new 2019-08-07 13:53:59.188857773 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define modname tzlocal Name: python-%{modname} -Version: 1.5.1 +Version: 2.0.0 Release: 0 Summary: tzinfo object for the local timezone License: MIT ++++++ 1.5.1.tar.gz -> 2.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/.travis.yml new/tzlocal-2.0.0/.travis.yml --- old/tzlocal-1.5.1/.travis.yml 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/.travis.yml 2019-07-23 17:12:14.000000000 +0200 @@ -1,6 +1,5 @@ os: - linux - - osx language: python python: - "2.7" @@ -11,37 +10,3 @@ install: # Nothing needed script: python setup.py test - -# osx Python installs must be run as generic language, because Python isn't -# installed. Also, the queue for their macs is long so let's just test 3.6. -matrix: - include: - - os: osx - language: generic - python: "3.6" - exclude: - - os: osx - -# Why travis can't include Python in the macOS builds is beyond me -before_install: | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - brew update - # Per the `pyenv homebrew recommendations <https://github.com/yyuu/pyenv/wiki#suggested-build-environment>`_. - brew install openssl readline - # See https://docs.travis-ci.com/user/osx-ci-environment/#A-note-on-upgrading-packages. - # I didn't do this above because it works and I'm lazy. - brew outdated pyenv || brew upgrade pyenv - # virtualenv doesn't work without pyenv knowledge. venv in Python 3.3 - # doesn't provide Pip by default. So, use `pyenv-virtualenv <https://github.com/yyuu/pyenv-virtualenv/blob/master/README.md>`_. - brew install pyenv-virtualenv - pyenv install $PYTHON - # I would expect something like ``pyenv init; pyenv local $PYTHON`` or - # ``pyenv shell $PYTHON`` would work, but ``pyenv init`` doesn't seem to - # modify the Bash environment. ??? So, I hand-set the variables instead. - export PYENV_VERSION=$PYTHON - export PATH="/Users/travis/.pyenv/shims:${PATH}" - pyenv-virtualenv venv - source venv/bin/activate - # A manual check that the correct version of Python is running. - python --version - fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/CHANGES.txt new/tzlocal-2.0.0/CHANGES.txt --- old/tzlocal-1.5.1/CHANGES.txt 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/CHANGES.txt 2019-07-23 17:12:14.000000000 +0200 @@ -1,6 +1,47 @@ Changes ======= +2.0.0 (2019-07-23) +------------------ + +- No differences since 2.0.0b3 + +Major differences since 1.5.1 +............................. + +- When no time zone configuration can be find, tzlocal now return UTC. + This is a major difference from 1.x, where an exception would be raised. + This change is because Docker images often have no configuration at all, + and the unix utilities will then default to UTC, so we follow that. + +- If tzlocal on Unix finds a timezone name in a /etc config file, then + tzlocal now verifies that the timezone it fouds has the same offset as + the local computer is configured with. If it doesn't, something is + configured incorrectly. (Victor Torres, regebro) + +- Get timezone via Termux `getprop` wrapper on Android. It's not officially + supported because we can't test it, but at least we make an effort. + (Jean Jordaan) + +Minor differences and bug fixes +............................... + +- Skip comment lines when parsing /etc/timezone. (Edward Betts) + +- Don't load timezone from current directory. (Gabriel Corona) + +- Now verifies that the config files actually contain something before + reading them. (Zackary Welch, regebro) + +- Got rid of a BytesWarning (Mickaël Schoentgen) + +- Now handles if config file paths exists, but are directories. + +- Moved tests out from distributions + +- Support wheels + + 1.5.1 (2017-12-01) ------------------ @@ -38,9 +79,6 @@ ------------------ - Ensure closing of subprocess on OS X (ayalash) -DOING: Implementing feedback on the unsubscribe button -DOING: Investigating remaining issues with DOCX export -BLOCKERS: None - Removed unused imports (jwilk) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/MANIFEST.in new/tzlocal-2.0.0/MANIFEST.in --- old/tzlocal-1.5.1/MANIFEST.in 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/MANIFEST.in 2019-07-23 17:12:14.000000000 +0200 @@ -1,4 +1,5 @@ include *.py include *.rst include *.txt -recursive-include tzlocal/test_data * \ No newline at end of file +recursive-include tests/test_data * +include tests/*.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/README.rst new/tzlocal-2.0.0/README.rst --- old/tzlocal-1.5.1/README.rst 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/README.rst 2019-07-23 17:12:14.000000000 +0200 @@ -1,18 +1,18 @@ tzlocal ======= -This Python module returns a `tzinfo` object with the local timezone information under Unix and Win-32. -It requires `pytz`, and returns `pytz` `tzinfo` objects. +This Python module returns a ``tzinfo`` object with the local timezone information under Unix and Win-32. +It requires ``pytz``, and returns ``pytz`` ``tzinfo`` objects. -This module attempts to fix a glaring hole in `pytz`, that there is no way to +This module attempts to fix a glaring hole in ``pytz``, that there is no way to get the local timezone information, unless you know the zoneinfo name, and under several Linux distros that's hard or impossible to figure out. Also, with Windows different timezone system using pytz isn't of much use unless you separately configure the zoneinfo timezone name. -With `tzlocal` you only need to call `get_localzone()` and you will get a -`tzinfo` object with the local time zone info. On some Unices you will still +With ``tzlocal`` you only need to call ``get_localzone()`` and you will get a +``tzinfo`` object with the local time zone info. On some Unices you will still not get to know what the timezone name is, but you don't need that when you have the tzinfo file. However, if the timezone name is readily available it will be used. @@ -25,11 +25,22 @@ * Windows 2000 and later - * Any unix-like system with a /etc/localtime or /usr/local/etc/localtime + * Any unix-like system with a ``/etc/localtime`` or ``/usr/local/etc/localtime`` If you have one of the above systems and it does not work, it's a bug. Please report it. +Please note that if you getting a time zone called ``local``, this is not a bug, it's +actually the main feature of ``tzlocal``, that even if your system does NOT have a configuration file +with the zoneinfo name of your time zone, it will still work. + +You can also use ``tzlocal`` to get the name of your local timezone, but only if your system is +configured to make that possible. ``tzlocal`` looks for the timezone name in ``/etc/timezone``, ``/var/db/zoneinfo``, +``/etc/sysconfig/clock`` and ``/etc/conf.d/clock``. If your ``/etc/localtime`` is a symlink it can also extract the +name from that symlink. + +If you need the name of your local time zone, then please make sure your system is properly configured to allow that. +If it isn't configured, tzlocal will default to UTC. Usage ----- @@ -80,6 +91,11 @@ * Jakub Wilk * John Quarles * Preston Landers +* Victor Torres +* Jean Jordaan +* Zackary Welch +* Mickaël Schoentgen +* Gabriel Corona (Sorry if I forgot someone) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/setup.cfg new/tzlocal-2.0.0/setup.cfg --- old/tzlocal-1.5.1/setup.cfg 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/setup.cfg 2019-07-23 17:12:14.000000000 +0200 @@ -1,2 +1,5 @@ [check-manifest] ignore=spiny.conf + +[bdist_wheel] +universal=1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/setup.py new/tzlocal-2.0.0/setup.py --- old/tzlocal-1.5.1/setup.py 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/setup.py 2019-07-23 17:12:14.000000000 +0200 @@ -1,11 +1,19 @@ from setuptools import setup, find_packages +from io import open + +version = '2.0.0' + +with open("README.rst", 'rt', encoding='UTF-8') as file: + long_description = file.read() + '\n\n' + +with open("CHANGES.txt", 'rt', encoding='UTF-8') as file: + long_description += file.read() -version = '1.5.1' setup(name='tzlocal', version=version, description="tzinfo object for the local timezone", - long_description=open('README.rst', 'rt').read() + '\n\n' + open('CHANGES.txt', 'rt').read(), + long_description=long_description, classifiers=[ 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: MIT License', @@ -31,5 +39,5 @@ tests_require=[ 'mock', ], - test_suite='tzlocal.tests', + test_suite='tests', ) Binary files old/tzlocal-1.5.1/tests/test_data/Harare and new/tzlocal-2.0.0/tests/test_data/Harare differ Binary files old/tzlocal-1.5.1/tests/test_data/localtime/etc/localtime and new/tzlocal-2.0.0/tests/test_data/localtime/etc/localtime differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/test_data/symlink_localtime/etc/localtime new/tzlocal-2.0.0/tests/test_data/symlink_localtime/etc/localtime --- old/tzlocal-1.5.1/tests/test_data/symlink_localtime/etc/localtime 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/test_data/symlink_localtime/etc/localtime 2019-08-07 13:53:59.236857772 +0200 @@ -0,0 +1 @@ +symbolic link to ../usr/share/zoneinfo/Africa/Harare Binary files old/tzlocal-1.5.1/tests/test_data/symlink_localtime/usr/share/zoneinfo/Africa/Harare and new/tzlocal-2.0.0/tests/test_data/symlink_localtime/usr/share/zoneinfo/Africa/Harare differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/test_data/timezone/etc/timezone new/tzlocal-2.0.0/tests/test_data/timezone/etc/timezone --- old/tzlocal-1.5.1/tests/test_data/timezone/etc/timezone 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/test_data/timezone/etc/timezone 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1 @@ +Africa/Harare# We allow comments. It's unusual, but has happened diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/test_data/timezone_setting/etc/conf.d/clock new/tzlocal-2.0.0/tests/test_data/timezone_setting/etc/conf.d/clock --- old/tzlocal-1.5.1/tests/test_data/timezone_setting/etc/conf.d/clock 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/test_data/timezone_setting/etc/conf.d/clock 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1 @@ +TIMEZONE = "Africa/Harare" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/test_data/top_line_comment/etc/timezone new/tzlocal-2.0.0/tests/test_data/top_line_comment/etc/timezone --- old/tzlocal-1.5.1/tests/test_data/top_line_comment/etc/timezone 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/test_data/top_line_comment/etc/timezone 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1,2 @@ +# Comment on first line +Africa/Harare diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/test_data/vardbzoneinfo/etc/timezone new/tzlocal-2.0.0/tests/test_data/vardbzoneinfo/etc/timezone --- old/tzlocal-1.5.1/tests/test_data/vardbzoneinfo/etc/timezone 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/test_data/vardbzoneinfo/etc/timezone 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1 @@ + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/test_data/vardbzoneinfo/var/db/zoneinfo new/tzlocal-2.0.0/tests/test_data/vardbzoneinfo/var/db/zoneinfo --- old/tzlocal-1.5.1/tests/test_data/vardbzoneinfo/var/db/zoneinfo 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/test_data/vardbzoneinfo/var/db/zoneinfo 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1 @@ +Africa/Harare localhost # The host is not a part of the format, but is allowed. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/test_data/zone_setting/etc/sysconfig/clock new/tzlocal-2.0.0/tests/test_data/zone_setting/etc/sysconfig/clock --- old/tzlocal-1.5.1/tests/test_data/zone_setting/etc/sysconfig/clock 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/test_data/zone_setting/etc/sysconfig/clock 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1 @@ +ZONE="Africa/Harare" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tests/tests.py new/tzlocal-2.0.0/tests/tests.py --- old/tzlocal-1.5.1/tests/tests.py 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tests/tests.py 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1,166 @@ +import mock +import os +import pytz +import sys +import tzlocal.unix +import unittest + +from datetime import datetime +try: + from cStringIO import StringIO # Python 2 +except ImportError: + from io import StringIO # Python 3 + +from tzlocal import utils + +class TzLocalTests(unittest.TestCase): + def setUp(self): + if 'TZ' in os.environ: + del os.environ['TZ'] + + self.path = os.path.split(__file__)[0] + + def test_env(self): + tz_harare = tzlocal.unix._tz_from_env(':Africa/Harare') + self.assertEqual(tz_harare.zone, 'Africa/Harare') + + # Some Unices allow this as well, so we must allow it: + tz_harare = tzlocal.unix._tz_from_env('Africa/Harare') + self.assertEqual(tz_harare.zone, 'Africa/Harare') + + tz_local = tzlocal.unix._tz_from_env(':' + os.path.join(self.path, 'test_data', 'Harare')) + self.assertEqual(tz_local.zone, 'local') + # Make sure the local timezone is the same as the Harare one above. + # We test this with a past date, so that we don't run into future changes + # of the Harare timezone. + dt = datetime(2012, 1, 1, 5) + self.assertEqual(tz_harare.localize(dt), tz_local.localize(dt)) + + # Non-zoneinfo timezones are not supported in the TZ environment. + self.assertRaises(pytz.UnknownTimeZoneError, tzlocal.unix._tz_from_env, 'GMT+03:00') + + # Test the _try function + os.environ['TZ'] = 'Africa/Harare' + tz_harare = tzlocal.unix._try_tz_from_env() + self.assertEqual(tz_harare.zone, 'Africa/Harare') + # With a zone that doesn't exist + os.environ['TZ'] = 'Just Nonsense' + tz_harare = tzlocal.unix._try_tz_from_env() + self.assertIsNone(tz_harare) + + def test_timezone(self): + # Most versions of Ubuntu + + tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'timezone')) + self.assertEqual(tz.zone, 'Africa/Harare') + + def test_timezone_top_line_comment(self): + tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'top_line_comment')) + self.assertEqual(tz.zone, 'Africa/Harare') + + def test_zone_setting(self): + # A ZONE setting in /etc/sysconfig/clock, f ex CentOS + + tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'zone_setting')) + self.assertEqual(tz.zone, 'Africa/Harare') + + def test_timezone_setting(self): + # A ZONE setting in /etc/conf.d/clock, f ex Gentoo + + tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'timezone_setting')) + self.assertEqual(tz.zone, 'Africa/Harare') + + def test_symlink_localtime(self): + # A ZONE setting in the target path of a symbolic linked localtime, f ex systemd distributions + + tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'symlink_localtime')) + self.assertEqual(tz.zone, 'Africa/Harare') + + def test_vardbzoneinfo_setting(self): + # A ZONE setting in /etc/conf.d/clock, f ex Gentoo + + tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'vardbzoneinfo')) + self.assertEqual(tz.zone, 'Africa/Harare') + + def test_only_localtime(self): + tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'localtime')) + self.assertEqual(tz.zone, 'local') + dt = datetime(2012, 1, 1, 5) + self.assertEqual(pytz.timezone('Africa/Harare').localize(dt), tz.localize(dt)) + + @mock.patch('tzlocal.utils.assert_tz_offset') + def test_get_reload(self, atomock): + # Clear any cached zone + tzlocal.unix._cache_tz = None + os.environ['TZ'] = 'Africa/Harare' + tz_harare = tzlocal.unix.get_localzone() + self.assertEqual(tz_harare.zone, 'Africa/Harare') + # Changing the TZ makes no difference, because it's cached + os.environ['TZ'] = 'Africa/Johannesburg' + tz_harare = tzlocal.unix.get_localzone() + self.assertEqual(tz_harare.zone, 'Africa/Harare') + # So we reload it + tz_harare = tzlocal.unix.reload_localzone() + self.assertEqual(tz_harare.zone, 'Africa/Johannesburg') + + def test_fail(self): + out = StringIO() + stderr = sys.stderr + try: + sys.stderr = out + tz = tzlocal.unix._get_localzone( + _root=os.path.join(self.path, 'test_data')) + finally: + sys.stderr = stderr + self.assertEqual(tz, pytz.utc) + self.assertIn('Can not find any timezone configuration', + out.getvalue()) + + def test_assert_tz_offset(self): + # The local zone should be the local zone: + local = tzlocal.get_localzone() + tzlocal.utils.assert_tz_offset(local) + + # Get a non local zone. Let's use Chatham, population 600. + other = pytz.timezone('Pacific/Chatham') + with self.assertRaises(ValueError): + tzlocal.utils.assert_tz_offset(other) + + +if sys.platform == 'win32': + + import tzlocal.win32 + class TzWin32Tests(unittest.TestCase): + + def test_win32(self): + tzlocal.win32.get_localzone() + +else: + + class TzWin32Tests(unittest.TestCase): + + @mock.patch('tzlocal.utils.assert_tz_offset') + def test_win32_on_unix(self, atomock): + # Yes, winreg is all mocked out, but this test means we at least + # catch syntax errors, etc. + winreg = mock.MagicMock() + winreg.OpenKey = mock.MagicMock() + winreg.OpenKey.close = mock.MagicMock() + winreg.QueryInfoKey = mock.MagicMock(return_value=(1, 1)) + winreg.EnumValue = mock.MagicMock( + return_value=('TimeZoneKeyName','Belarus Standard Time')) + winreg.EnumKey = mock.Mock(return_value='Bahia Standard Time') + sys.modules['winreg'] = winreg + import tzlocal.win32 + tz = tzlocal.win32.get_localzone() + self.assertEqual(tz.zone, 'Europe/Minsk') + + tzlocal.win32.valuestodict = mock.Mock(return_value={ + 'StandardName': 'Mocked Standard Time', + 'Std': 'Mocked Standard Time', + }) + tz = tzlocal.win32.reload_localzone() + self.assertEqual(tz.zone, 'America/Bahia') + +if __name__ == '__main__': + unittest.main() Binary files old/tzlocal-1.5.1/tzlocal/test_data/Harare and new/tzlocal-2.0.0/tzlocal/test_data/Harare differ Binary files old/tzlocal-1.5.1/tzlocal/test_data/localtime/etc/localtime and new/tzlocal-2.0.0/tzlocal/test_data/localtime/etc/localtime differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/test_data/symlink_localtime/etc/localtime new/tzlocal-2.0.0/tzlocal/test_data/symlink_localtime/etc/localtime --- old/tzlocal-1.5.1/tzlocal/test_data/symlink_localtime/etc/localtime 2019-08-07 13:53:59.232857772 +0200 +++ new/tzlocal-2.0.0/tzlocal/test_data/symlink_localtime/etc/localtime 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -symbolic link to ../usr/share/zoneinfo/Africa/Harare Binary files old/tzlocal-1.5.1/tzlocal/test_data/symlink_localtime/usr/share/zoneinfo/Africa/Harare and new/tzlocal-2.0.0/tzlocal/test_data/symlink_localtime/usr/share/zoneinfo/Africa/Harare differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/test_data/timezone/etc/timezone new/tzlocal-2.0.0/tzlocal/test_data/timezone/etc/timezone --- old/tzlocal-1.5.1/tzlocal/test_data/timezone/etc/timezone 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/test_data/timezone/etc/timezone 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -Africa/Harare# We allow comments. It's unusual, but has happened diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/test_data/timezone_setting/etc/conf.d/clock new/tzlocal-2.0.0/tzlocal/test_data/timezone_setting/etc/conf.d/clock --- old/tzlocal-1.5.1/tzlocal/test_data/timezone_setting/etc/conf.d/clock 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/test_data/timezone_setting/etc/conf.d/clock 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -TIMEZONE = "Africa/Harare" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/test_data/vardbzoneinfo/var/db/zoneinfo new/tzlocal-2.0.0/tzlocal/test_data/vardbzoneinfo/var/db/zoneinfo --- old/tzlocal-1.5.1/tzlocal/test_data/vardbzoneinfo/var/db/zoneinfo 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/test_data/vardbzoneinfo/var/db/zoneinfo 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -Africa/Harare localhost # The host is not a part of the format, but is allowed. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/test_data/zone_setting/etc/sysconfig/clock new/tzlocal-2.0.0/tzlocal/test_data/zone_setting/etc/sysconfig/clock --- old/tzlocal-1.5.1/tzlocal/test_data/zone_setting/etc/sysconfig/clock 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/test_data/zone_setting/etc/sysconfig/clock 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -ZONE="Africa/Harare" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/tests.py new/tzlocal-2.0.0/tzlocal/tests.py --- old/tzlocal-1.5.1/tzlocal/tests.py 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/tests.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,134 +0,0 @@ -import mock -import os -import pytz -import sys -import tzlocal.unix -import unittest - -from datetime import datetime - - -class TzLocalTests(unittest.TestCase): - def setUp(self): - if 'TZ' in os.environ: - del os.environ['TZ'] - - self.path = os.path.split(__file__)[0] - - def test_env(self): - tz_harare = tzlocal.unix._tz_from_env(':Africa/Harare') - self.assertEqual(tz_harare.zone, 'Africa/Harare') - - # Some Unices allow this as well, so we must allow it: - tz_harare = tzlocal.unix._tz_from_env('Africa/Harare') - self.assertEqual(tz_harare.zone, 'Africa/Harare') - - tz_local = tzlocal.unix._tz_from_env(':' + os.path.join(self.path, 'test_data', 'Harare')) - self.assertEqual(tz_local.zone, 'local') - # Make sure the local timezone is the same as the Harare one above. - # We test this with a past date, so that we don't run into future changes - # of the Harare timezone. - dt = datetime(2012, 1, 1, 5) - self.assertEqual(tz_harare.localize(dt), tz_local.localize(dt)) - - # Non-zoneinfo timezones are not supported in the TZ environment. - self.assertRaises(pytz.UnknownTimeZoneError, tzlocal.unix._tz_from_env, 'GMT+03:00') - - # Test the _try function - os.environ['TZ'] = 'Africa/Harare' - tz_harare = tzlocal.unix._try_tz_from_env() - self.assertEqual(tz_harare.zone, 'Africa/Harare') - # With a zone that doesn't exist - os.environ['TZ'] = 'Just Nonsense' - tz_harare = tzlocal.unix._try_tz_from_env() - self.assertIsNone(tz_harare) - - - def test_timezone(self): - # Most versions of Ubuntu - - tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'timezone')) - self.assertEqual(tz.zone, 'Africa/Harare') - - def test_zone_setting(self): - # A ZONE setting in /etc/sysconfig/clock, f ex CentOS - - tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'zone_setting')) - self.assertEqual(tz.zone, 'Africa/Harare') - - def test_timezone_setting(self): - # A ZONE setting in /etc/conf.d/clock, f ex Gentoo - - tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'timezone_setting')) - self.assertEqual(tz.zone, 'Africa/Harare') - - def test_symlink_localtime(self): - # A ZONE setting in the target path of a symbolic linked localtime, f ex systemd distributions - - tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'symlink_localtime')) - self.assertEqual(tz.zone, 'Africa/Harare') - - def test_vardbzoneinfo_setting(self): - # A ZONE setting in /etc/conf.d/clock, f ex Gentoo - - tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'vardbzoneinfo')) - self.assertEqual(tz.zone, 'Africa/Harare') - - def test_only_localtime(self): - tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data', 'localtime')) - self.assertEqual(tz.zone, 'local') - dt = datetime(2012, 1, 1, 5) - self.assertEqual(pytz.timezone('Africa/Harare').localize(dt), tz.localize(dt)) - - def test_get_reload(self): - os.environ['TZ'] = 'Africa/Harare' - tz_harare = tzlocal.unix.get_localzone() - self.assertEqual(tz_harare.zone, 'Africa/Harare') - # Changing the TZ makes no difference, because it's cached - os.environ['TZ'] = 'Africa/Johannesburg' - tz_harare = tzlocal.unix.get_localzone() - self.assertEqual(tz_harare.zone, 'Africa/Harare') - # So we reload it - tz_harare = tzlocal.unix.reload_localzone() - self.assertEqual(tz_harare.zone, 'Africa/Johannesburg') - - def test_fail(self): - with self.assertRaises(pytz.exceptions.UnknownTimeZoneError): - tz = tzlocal.unix._get_localzone(_root=os.path.join(self.path, 'test_data')) - -if sys.platform == 'win32': - - import tzlocal.win32 - class TzWin32Tests(unittest.TestCase): - - def test_win32(self): - tzlocal.win32.get_localzone() - -else: - - class TzWin32Tests(unittest.TestCase): - - def test_win32_on_unix(self): - # Yes, winreg is all mocked out, but this test means we at least - # catch syntax errors, etc. - winreg = mock.MagicMock() - winreg.OpenKey = mock.MagicMock() - winreg.OpenKey.close = mock.MagicMock() - winreg.QueryInfoKey = mock.MagicMock(return_value=(1, 1)) - winreg.EnumValue = mock.MagicMock( - return_value=('TimeZoneKeyName','Belarus Standard Time')) - winreg.EnumKey = mock.Mock(return_value='Bahia Standard Time') - sys.modules['winreg'] = winreg - import tzlocal.win32 - tz = tzlocal.win32.get_localzone() - self.assertEqual(tz.zone, 'Europe/Minsk') - - tzlocal.win32.valuestodict = mock.Mock(return_value={ - 'StandardName': 'Mocked Standard Time', - 'Std': 'Mocked Standard Time', - }) - tz = tzlocal.win32.reload_localzone() - self.assertEqual(tz.zone, 'America/Bahia') - -if __name__ == '__main__': - unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/unix.py new/tzlocal-2.0.0/tzlocal/unix.py --- old/tzlocal-1.5.1/tzlocal/unix.py 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/unix.py 2019-07-23 17:12:14.000000000 +0200 @@ -1,15 +1,19 @@ import os -import re import pytz +import re +import warnings + +from tzlocal import utils _cache_tz = None + def _tz_from_env(tzenv): if tzenv[0] == ':': tzenv = tzenv[1:] # TZ specifies a file - if os.path.exists(tzenv): + if os.path.isabs(tzenv) and os.path.exists(tzenv): with open(tzenv, 'rb') as tzfile: return pytz.tzfile.build_tzinfo('local', tzfile) @@ -48,56 +52,85 @@ if tzenv: return tzenv + # Are we under Termux on Android? + if os.path.exists('/system/bin/getprop'): + import subprocess + androidtz = subprocess.check_output(['getprop', 'persist.sys.timezone']).strip().decode() + return pytz.timezone(androidtz) + # Now look for distribution specific configuration files # that contain the timezone name. for configfile in ('etc/timezone', 'var/db/zoneinfo'): tzpath = os.path.join(_root, configfile) - if os.path.exists(tzpath): + 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] == 'TZif2': + 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) - return pytz.timezone(etctz.replace(' ', '_')) + 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 = pytz.timezone(etctz.replace(' ', '_')) + if _root == '/': + # We are using a file in etc to name the timezone. + # Verify that the timezone specified there is actually used: + utils.assert_tz_offset(tz) + 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*\"') + 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) - if not os.path.exists(tzpath): - continue - with open(tzpath, 'rt') as tzfile: - data = tzfile.readlines() + 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()] + 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 = pytz.timezone(etctz.replace(' ', '_')) + if _root == '/': + # We are using a file in etc to name the timezone. + # Verify that the timezone specified there is actually used: + utils.assert_tz_offset(tz) + return tz - # We found a timezone - return pytz.timezone(etctz.replace(' ', '_')) + except IOError: + # File doesn't exist or is a directory + continue # systemd distributions use symlinks that include the zone name, # see manpage of localtime(5) and timedatectl(1) @@ -122,15 +155,18 @@ with open(tzpath, 'rb') as tzfile: return pytz.tzfile.build_tzinfo('local', tzfile) - raise pytz.UnknownTimeZoneError('Can not find any timezone configuration') + warnings.warn('Can not find any timezone configuration, defaulting to UTC.') + return pytz.utc def get_localzone(): """Get the computers configured local timezone, if any.""" global _cache_tz if _cache_tz is None: _cache_tz = _get_localzone() + return _cache_tz + def reload_localzone(): """Reload the cached localzone. You need to call this if the timezone has changed.""" global _cache_tz diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/utils.py new/tzlocal-2.0.0/tzlocal/utils.py --- old/tzlocal-1.5.1/tzlocal/utils.py 1970-01-01 01:00:00.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/utils.py 2019-07-23 17:12:14.000000000 +0200 @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +import datetime + + +def get_system_offset(): + """Get system's timezone offset using built-in library time. + + For the Timezone constants (altzone, daylight, timezone, and tzname), the + value is determined by the timezone rules in effect at module load time or + the last time tzset() is called and may be incorrect for times in the past. + + To keep compatibility with Windows, we're always importing time module here. + """ + import time + if time.daylight and time.localtime().tm_isdst > 0: + return -time.altzone + else: + return -time.timezone + + +def get_tz_offset(tz): + """Get timezone's offset using built-in function datetime.utcoffset().""" + return int(datetime.datetime.now(tz).utcoffset().total_seconds()) + + +def assert_tz_offset(tz): + """Assert that system's timezone offset equals to the timezone offset found. + + If they don't match, we probably have a misconfiguration, for example, an + incorrect timezone set in /etc/timezone file in systemd distributions.""" + tz_offset = get_tz_offset(tz) + system_offset = get_system_offset() + if tz_offset != system_offset: + msg = ('Timezone offset does not match system offset: {0} != {1}. ' + 'Please, check your config files.').format( + tz_offset, system_offset + ) + raise ValueError(msg) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/win32.py new/tzlocal-2.0.0/tzlocal/win32.py --- old/tzlocal-1.5.1/tzlocal/win32.py 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/win32.py 2019-07-23 17:12:14.000000000 +0200 @@ -3,11 +3,14 @@ except ImportError: import winreg -from tzlocal.windows_tz import win_tz import pytz +from tzlocal.windows_tz import win_tz +from tzlocal import utils + _cache_tz = None + def valuestodict(key): """Convert a registry key's values to a dictionary.""" dict = {} @@ -17,6 +20,7 @@ dict[data[0]] = data[1] return dict + def get_localzone_name(): # Windows is special. It has unique time zone names (in several # meanings of the word) available, but unfortunately, they can be @@ -81,15 +85,20 @@ return timezone + def get_localzone(): """Returns the zoneinfo-based tzinfo object that matches the Windows-configured timezone.""" global _cache_tz if _cache_tz is None: _cache_tz = pytz.timezone(get_localzone_name()) + + utils.assert_tz_offset(_cache_tz) return _cache_tz + def reload_localzone(): """Reload the cached localzone. You need to call this if the timezone has changed.""" global _cache_tz _cache_tz = pytz.timezone(get_localzone_name()) + utils.assert_tz_offset(_cache_tz) return _cache_tz diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/tzlocal/windows_tz.py new/tzlocal-2.0.0/tzlocal/windows_tz.py --- old/tzlocal-1.5.1/tzlocal/windows_tz.py 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/tzlocal/windows_tz.py 2019-07-23 17:12:14.000000000 +0200 @@ -99,10 +99,12 @@ 'Saint Pierre Standard Time': 'America/Miquelon', 'Sakhalin Standard Time': 'Asia/Sakhalin', 'Samoa Standard Time': 'Pacific/Apia', + 'Sao Tome Standard Time': 'Africa/Sao_Tome', 'Saratov Standard Time': 'Europe/Saratov', 'Singapore Standard Time': 'Asia/Singapore', 'South Africa Standard Time': 'Africa/Johannesburg', 'Sri Lanka Standard Time': 'Asia/Colombo', + 'Sudan Standard Time': 'Africa/Khartoum', 'Syria Standard Time': 'Asia/Damascus', 'Taipei Standard Time': 'Asia/Taipei', 'Tasmania Standard Time': 'Australia/Hobart', @@ -164,7 +166,7 @@ 'Africa/Johannesburg': 'South Africa Standard Time', 'Africa/Juba': 'E. Africa Standard Time', 'Africa/Kampala': 'E. Africa Standard Time', - 'Africa/Khartoum': 'E. Africa Standard Time', + 'Africa/Khartoum': 'Sudan Standard Time', 'Africa/Kigali': 'South Africa Standard Time', 'Africa/Kinshasa': 'W. Central Africa Standard Time', 'Africa/Lagos': 'W. Central Africa Standard Time', @@ -185,7 +187,7 @@ 'Africa/Nouakchott': 'Greenwich Standard Time', 'Africa/Ouagadougou': 'Greenwich Standard Time', 'Africa/Porto-Novo': 'W. Central Africa Standard Time', - 'Africa/Sao_Tome': 'Greenwich Standard Time', + 'Africa/Sao_Tome': 'Sao Tome Standard Time', 'Africa/Timbuktu': 'Greenwich Standard Time', 'Africa/Tripoli': 'Libya Standard Time', 'Africa/Tunis': 'W. Central Africa Standard Time', @@ -285,7 +287,7 @@ 'America/Mendoza': 'Argentina Standard Time', 'America/Menominee': 'Central Standard Time', 'America/Merida': 'Central Standard Time (Mexico)', - 'America/Metlakatla': 'Alaskan Standard Time', + 'America/Metlakatla': 'Pacific Standard Time', 'America/Mexico_City': 'Central Standard Time (Mexico)', 'America/Miquelon': 'Saint Pierre Standard Time', 'America/Moncton': 'Atlantic Standard Time', @@ -345,7 +347,7 @@ 'America/Winnipeg': 'Central Standard Time', 'America/Yakutat': 'Alaskan Standard Time', 'America/Yellowknife': 'Mountain Standard Time', - 'Antarctica/Casey': 'Central Pacific Standard Time', + 'Antarctica/Casey': 'W. Australia Standard Time', 'Antarctica/Davis': 'SE Asia Standard Time', 'Antarctica/DumontDUrville': 'West Pacific Standard Time', 'Antarctica/Macquarie': 'Central Pacific Standard Time', @@ -386,7 +388,7 @@ 'Asia/Dili': 'Tokyo Standard Time', 'Asia/Dubai': 'Arabian Standard Time', 'Asia/Dushanbe': 'West Asia Standard Time', - 'Asia/Famagusta': 'Turkey Standard Time', + 'Asia/Famagusta': 'GTB Standard Time', 'Asia/Gaza': 'West Bank Standard Time', 'Asia/Harbin': 'China Standard Time', 'Asia/Hebron': 'West Bank Standard Time', @@ -421,7 +423,8 @@ 'Asia/Pontianak': 'SE Asia Standard Time', 'Asia/Pyongyang': 'North Korea Standard Time', 'Asia/Qatar': 'Arab Standard Time', - 'Asia/Qyzylorda': 'Central Asia Standard Time', + 'Asia/Qostanay': 'Central Asia Standard Time', + 'Asia/Qyzylorda': 'West Asia Standard Time', 'Asia/Rangoon': 'Myanmar Standard Time', 'Asia/Riyadh': 'Arab Standard Time', 'Asia/Saigon': 'SE Asia Standard Time', @@ -530,6 +533,7 @@ 'Etc/GMT-7': 'SE Asia Standard Time', 'Etc/GMT-8': 'Singapore Standard Time', 'Etc/GMT-9': 'Tokyo Standard Time', + 'Etc/UCT': 'UTC', 'Etc/UTC': 'UTC', 'Europe/Amsterdam': 'W. Europe Standard Time', 'Europe/Andorra': 'W. Europe Standard Time', @@ -673,6 +677,7 @@ 'ROK': 'Korea Standard Time', 'Singapore': 'Singapore Standard Time', 'Turkey': 'Turkey Standard Time', + 'UCT': 'UTC', 'US/Alaska': 'Alaskan Standard Time', 'US/Aleutian': 'Aleutian Standard Time', 'US/Arizona': 'US Mountain Standard Time', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tzlocal-1.5.1/update_windows_mappings.py new/tzlocal-2.0.0/update_windows_mappings.py --- old/tzlocal-1.5.1/update_windows_mappings.py 2017-12-01 10:37:45.000000000 +0100 +++ new/tzlocal-2.0.0/update_windows_mappings.py 2019-07-23 17:12:14.000000000 +0200 @@ -16,7 +16,7 @@ from urllib.request import urlopen from xml.dom import minidom -WIN_ZONES_URL = 'http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml' +WIN_ZONES_URL = 'https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml' ZONEINFO_URL = 'ftp://ftp.iana.org/tz/tzdata-latest.tar.gz' logging.basicConfig(level=logging.INFO)
