From 3d04299516f6751d35ea5e00e0517f7e1874a7b6 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 16:44:44 +0200 Subject: [PATCH 01/15] Handle ZoneInfo objects in get_timezone_location, get_timezone_name --- babel/dates.py | 8 ++++++-- tests/test_dates.py | 25 ++++++++++++++----------- tox.ini | 1 + 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/babel/dates.py b/babel/dates.py index f1bd66faf..e0e6eae4d 100644 --- a/babel/dates.py +++ b/babel/dates.py @@ -503,8 +503,10 @@ def get_timezone_location(dt_or_tzinfo=None, locale=LC_TIME, return_city=False): dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) locale = Locale.parse(locale) - if hasattr(tzinfo, 'zone'): + if hasattr(tzinfo, 'zone'): # pytz object zone = tzinfo.zone + elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object + zone = tzinfo.key else: zone = tzinfo.tzname(dt or datetime.utcnow()) @@ -619,8 +621,10 @@ def get_timezone_name(dt_or_tzinfo=None, width='long', uncommon=False, dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) locale = Locale.parse(locale) - if hasattr(tzinfo, 'zone'): + if hasattr(tzinfo, 'zone'): # pytz object zone = tzinfo.zone + elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object + zone = tzinfo.key else: zone = tzinfo.tzname(dt) diff --git a/tests/test_dates.py b/tests/test_dates.py index 5be0d16a1..4c53038bc 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -15,6 +15,7 @@ from datetime import date, datetime, time, timedelta import unittest +from backports import zoneinfo import pytest import pytz from pytz import timezone @@ -583,8 +584,9 @@ def test_get_timezone_gmt(): assert dates.get_timezone_gmt(dt, 'long', locale='fr_FR') == u'UTC-07:00' -def test_get_timezone_location(): - tz = timezone('America/St_Johns') +@pytest.mark.parametrize("timezone_creator", [timezone, zoneinfo.ZoneInfo]) +def test_get_timezone_location(timezone_creator): + tz = timezone_creator('America/St_Johns') assert (dates.get_timezone_location(tz, locale='de_DE') == u"Kanada (St. John\u2019s) Zeit") assert (dates.get_timezone_location(tz, locale='en') == @@ -592,37 +594,38 @@ def test_get_timezone_location(): assert (dates.get_timezone_location(tz, locale='en', return_city=True) == u'St. John’s') - tz = timezone('America/Mexico_City') + tz = timezone_creator('America/Mexico_City') assert (dates.get_timezone_location(tz, locale='de_DE') == u'Mexiko (Mexiko-Stadt) Zeit') - tz = timezone('Europe/Berlin') + tz = timezone_creator('Europe/Berlin') assert (dates.get_timezone_location(tz, locale='de_DE') == u'Deutschland (Berlin) Zeit') -def test_get_timezone_name(): - dt = time(15, 30, tzinfo=timezone('America/Los_Angeles')) +@pytest.mark.parametrize("timezone_creator", [timezone, zoneinfo.ZoneInfo]) +def test_get_timezone_name(timezone_creator): + dt = time(15, 30, tzinfo=timezone_creator('America/Los_Angeles')) assert (dates.get_timezone_name(dt, locale='en_US') == u'Pacific Standard Time') assert (dates.get_timezone_name(dt, locale='en_US', return_zone=True) == u'America/Los_Angeles') assert dates.get_timezone_name(dt, width='short', locale='en_US') == u'PST' - tz = timezone('America/Los_Angeles') + tz = timezone_creator('America/Los_Angeles') assert dates.get_timezone_name(tz, locale='en_US') == u'Pacific Time' assert dates.get_timezone_name(tz, 'short', locale='en_US') == u'PT' - tz = timezone('Europe/Berlin') + tz = timezone_creator('Europe/Berlin') assert (dates.get_timezone_name(tz, locale='de_DE') == u'Mitteleurop\xe4ische Zeit') assert (dates.get_timezone_name(tz, locale='pt_BR') == u'Hor\xe1rio da Europa Central') - tz = timezone('America/St_Johns') + tz = timezone_creator('America/St_Johns') assert dates.get_timezone_name(tz, locale='de_DE') == u'Neufundland-Zeit' - tz = timezone('America/Los_Angeles') + tz = timezone_creator('America/Los_Angeles') assert dates.get_timezone_name(tz, locale='en', width='short', zone_variant='generic') == u'PT' assert dates.get_timezone_name(tz, locale='en', width='short', @@ -636,7 +639,7 @@ def test_get_timezone_name(): assert dates.get_timezone_name(tz, locale='en', width='long', zone_variant='daylight') == u'Pacific Daylight Time' - localnow = datetime.utcnow().replace(tzinfo=timezone('UTC')).astimezone(dates.LOCALTZ) + localnow = datetime.utcnow().replace(tzinfo=timezone_creator('UTC')).astimezone(dates.LOCALTZ) assert (dates.get_timezone_name(None, locale='en_US') == dates.get_timezone_name(localnow, locale='en_US')) diff --git a/tox.ini b/tox.ini index eccffea94..fe353b8d6 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ deps = pytest-cov==2.6.1 cdecimal: m3-cdecimal freezegun==0.3.12 + backports.zoneinfo whitelist_externals = make commands = make clean-cldr test passenv = PYTHON_TEST_FLAGS From eb89746cc470f38f22f6485349d170ef4c128daa Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 16:52:02 +0200 Subject: [PATCH 02/15] Add test dependency `backports.zoneinfo` to the appveyor configuration --- .ci/appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/appveyor.yml b/.ci/appveyor.yml index 91758f415..954c6a955 100644 --- a/.ci/appveyor.yml +++ b/.ci/appveyor.yml @@ -27,7 +27,7 @@ install: - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" # Build data files - - "pip install --upgrade pytest==4.3.1 pytest-cov==2.6.1 codecov freezegun==0.3.12" + - "pip install --upgrade pytest==4.3.1 pytest-cov==2.6.1 codecov freezegun==0.3.12 backports.zoneinfo" - "pip install --editable ." - "python setup.py import_cldr" From 2321f3937a5377f428ff61d0a605d5f4b3d3dbea Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 16:54:45 +0200 Subject: [PATCH 03/15] Add test dependency `backports.zoneinfo` to the travis configuration --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9650f674c..c34f20930 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ matrix: install: - bash .ci/deps.${TRAVIS_OS_NAME}.sh - pip install --upgrade pip - - pip install --upgrade $CDECIMAL pytest==4.3.1 pytest-cov==2.6.1 freezegun==0.3.12 + - pip install --upgrade $CDECIMAL pytest==4.3.1 pytest-cov==2.6.1 freezegun==0.3.12 backports.zoneinfo - pip install --editable . script: From 662bb1241b5eb72cee5b318962e5219a2e6291b1 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 17:07:28 +0200 Subject: [PATCH 04/15] Remove `backports.zoneinfo` dependency to configurations that don't allow it. Run the tests for zoneinfo only if available --- .ci/appveyor.yml | 2 +- .travis.yml | 2 +- tests/test_dates.py | 31 ++++++++++++++++++++++++++++--- tox.ini | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/.ci/appveyor.yml b/.ci/appveyor.yml index 954c6a955..5f8bf9efd 100644 --- a/.ci/appveyor.yml +++ b/.ci/appveyor.yml @@ -27,7 +27,7 @@ install: - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" # Build data files - - "pip install --upgrade pytest==4.3.1 pytest-cov==2.6.1 codecov freezegun==0.3.12 backports.zoneinfo" + - 'pip install --upgrade pytest==4.3.1 pytest-cov==2.6.1 codecov freezegun==0.3.12' - "pip install --editable ." - "python setup.py import_cldr" diff --git a/.travis.yml b/.travis.yml index c34f20930..987a8c34b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ matrix: install: - bash .ci/deps.${TRAVIS_OS_NAME}.sh - pip install --upgrade pip - - pip install --upgrade $CDECIMAL pytest==4.3.1 pytest-cov==2.6.1 freezegun==0.3.12 backports.zoneinfo + - pip install --upgrade $CDECIMAL pytest==4.3.1 pytest-cov==2.6.1 freezegun==0.3.12 backports.zoneinfo;python_version<"3.9" - pip install --editable . script: diff --git a/tests/test_dates.py b/tests/test_dates.py index 4c53038bc..0a889d883 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -15,7 +15,14 @@ from datetime import date, datetime, time, timedelta import unittest -from backports import zoneinfo +try: + import zoneinfo +except ImportError: + try: + from backports import zoneinfo + except ImportError: + zoneinfo = None + import pytest import pytz from pytz import timezone @@ -584,7 +591,16 @@ def test_get_timezone_gmt(): assert dates.get_timezone_gmt(dt, 'long', locale='fr_FR') == u'UTC-07:00' -@pytest.mark.parametrize("timezone_creator", [timezone, zoneinfo.ZoneInfo]) +@pytest.mark.parametrize( + "timezone_creator", + [ + timezone, + pytest.param( + zoneinfo.ZoneInfo, + marks=pytest.mark.skipif(zoneinfo is None, reason="zoneinfo not available"), + ), + ], +) def test_get_timezone_location(timezone_creator): tz = timezone_creator('America/St_Johns') assert (dates.get_timezone_location(tz, locale='de_DE') == @@ -603,7 +619,16 @@ def test_get_timezone_location(timezone_creator): u'Deutschland (Berlin) Zeit') -@pytest.mark.parametrize("timezone_creator", [timezone, zoneinfo.ZoneInfo]) +@pytest.mark.parametrize( + "timezone_creator", + [ + timezone, + pytest.param( + zoneinfo.ZoneInfo, + marks=pytest.mark.skipif(zoneinfo is None, reason="zoneinfo not available"), + ), + ], +) def test_get_timezone_name(timezone_creator): dt = time(15, 30, tzinfo=timezone_creator('America/Los_Angeles')) assert (dates.get_timezone_name(dt, locale='en_US') == diff --git a/tox.ini b/tox.ini index fe353b8d6..f2c1bc40d 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ deps = pytest-cov==2.6.1 cdecimal: m3-cdecimal freezegun==0.3.12 - backports.zoneinfo + backports.zoneinfo;python_version<"3.9" whitelist_externals = make commands = make clean-cldr test passenv = PYTHON_TEST_FLAGS From dd8aabd01d831ccbaab20e5434137c744fd3254c Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 17:08:04 +0200 Subject: [PATCH 05/15] restore double quotes --- .ci/appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/appveyor.yml b/.ci/appveyor.yml index 5f8bf9efd..91758f415 100644 --- a/.ci/appveyor.yml +++ b/.ci/appveyor.yml @@ -27,7 +27,7 @@ install: - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" # Build data files - - 'pip install --upgrade pytest==4.3.1 pytest-cov==2.6.1 codecov freezegun==0.3.12' + - "pip install --upgrade pytest==4.3.1 pytest-cov==2.6.1 codecov freezegun==0.3.12" - "pip install --editable ." - "python setup.py import_cldr" From 5beb90722f95d241c434198d3fa4f12b25fb90e0 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 17:16:54 +0200 Subject: [PATCH 06/15] install backports.zoneinfo only on pythons that support it --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 987a8c34b..57431d7e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ matrix: install: - bash .ci/deps.${TRAVIS_OS_NAME}.sh - pip install --upgrade pip - - pip install --upgrade $CDECIMAL pytest==4.3.1 pytest-cov==2.6.1 freezegun==0.3.12 backports.zoneinfo;python_version<"3.9" + - pip install --upgrade $CDECIMAL pytest==4.3.1 pytest-cov==2.6.1 freezegun==0.3.12 'backports.zoneinfo;python_version>="3.6" and python_version<"3.9"' - pip install --editable . script: From b6696f67dd168da55cd33e74becc1de0bfdc44b2 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 17:16:54 +0200 Subject: [PATCH 07/15] install backports.zoneinfo only on pythons that support it --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index f2c1bc40d..e159d1d21 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ deps = pytest-cov==2.6.1 cdecimal: m3-cdecimal freezegun==0.3.12 - backports.zoneinfo;python_version<"3.9" + backports.zoneinfo;python_version>"3.7" and python_version<"3.9" whitelist_externals = make commands = make clean-cldr test passenv = PYTHON_TEST_FLAGS From 7f69dccc136a2c6034a188d5f6557490ba96ab33 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 17:23:56 +0200 Subject: [PATCH 08/15] don't access zoneinfo.ZoneInfo at import time, some configurations don't support it --- tests/test_dates.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_dates.py b/tests/test_dates.py index 0a889d883..71dd05250 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -596,7 +596,7 @@ def test_get_timezone_gmt(): [ timezone, pytest.param( - zoneinfo.ZoneInfo, + lambda v: zoneinfo.ZoneInfo(v), marks=pytest.mark.skipif(zoneinfo is None, reason="zoneinfo not available"), ), ], @@ -624,7 +624,7 @@ def test_get_timezone_location(timezone_creator): [ timezone, pytest.param( - zoneinfo.ZoneInfo, + lambda v: zoneinfo.ZoneInfo(v), marks=pytest.mark.skipif(zoneinfo is None, reason="zoneinfo not available"), ), ], From 82ffa23b1331c1f86f1484c25f83796a0f466f39 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 17:44:09 +0200 Subject: [PATCH 09/15] define a module_level timezone_getter, simplify test by using only parametrization --- tests/test_dates.py | 105 ++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 62 deletions(-) diff --git a/tests/test_dates.py b/tests/test_dates.py index 71dd05250..558208946 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -15,14 +15,6 @@ from datetime import date, datetime, time, timedelta import unittest -try: - import zoneinfo -except ImportError: - try: - from backports import zoneinfo - except ImportError: - zoneinfo = None - import pytest import pytz from pytz import timezone @@ -32,6 +24,23 @@ from babel.util import FixedOffsetTimezone +@pytest.fixture(scope="session", params=["pytz.timezone", "zoneinfo.ZoneInfo"]) +def timezone_getter(request): + if request.param == "pytz.timezone": + return timezone + elif request.param == "zoneinfo.ZoneInfo": + try: + import zoneinfo + except ImportError: + try: + from backports import zoneinfo + except ImportError: + pytest.skip("zoneinfo not available") + return zoneinfo.ZoneInfo + else: + raise NotImplementedError + + class DateTimeFormatTestCase(unittest.TestCase): def test_quarter_format(self): @@ -591,18 +600,8 @@ def test_get_timezone_gmt(): assert dates.get_timezone_gmt(dt, 'long', locale='fr_FR') == u'UTC-07:00' -@pytest.mark.parametrize( - "timezone_creator", - [ - timezone, - pytest.param( - lambda v: zoneinfo.ZoneInfo(v), - marks=pytest.mark.skipif(zoneinfo is None, reason="zoneinfo not available"), - ), - ], -) -def test_get_timezone_location(timezone_creator): - tz = timezone_creator('America/St_Johns') +def test_get_timezone_location(timezone_getter): + tz = timezone_getter('America/St_Johns') assert (dates.get_timezone_location(tz, locale='de_DE') == u"Kanada (St. John\u2019s) Zeit") assert (dates.get_timezone_location(tz, locale='en') == @@ -610,61 +609,43 @@ def test_get_timezone_location(timezone_creator): assert (dates.get_timezone_location(tz, locale='en', return_city=True) == u'St. John’s') - tz = timezone_creator('America/Mexico_City') + tz = timezone_getter('America/Mexico_City') assert (dates.get_timezone_location(tz, locale='de_DE') == u'Mexiko (Mexiko-Stadt) Zeit') - tz = timezone_creator('Europe/Berlin') + tz = timezone_getter('Europe/Berlin') assert (dates.get_timezone_location(tz, locale='de_DE') == u'Deutschland (Berlin) Zeit') -@pytest.mark.parametrize( - "timezone_creator", - [ - timezone, - pytest.param( - lambda v: zoneinfo.ZoneInfo(v), - marks=pytest.mark.skipif(zoneinfo is None, reason="zoneinfo not available"), - ), - ], -) -def test_get_timezone_name(timezone_creator): - dt = time(15, 30, tzinfo=timezone_creator('America/Los_Angeles')) +@pytest.mark.parametrize("tzname, params, expected", [ + ("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Time"), + ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PT"), + ("Europe/Berlin", {"locale": "de_DE"}, u"Mitteleurop\xe4ische Zeit"), + ("Europe/Berlin", {"locale": "pt_BR"}, u"Hor\xe1rio da Europa Central"), + ("America/St_Johns", {"locale": "de_DE"}, u'Neufundland-Zeit'), + ("America/Los_Angeles", {"locale": "en", "width": "short", "zone_variant": "generic"}, u'PT'), + ("America/Los_Angeles", {"locale": "en", "width": "short", "zone_variant": "standard"}, u'PST'), + ("America/Los_Angeles", {"locale": "en", "width": "short", "zone_variant": "daylight"}, u'PDT'), + ("America/Los_Angeles", {"locale": "en", "width": "long", "zone_variant": "generic"}, u'Pacific Time'), + ("America/Los_Angeles", {"locale": "en", "width": "long", "zone_variant": "standard"}, u'Pacific Standard Time'), + ("America/Los_Angeles", {"locale": "en", "width": "long", "zone_variant": "daylight"}, u'Pacific Daylight Time'), + ("Europe/Berlin", {"locale": "en_US"}, u'Central European Time'), +]) +def test_get_timezone_name_tz(timezone_getter, tzname, params, expected): + tz = timezone_getter(tzname) + assert dates.get_timezone_name(tz, **params) == expected + + +def test_get_timezone_name_non_tzinfo(timezone_getter): + dt = time(15, 30, tzinfo=timezone_getter('America/Los_Angeles')) assert (dates.get_timezone_name(dt, locale='en_US') == u'Pacific Standard Time') assert (dates.get_timezone_name(dt, locale='en_US', return_zone=True) == u'America/Los_Angeles') assert dates.get_timezone_name(dt, width='short', locale='en_US') == u'PST' - tz = timezone_creator('America/Los_Angeles') - assert dates.get_timezone_name(tz, locale='en_US') == u'Pacific Time' - assert dates.get_timezone_name(tz, 'short', locale='en_US') == u'PT' - - tz = timezone_creator('Europe/Berlin') - assert (dates.get_timezone_name(tz, locale='de_DE') == - u'Mitteleurop\xe4ische Zeit') - assert (dates.get_timezone_name(tz, locale='pt_BR') == - u'Hor\xe1rio da Europa Central') - - tz = timezone_creator('America/St_Johns') - assert dates.get_timezone_name(tz, locale='de_DE') == u'Neufundland-Zeit' - - tz = timezone_creator('America/Los_Angeles') - assert dates.get_timezone_name(tz, locale='en', width='short', - zone_variant='generic') == u'PT' - assert dates.get_timezone_name(tz, locale='en', width='short', - zone_variant='standard') == u'PST' - assert dates.get_timezone_name(tz, locale='en', width='short', - zone_variant='daylight') == u'PDT' - assert dates.get_timezone_name(tz, locale='en', width='long', - zone_variant='generic') == u'Pacific Time' - assert dates.get_timezone_name(tz, locale='en', width='long', - zone_variant='standard') == u'Pacific Standard Time' - assert dates.get_timezone_name(tz, locale='en', width='long', - zone_variant='daylight') == u'Pacific Daylight Time' - - localnow = datetime.utcnow().replace(tzinfo=timezone_creator('UTC')).astimezone(dates.LOCALTZ) + localnow = datetime.utcnow().replace(tzinfo=timezone_getter('UTC')).astimezone(dates.LOCALTZ) assert (dates.get_timezone_name(None, locale='en_US') == dates.get_timezone_name(localnow, locale='en_US')) From de2d2db61573549bcbc019902577d79eb44065ad Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 18:01:12 +0200 Subject: [PATCH 10/15] use more pytest parametrization, so that we can immediately which individual cases fail --- tests/test_dates.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/test_dates.py b/tests/test_dates.py index 558208946..62fddcb62 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -632,19 +632,22 @@ def test_get_timezone_location(timezone_getter): ("America/Los_Angeles", {"locale": "en", "width": "long", "zone_variant": "daylight"}, u'Pacific Daylight Time'), ("Europe/Berlin", {"locale": "en_US"}, u'Central European Time'), ]) -def test_get_timezone_name_tz(timezone_getter, tzname, params, expected): +def test_get_timezone_name_tzinfo(timezone_getter, tzname, params, expected): tz = timezone_getter(tzname) assert dates.get_timezone_name(tz, **params) == expected -def test_get_timezone_name_non_tzinfo(timezone_getter): - dt = time(15, 30, tzinfo=timezone_getter('America/Los_Angeles')) - assert (dates.get_timezone_name(dt, locale='en_US') == - u'Pacific Standard Time') - assert (dates.get_timezone_name(dt, locale='en_US', return_zone=True) == - u'America/Los_Angeles') - assert dates.get_timezone_name(dt, width='short', locale='en_US') == u'PST' +@pytest.mark.parametrize("tzname, params, expected", [ + ("America/Los_Angeles", {"locale": "en_US"}, u'Pacific Standard Time'), + ("America/Los_Angeles", {"locale": "en_US", "return_zone": True}, u'America/Los_Angeles'), + ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u'PST'), +]) +def test_get_timezone_name_time(timezone_getter, tzname, params, expected): + dt = time(15, 30, tzinfo=timezone_getter(tzname)) + assert dates.get_timezone_name(dt, **params) == expected + +def test_get_timezone_name_misc(timezone_getter): localnow = datetime.utcnow().replace(tzinfo=timezone_getter('UTC')).astimezone(dates.LOCALTZ) assert (dates.get_timezone_name(None, locale='en_US') == dates.get_timezone_name(localnow, locale='en_US')) From 7c01f5030c646617e1ea3f7d63e793b4ea103f3d Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 18:09:47 +0200 Subject: [PATCH 11/15] Distinguish test case for pytz broken behaviour and for zoneinfo correct behaviour. --- tests/test_dates.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test_dates.py b/tests/test_dates.py index 62fddcb62..ee9c75c86 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -24,7 +24,7 @@ from babel.util import FixedOffsetTimezone -@pytest.fixture(scope="session", params=["pytz.timezone", "zoneinfo.ZoneInfo"]) +@pytest.fixture(params=["pytz.timezone", "zoneinfo.ZoneInfo"]) def timezone_getter(request): if request.param == "pytz.timezone": return timezone @@ -637,12 +637,28 @@ def test_get_timezone_name_tzinfo(timezone_getter, tzname, params, expected): assert dates.get_timezone_name(tz, **params) == expected +@pytest.mark.parametrize("timezone_getter", ["pytz.timezone"], indirect=True) @pytest.mark.parametrize("tzname, params, expected", [ ("America/Los_Angeles", {"locale": "en_US"}, u'Pacific Standard Time'), ("America/Los_Angeles", {"locale": "en_US", "return_zone": True}, u'America/Los_Angeles'), ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u'PST'), ]) -def test_get_timezone_name_time(timezone_getter, tzname, params, expected): +def test_get_timezone_name_time_pytz(timezone_getter, tzname, params, expected): + """pytz design can't find out if the time is in dst or not, so it will always return Standard time""" + dt = time(15, 30, tzinfo=timezone_getter(tzname)) + assert dates.get_timezone_name(dt, **params) == expected + + +@pytest.mark.parametrize("tzname, params, expected", [ + ("America/Los_Angeles", {"locale": "en_US"}, u'Pacific Daylight Time'), + ("America/Los_Angeles", {"locale": "en_US", "return_zone": True}, u'America/Los_Angeles'), + ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u'PDT'), +]) +@pytest.mark.parametrize("timezone_getter", ["zoneinfo.ZoneInfo"], indirect=True) +def test_get_timezone_name_time_zoneinfo(timezone_getter, tzname, params, expected): + """zoneinfo will actually return dst/non dst correctly + FIXME: this test will only succeed in the winter. + """ dt = time(15, 30, tzinfo=timezone_getter(tzname)) assert dates.get_timezone_name(dt, **params) == expected From 0d8637fe42d31a32d10c7ad7c73358a121a62fa3 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 18:12:36 +0200 Subject: [PATCH 12/15] Fit code in 80 columns, update comment --- tests/test_dates.py | 94 ++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/tests/test_dates.py b/tests/test_dates.py index ee9c75c86..75f362733 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -618,45 +618,87 @@ def test_get_timezone_location(timezone_getter): u'Deutschland (Berlin) Zeit') -@pytest.mark.parametrize("tzname, params, expected", [ - ("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Time"), - ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PT"), - ("Europe/Berlin", {"locale": "de_DE"}, u"Mitteleurop\xe4ische Zeit"), - ("Europe/Berlin", {"locale": "pt_BR"}, u"Hor\xe1rio da Europa Central"), - ("America/St_Johns", {"locale": "de_DE"}, u'Neufundland-Zeit'), - ("America/Los_Angeles", {"locale": "en", "width": "short", "zone_variant": "generic"}, u'PT'), - ("America/Los_Angeles", {"locale": "en", "width": "short", "zone_variant": "standard"}, u'PST'), - ("America/Los_Angeles", {"locale": "en", "width": "short", "zone_variant": "daylight"}, u'PDT'), - ("America/Los_Angeles", {"locale": "en", "width": "long", "zone_variant": "generic"}, u'Pacific Time'), - ("America/Los_Angeles", {"locale": "en", "width": "long", "zone_variant": "standard"}, u'Pacific Standard Time'), - ("America/Los_Angeles", {"locale": "en", "width": "long", "zone_variant": "daylight"}, u'Pacific Daylight Time'), - ("Europe/Berlin", {"locale": "en_US"}, u'Central European Time'), -]) +@pytest.mark.parametrize( + "tzname, params, expected", + [ + ("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Time"), + ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PT"), + ("Europe/Berlin", {"locale": "de_DE"}, u"Mitteleurop\xe4ische Zeit"), + ("Europe/Berlin", {"locale": "pt_BR"}, u"Hor\xe1rio da Europa Central"), + ("America/St_Johns", {"locale": "de_DE"}, u"Neufundland-Zeit"), + ( + "America/Los_Angeles", + {"locale": "en", "width": "short", "zone_variant": "generic"}, + u"PT", + ), + ( + "America/Los_Angeles", + {"locale": "en", "width": "short", "zone_variant": "standard"}, + u"PST", + ), + ( + "America/Los_Angeles", + {"locale": "en", "width": "short", "zone_variant": "daylight"}, + u"PDT", + ), + ( + "America/Los_Angeles", + {"locale": "en", "width": "long", "zone_variant": "generic"}, + u"Pacific Time", + ), + ( + "America/Los_Angeles", + {"locale": "en", "width": "long", "zone_variant": "standard"}, + u"Pacific Standard Time", + ), + ( + "America/Los_Angeles", + {"locale": "en", "width": "long", "zone_variant": "daylight"}, + u"Pacific Daylight Time", + ), + ("Europe/Berlin", {"locale": "en_US"}, u"Central European Time"), + ], +) def test_get_timezone_name_tzinfo(timezone_getter, tzname, params, expected): tz = timezone_getter(tzname) assert dates.get_timezone_name(tz, **params) == expected @pytest.mark.parametrize("timezone_getter", ["pytz.timezone"], indirect=True) -@pytest.mark.parametrize("tzname, params, expected", [ - ("America/Los_Angeles", {"locale": "en_US"}, u'Pacific Standard Time'), - ("America/Los_Angeles", {"locale": "en_US", "return_zone": True}, u'America/Los_Angeles'), - ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u'PST'), -]) +@pytest.mark.parametrize( + "tzname, params, expected", + [ + ("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Standard Time"), + ( + "America/Los_Angeles", + {"locale": "en_US", "return_zone": True}, + u"America/Los_Angeles", + ), + ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PST"), + ], +) def test_get_timezone_name_time_pytz(timezone_getter, tzname, params, expected): - """pytz design can't find out if the time is in dst or not, so it will always return Standard time""" + """pytz (by design) can't determine if the time is in DST or not, + so it will always return Standard time""" dt = time(15, 30, tzinfo=timezone_getter(tzname)) assert dates.get_timezone_name(dt, **params) == expected -@pytest.mark.parametrize("tzname, params, expected", [ - ("America/Los_Angeles", {"locale": "en_US"}, u'Pacific Daylight Time'), - ("America/Los_Angeles", {"locale": "en_US", "return_zone": True}, u'America/Los_Angeles'), - ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u'PDT'), -]) +@pytest.mark.parametrize( + "tzname, params, expected", + [ + ("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Daylight Time"), + ( + "America/Los_Angeles", + {"locale": "en_US", "return_zone": True}, + u"America/Los_Angeles", + ), + ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PDT"), + ], +) @pytest.mark.parametrize("timezone_getter", ["zoneinfo.ZoneInfo"], indirect=True) def test_get_timezone_name_time_zoneinfo(timezone_getter, tzname, params, expected): - """zoneinfo will actually return dst/non dst correctly + """zoneinfo will correctly detect DST from the object. FIXME: this test will only succeed in the winter. """ dt = time(15, 30, tzinfo=timezone_getter(tzname)) From 5118465ad466ce157051fcef968b9e29fae1fefe Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Sun, 11 Oct 2020 18:15:21 +0200 Subject: [PATCH 13/15] backports.zoneinfo should be installed also with python 3.6 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index e159d1d21..c069c5942 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ deps = pytest-cov==2.6.1 cdecimal: m3-cdecimal freezegun==0.3.12 - backports.zoneinfo;python_version>"3.7" and python_version<"3.9" + backports.zoneinfo;python_version>"3.6" and python_version<"3.9" whitelist_externals = make commands = make clean-cldr test passenv = PYTHON_TEST_FLAGS From 5d1ab018b99130bfe4ab80e4c0ab3f2000087ad2 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Tue, 13 Oct 2020 17:06:41 +0200 Subject: [PATCH 14/15] Extract code into private function --- babel/dates.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/babel/dates.py b/babel/dates.py index e0e6eae4d..1775029fa 100644 --- a/babel/dates.py +++ b/babel/dates.py @@ -76,6 +76,21 @@ def _get_dt_and_tzinfo(dt_or_tzinfo): return dt, tzinfo +def _get_tz_name(dt_or_tzinfo): + """ + Get the timezone name out of a time, datetime, or tzinfo object. + + :rtype: str + """ + dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) + if hasattr(tzinfo, 'zone'): # pytz object + return tzinfo.zone + elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object + return tzinfo.key + else: + return tzinfo.tzname(dt or datetime.utcnow()) + + def _get_datetime(instant): """ Get a datetime out of an "instant" (date, time, datetime, number). @@ -500,15 +515,9 @@ def get_timezone_location(dt_or_tzinfo=None, locale=LC_TIME, return_city=False): :return: the localized timezone name using location format """ - dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) locale = Locale.parse(locale) - if hasattr(tzinfo, 'zone'): # pytz object - zone = tzinfo.zone - elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object - zone = tzinfo.key - else: - zone = tzinfo.tzname(dt or datetime.utcnow()) + zone = _get_tz_name(dt_or_tzinfo) # Get the canonical time-zone code zone = get_global('zone_aliases').get(zone, zone) @@ -621,12 +630,7 @@ def get_timezone_name(dt_or_tzinfo=None, width='long', uncommon=False, dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) locale = Locale.parse(locale) - if hasattr(tzinfo, 'zone'): # pytz object - zone = tzinfo.zone - elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object - zone = tzinfo.key - else: - zone = tzinfo.tzname(dt) + zone = _get_tz_name(dt_or_tzinfo) if zone_variant is None: if dt is None: From ef7bd8250a7346c114588932b6910d8780490aa6 Mon Sep 17 00:00:00 2001 From: Alessio Bogon Date: Wed, 21 Oct 2020 11:32:32 +0200 Subject: [PATCH 15/15] Remove test that doesn't make much sense in the first place --- tests/test_dates.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/tests/test_dates.py b/tests/test_dates.py index 75f362733..423f737de 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -684,27 +684,6 @@ def test_get_timezone_name_time_pytz(timezone_getter, tzname, params, expected): assert dates.get_timezone_name(dt, **params) == expected -@pytest.mark.parametrize( - "tzname, params, expected", - [ - ("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Daylight Time"), - ( - "America/Los_Angeles", - {"locale": "en_US", "return_zone": True}, - u"America/Los_Angeles", - ), - ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PDT"), - ], -) -@pytest.mark.parametrize("timezone_getter", ["zoneinfo.ZoneInfo"], indirect=True) -def test_get_timezone_name_time_zoneinfo(timezone_getter, tzname, params, expected): - """zoneinfo will correctly detect DST from the object. - FIXME: this test will only succeed in the winter. - """ - dt = time(15, 30, tzinfo=timezone_getter(tzname)) - assert dates.get_timezone_name(dt, **params) == expected - - def test_get_timezone_name_misc(timezone_getter): localnow = datetime.utcnow().replace(tzinfo=timezone_getter('UTC')).astimezone(dates.LOCALTZ) assert (dates.get_timezone_name(None, locale='en_US') ==