Skip to content

Commit 5315341

Browse files
authored
Handle ZoneInfo objects in get_timezone_location, get_timezone_name (#741)
Fixes #740
1 parent 77a3d2f commit 5315341

4 files changed

Lines changed: 108 additions & 50 deletions

File tree

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ matrix:
3737
install:
3838
- bash .ci/deps.${TRAVIS_OS_NAME}.sh
3939
- pip install --upgrade pip
40-
- pip install --upgrade $CDECIMAL pytest==4.3.1 pytest-cov==2.6.1 freezegun==0.3.12
40+
- 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"'
4141
- pip install --editable .
4242

4343
script:

babel/dates.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ def _get_dt_and_tzinfo(dt_or_tzinfo):
7676
return dt, tzinfo
7777

7878

79+
def _get_tz_name(dt_or_tzinfo):
80+
"""
81+
Get the timezone name out of a time, datetime, or tzinfo object.
82+
83+
:rtype: str
84+
"""
85+
dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo)
86+
if hasattr(tzinfo, 'zone'): # pytz object
87+
return tzinfo.zone
88+
elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object
89+
return tzinfo.key
90+
else:
91+
return tzinfo.tzname(dt or datetime.utcnow())
92+
93+
7994
def _get_datetime(instant):
8095
"""
8196
Get a datetime out of an "instant" (date, time, datetime, number).
@@ -500,13 +515,9 @@ def get_timezone_location(dt_or_tzinfo=None, locale=LC_TIME, return_city=False):
500515
:return: the localized timezone name using location format
501516
502517
"""
503-
dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo)
504518
locale = Locale.parse(locale)
505519

506-
if hasattr(tzinfo, 'zone'):
507-
zone = tzinfo.zone
508-
else:
509-
zone = tzinfo.tzname(dt or datetime.utcnow())
520+
zone = _get_tz_name(dt_or_tzinfo)
510521

511522
# Get the canonical time-zone code
512523
zone = get_global('zone_aliases').get(zone, zone)
@@ -619,10 +630,7 @@ def get_timezone_name(dt_or_tzinfo=None, width='long', uncommon=False,
619630
dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo)
620631
locale = Locale.parse(locale)
621632

622-
if hasattr(tzinfo, 'zone'):
623-
zone = tzinfo.zone
624-
else:
625-
zone = tzinfo.tzname(dt)
633+
zone = _get_tz_name(dt_or_tzinfo)
626634

627635
if zone_variant is None:
628636
if dt is None:

tests/test_dates.py

Lines changed: 89 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@
2424
from babel.util import FixedOffsetTimezone
2525

2626

27+
@pytest.fixture(params=["pytz.timezone", "zoneinfo.ZoneInfo"])
28+
def timezone_getter(request):
29+
if request.param == "pytz.timezone":
30+
return timezone
31+
elif request.param == "zoneinfo.ZoneInfo":
32+
try:
33+
import zoneinfo
34+
except ImportError:
35+
try:
36+
from backports import zoneinfo
37+
except ImportError:
38+
pytest.skip("zoneinfo not available")
39+
return zoneinfo.ZoneInfo
40+
else:
41+
raise NotImplementedError
42+
43+
2744
class DateTimeFormatTestCase(unittest.TestCase):
2845

2946
def test_quarter_format(self):
@@ -583,60 +600,92 @@ def test_get_timezone_gmt():
583600
assert dates.get_timezone_gmt(dt, 'long', locale='fr_FR') == u'UTC-07:00'
584601

585602

586-
def test_get_timezone_location():
587-
tz = timezone('America/St_Johns')
603+
def test_get_timezone_location(timezone_getter):
604+
tz = timezone_getter('America/St_Johns')
588605
assert (dates.get_timezone_location(tz, locale='de_DE') ==
589606
u"Kanada (St. John\u2019s) Zeit")
590607
assert (dates.get_timezone_location(tz, locale='en') ==
591608
u'Canada (St. John’s) Time')
592609
assert (dates.get_timezone_location(tz, locale='en', return_city=True) ==
593610
u'St. John’s')
594611

595-
tz = timezone('America/Mexico_City')
612+
tz = timezone_getter('America/Mexico_City')
596613
assert (dates.get_timezone_location(tz, locale='de_DE') ==
597614
u'Mexiko (Mexiko-Stadt) Zeit')
598615

599-
tz = timezone('Europe/Berlin')
616+
tz = timezone_getter('Europe/Berlin')
600617
assert (dates.get_timezone_location(tz, locale='de_DE') ==
601618
u'Deutschland (Berlin) Zeit')
602619

603620

