Skip to content

dates: return a location without a region when the locale has no name for it - #1405

Open
simpleqt wants to merge 2 commits into
python-babel:masterfrom
simpleqt:fix/timezone-location-missing-region-name
Open

simpleqt wants to merge 2 commits into
python-babel:masterfrom
simpleqt:fix/timezone-location-missing-region-name

Conversation

@simpleqt

Copy link
Copy Markdown

get_timezone_location() falls back to the CLDR region ZZ ("unknown region") when the zone's territory has no name in the requested locale, but it then looked that name up with [] rather than .get(). Any locale that does not translate ZZ therefore raised KeyError: 'ZZ' for a perfectly valid zone — 173 of the 1,121 locales:

>>> from babel.dates import get_timezone, get_timezone_location, get_timezone_name
>>> tz = get_timezone('Europe/Paris')
>>> get_timezone_location(tz, locale='aa')
KeyError: 'ZZ'
>>> get_timezone_name(tz, locale='aa')     # reaches the same code with no zone variant
KeyError: 'ZZ'

get_timezone_name() calls get_timezone_location() when no zone_variant is given, so the crash also surfaced through the name lookup, which is the more common entry point.

The change keeps the region name optional: when the locale cannot name the region at all, the location is returned without one, which is what the location format degenerates to. Locales that do name it are untouched (en still gives Canada (St. John's) Time, de_DE still gives Kanada (St. John's) (Ortszeit)), and return_city=True is unaffected.

tests/test_dates.py::test_get_timezone_location_without_a_region_name covers the fallback and the unchanged case: 2 failed with KeyError: 'ZZ' before, 33 passed in the surrounding timezone tests after, and the full test_dates.py plus test_date_intervals.py are 2400 passed / 2 xfailed. ruff check reports no findings on either file.

Prepared with AI assistance; every line was verified by running the cases above.

format_interval() raises NotImplementedError for any skeleton whose CLDR
interval pattern defines the era ('G') or day period ('B') field, because
DateTimeFormat.extract() only understood y M d H h m a. The affected
skeletons (Gy, GyM, GyMd, GyMEd, GyMMM, GyMMMd, GyMMMEd, GyMMMM,
GyMMMMd, GyMMMMEd, GyMMMEEEEd, Bh, Bhm) are defined in interval_formats
for every locale, so format_interval(datetime(2020, 1, 1),
datetime(2021, 1, 1), 'Gy', locale='en') failed with
"Not implemented: extracting 'G' ...".

Extract those two fields, mirroring how they are rendered: the era index
from format_era() and the day period identifier from format_period().

Sweeping all locales, interval skeletons and instants (230,698 cases)
shows 20,220 NotImplementedError crashes on the base, 0 after the fix,
with no case that previously produced output changing its output.
… for it

get_timezone_location() falls back to the CLDR region 'ZZ' ('unknown
region') when the zone's territory has no name in the locale, but it then
looked that name up with [] instead of .get(), so every locale that does
not translate 'ZZ' -- 173 of the 1,121 locales -- raised KeyError: 'ZZ'
for a perfectly valid zone.  get_timezone_name() reaches the same code
when no zone variant is given, so the crash surfaced through the public
name lookup too.

Leave the region out of the result in that case, which is what the
location format degenerates to when the locale cannot name the region.
Copilot AI balanced review requested due to automatic review settings October 24, 2026 15:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants