Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions babel/dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,33 @@ def _get_tz_name(dt_or_tzinfo: _DtOrTzinfo) -> str:
return tzinfo.tzname(dt or datetime.datetime.now(UTC))


@lru_cache(maxsize=1)
def _get_reverse_tz_aliases() -> dict[str, tuple[str, ...]]:
zone_aliases = get_global('zone_aliases')
reverse_aliases: dict[str, list[str]] = {}
for alias, canonical_zone in zone_aliases.items():
reverse_aliases.setdefault(canonical_zone, []).append(alias)
return {canonical_zone: tuple(aliases) for canonical_zone, aliases in reverse_aliases.items()}


def _get_display_tz_name(zone: str, locale: Locale, *, allow_territory: bool = False) -> str:
zone_aliases = get_global('zone_aliases')
candidates = [zone_aliases.get(zone, zone)]
for reverse_zone in _get_reverse_tz_aliases().get(zone, ()):
if reverse_zone not in candidates:
candidates.append(reverse_zone)

for candidate in candidates:
if locale.time_zones.get(candidate, {}):
return candidate
if get_global('meta_zones').get(candidate):
return candidate
if allow_territory and get_global('zone_territories').get(candidate):
return candidate

return candidates[0]


def _get_datetime(instant: _Instant) -> datetime.datetime:
"""
Get a datetime out of an "instant" (date, time, datetime, number).
Expand Down Expand Up @@ -518,8 +545,8 @@ def get_timezone_location(

zone = _get_tz_name(dt_or_tzinfo)

# Get the canonical time-zone code
zone = get_global('zone_aliases').get(zone, zone)
# Prefer the zone name that actually has locale data attached to it.
zone = _get_display_tz_name(zone, locale, allow_territory=True)

info = locale.time_zones.get(zone, {})

Expand Down Expand Up @@ -655,8 +682,8 @@ def get_timezone_name(
if zone_variant not in ('generic', 'standard', 'daylight'):
raise ValueError('Invalid zone variation')

# Get the canonical time-zone code
zone = get_global('zone_aliases').get(zone, zone)
# Prefer the zone name that actually has locale data attached to it.
zone = _get_display_tz_name(zone, locale)
if return_zone:
return zone
info = locale.time_zones.get(zone, {})
Expand Down
18 changes: 18 additions & 0 deletions tests/test_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
import freezegun
import pytest

try:
import zoneinfo
except ModuleNotFoundError:
try:
from backports import zoneinfo
except ImportError:
zoneinfo = None

from babel import Locale, dates
from babel.dates import NO_INHERITANCE_MARKER, UTC, _localize, parse_pattern
from babel.util import FixedOffsetTimezone
Expand Down Expand Up @@ -552,6 +560,16 @@ def test_get_timezone_name_misc(timezone_getter):
assert (dates.get_timezone_name(time(16, 20), locale='en_US', width='short') == "UTC")


@pytest.mark.skipif(zoneinfo is None, reason="zoneinfo not available")
def test_get_timezone_name_indianapolis_zoneinfo_regression():
tz = zoneinfo.ZoneInfo('America/Indiana/Indianapolis')
dt = datetime(2025, 7, 1, 12, 0, tzinfo=tz)

assert dates.get_timezone_name(dt, locale='en_US', width='long') == 'Eastern Daylight Time'
assert dates.get_timezone_name(dt, locale='en_US', width='short') == 'EDT'
assert dates.get_timezone_name(tz, locale='en_US', width='long') == 'Eastern Time'


def test_format_date():
d = date(2007, 4, 1)
assert dates.format_date(d, locale='en_US') == 'Apr 1, 2007'
Expand Down