Skip to content

Commit

Permalink
Handle ZoneInfo objects in get_timezone_location, get_timezone_name (#…
Browse files Browse the repository at this point in the history
…741)

Fixes #740
  • Loading branch information
youtux committed Nov 10, 2020
1 parent 77a3d2f commit 5315341
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -37,7 +37,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;python_version>="3.6" and python_version<"3.9"'
- pip install --editable .

script:
Expand Down
26 changes: 17 additions & 9 deletions babel/dates.py
Expand Up @@ -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).
Expand Down Expand Up @@ -500,13 +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'):
zone = tzinfo.zone
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)
Expand Down Expand Up @@ -619,10 +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'):
zone = tzinfo.zone
else:
zone = tzinfo.tzname(dt)
zone = _get_tz_name(dt_or_tzinfo)

if zone_variant is None:
if dt is None:
Expand Down
129 changes: 89 additions & 40 deletions tests/test_dates.py
Expand Up @@ -24,6 +24,23 @@
from babel.util import FixedOffsetTimezone


@pytest.fixture(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):
Expand Down Expand Up @@ -583,60 +600,92 @@ 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')
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') ==
u'Canada (St. John’s) Time')
assert (dates.get_timezone_location(tz, locale='en', return_city=True) ==
u'St. John’s')

tz = timezone('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('Europe/Berlin')
tz = timezone_getter('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'))
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')
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')
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')
assert dates.get_timezone_name(tz, locale='de_DE') == u'Neufundland-Zeit'

tz = timezone('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('UTC')).astimezone(dates.LOCALTZ)
@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"),
],
)
def test_get_timezone_name_time_pytz(timezone_getter, tzname, params, expected):
"""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


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'))

Expand Down
1 change: 1 addition & 0 deletions tox.ini
Expand Up @@ -7,6 +7,7 @@ deps =
pytest-cov==2.6.1
cdecimal: m3-cdecimal
freezegun==0.3.12
backports.zoneinfo;python_version>"3.6" and python_version<"3.9"
whitelist_externals = make
commands = make clean-cldr test
passenv = PYTHON_TEST_FLAGS
Expand Down

0 comments on commit 5315341

Please sign in to comment.