604-
def test_get_timezone_name():
605-
dt = time(15, 30, tzinfo=timezone('America/Los_Angeles'))
606-
assert (dates.get_timezone_name(dt, locale='en_US') ==
607-
u'Pacific Standard Time')
608-
assert (dates.get_timezone_name(dt, locale='en_US', return_zone=True) ==
609-
u'America/Los_Angeles')
610-
assert dates.get_timezone_name(dt, width='short', locale='en_US') == u'PST'
611-
612-
tz = timezone('America/Los_Angeles')
613-
assert dates.get_timezone_name(tz, locale='en_US') == u'Pacific Time'
614-
assert dates.get_timezone_name(tz, 'short', locale='en_US') == u'PT'
615-
616-
tz = timezone('Europe/Berlin')
617-
assert (dates.get_timezone_name(tz, locale='de_DE') ==
618-
u'Mitteleurop\xe4ische Zeit')
619-
assert (dates.get_timezone_name(tz, locale='pt_BR') ==
620-
u'Hor\xe1rio da Europa Central')
621-
622-
tz = timezone('America/St_Johns')
623-
assert dates.get_timezone_name(tz, locale='de_DE') == u'Neufundland-Zeit'
624-
625-
tz = timezone('America/Los_Angeles')
626-
assert dates.get_timezone_name(tz, locale='en', width='short',
627-
zone_variant='generic') == u'PT'
628-
assert dates.get_timezone_name(tz, locale='en', width='short',
629-
zone_variant='standard') == u'PST'
630-
assert dates.get_timezone_name(tz, locale='en', width='short',
631-
zone_variant='daylight') == u'PDT'
632-
assert dates.get_timezone_name(tz, locale='en', width='long',
633-
zone_variant='generic') == u'Pacific Time'
634-
assert dates.get_timezone_name(tz, locale='en', width='long',
635-
zone_variant='standard') == u'Pacific Standard Time'
636-
assert dates.get_timezone_name(tz, locale='en', width='long',
637-
zone_variant='daylight') == u'Pacific Daylight Time'
638-
639-
localnow = datetime.utcnow().replace(tzinfo=timezone('UTC')).astimezone(dates.LOCALTZ)
621+
@pytest.mark.parametrize(
622+
"tzname, params, expected",
623+
[
624+
("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Time"),
625+
("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PT"),
626+
("Europe/Berlin", {"locale": "de_DE"}, u"Mitteleurop\xe4ische Zeit"),
627+
("Europe/Berlin", {"locale": "pt_BR"}, u"Hor\xe1rio da Europa Central"),
628+
("America/St_Johns", {"locale": "de_DE"}, u"Neufundland-Zeit"),
629+
(
630+
"America/Los_Angeles",
631+
{"locale": "en", "width": "short", "zone_variant": "generic"},
632+
u"PT",
633+
),
634+
(
635+
"America/Los_Angeles",
636+
{"locale": "en", "width": "short", "zone_variant": "standard"},
637+
u"PST",
638+
),
639+
(
640+
"America/Los_Angeles",
641+
{"locale": "en", "width": "short", "zone_variant": "daylight"},
642+
u"PDT",
643+
),
644+
(
645+
"America/Los_Angeles",
646+
{"locale": "en", "width": "long", "zone_variant": "generic"},
647+
u"Pacific Time",
648+
),
649+
(
650+
"America/Los_Angeles",
651+
{"locale": "en", "width": "long", "zone_variant": "standard"},
652+
u"Pacific Standard Time",
653+
),
654+
(
655+
"America/Los_Angeles",
656+
{"locale": "en", "width": "long", "zone_variant": "daylight"},
657+
u"Pacific Daylight Time",
658+
),
659+
("Europe/Berlin", {"locale": "en_US"}, u"Central European Time"),
660+
],
661+
)
662+
def test_get_timezone_name_tzinfo(timezone_getter, tzname, params, expected):
663+
tz = timezone_getter(tzname)
664+
assert dates.get_timezone_name(tz, **params) == expected
665+
666+
667+
@pytest.mark.parametrize("timezone_getter", ["pytz.timezone"], indirect=True)
668+
@pytest.mark.parametrize(
669+
"tzname, params, expected",
670+
[
671+
("America/Los_Angeles", {"locale": "en_US"}, u"Pacific Standard Time"),
672+
(
673+
"America/Los_Angeles",
674+
{"locale": "en_US", "return_zone": True},
675+
u"America/Los_Angeles",
676+
),
677+
("America/Los_Angeles", {"width": "short", "locale": "en_US"}, u"PST"),
678+
],
679+
)
680+
def test_get_timezone_name_time_pytz(timezone_getter, tzname, params, expected):
681+
"""pytz (by design) can't determine if the time is in DST or not,
682+
so it will always return Standard time"""
683+
dt = time(15, 30, tzinfo=timezone_getter(tzname))
684+
assert dates.get_timezone_name(dt, **params) == expected
685+
686+
687+
def test_get_timezone_name_misc(timezone_getter):
688+
localnow = datetime.utcnow().replace(tzinfo=timezone_getter('UTC')).astimezone(dates.LOCALTZ)
640689
assert (dates.get_timezone_name(None, locale='en_US') ==
641690
dates.get_timezone_name(localnow, locale='en_US'))
642691

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ deps =
77
pytest-cov==2.6.1
88
cdecimal: m3-cdecimal
99
freezegun==0.3.12
10+
backports.zoneinfo;python_version>"3.6" and python_version<"3.9"
1011
whitelist_externals = make
1112
commands = make clean-cldr test
1213
passenv = PYTHON_TEST_FLAGS

0 commit comments

Comments
 (0)