23.10 What changed in time zones, translation and the utilities
The changes from Django 5.0 to this commit in time zones, localization, translation and the smaller modules of django/utils/: USE_TZ true by default, pytz, the is_dst argument and the alias utc gone, USE_L10N gone, Uyghur and Haitian Creole added, and what changed in cached_property, Truncator, parse_duration, import_string and django.VERSION.
Most of what 5.0 changed here ended deprecations that 4.0 began, when Django moved from pytz to the standard library’s zoneinfo and made localized output the default. What 5.0 removed is an error where it was a module, a name or an argument, with one exception, and is passed over in silence where it was a setting: the settings object takes any upper-case name, and nothing at this commit reads these.
Time zones: USE_TZ and zoneinfo
USE_TZ has been true by default since 5.0. The project template has written USE_TZ = True into a new project’s settings since 1.4, so the change reached the settings that never set it: a settings module written by hand, or settings in a script. With it on, django is aware, in UTC, and a model’s datetime field given a naive value warns and takes it to be in the default zone. Time zones tells the mechanism.
Support for pytz’s zones went in 5.0, with USE_DEPRECATED_PYTZ, the transitional setting that had kept it through the 4.x releases. No module of Django imports pytz at this commit. TIME_ZONE, and a name given to django, become a zoneinfo, and a zone given as an object is treated as any tzinfo: make_aware attaches it with datetime. The is_dst argument, which decided the treatment of local times that never happen or happen twice, went from make_aware, QuerySet, Trunc and the truncations by unit; for make_aware, which only attaches the zone, a zoneinfo takes such a time’s offset from the value’s datetime. The alias utc of datetime went from django too.
A run, with settings that set USE_L10N = False and USE_DEPRECATED_PYTZ = True and leave USE_TZ unset; Sailor and Voyage are models of the harbour project the ORM chapters use, with a datetime field and a date field:
# a run at this commit, every warning shown
settings.USE_TZ True
timezone.now().tzinfo datetime.timezone.utc
Sailor.objects.filter(signed_on__lt=datetime(2026, 3, 29, 11, 30)).count()
0
warns RuntimeWarning: DateTimeField Sailor.signed_on received a naive datetime (2026-03-29 11:30:00) while time zone support is active.
timezone.make_aware(datetime(2026, 10, 25, 2, 30), is_dst=True)
TypeError: make_aware() got an unexpected keyword argument 'is_dst'
Sailor.objects.datetimes("signed_on", "day", is_dst=True)
TypeError: QuerySet.datetimes() got an unexpected keyword argument 'is_dst'
TruncDay("sailed", is_dst=True): the same SQL as without
True
Localization: no USE_L10N
USE_L10N, the setting that switched localized formats on, was deprecated in 4.0, which made its default true, and removed in 5.0; the 4.0 notes give the consequence, that by default any date or number Django displays is localized. At this commit get_format takes use_l10n=None to mean true, and so do localize and the functions under it. Localization is turned off by a caller, as a template is inside {% localize off %}, which sets the context’s flag. A project that still sets USE_L10N = False is neither refused nor warned. The same run, in Dutch:
# the same run, inside translation.override("nl")
settings.USE_L10N False
formats.localize(1234.5) '1234,5'
formats.localize(date(2026, 3, 29)) '29 maart 2026'
formats.localize(1234.5, use_l10n=False) '1234.5'
Locale formats: get_format and the format modules tells how a format is found.
Languages added
Uyghur, "ug", joined LANGUAGES in 5.0, written right to left and so in LANGUAGES_BIDI, and Haitian Creole, "ht", in 6.0. The 6.0 notes speak of translations for Haitian Creole, but at this commit Django’s tree has a format module for it and no catalogue. With USE_I18N on, check_for_language asks for a catalogue, so for "ht" it is false and get_supported_language_variant raises LookupError: unless a project or an application provides a catalogue, the language is never chosen from a request’s path, cookie or Accept-Language, and in a run, activated by hand, it left Django’s messages in English. The security releases’ limits on a language code’s length are in Security fixes: limits against denial of service.
The utilities
The undocumented modules baseconv and datetime_safe went in 5.0, after their deprecation in 4.0, and itercompat, with its is_iterable, deprecated in 5.1, went in 6.0. Importing any of them raises ModuleNotFoundError. At this commit signing writes its base 62 with its own b62_encode and b62_decode; the 5.1 notes named collections in is_iterable’s place, which is how the check registry tests a check’s result.
| name | what changed | release | told in |
|---|---|---|---|
cached_property |
what changed the name argument is gone; the name comes only from cached_, called as the class statement runs |
release 5.0 | told in cached_property, classproperty and classonlymethod |
Truncator |
what changed with html=True, for the truncatechars_ and truncatewords_ filters, parses with subclasses of html, more robust and faster in the notes’ words, with small differences in output (Truncate django/ |
release 5.1 | told in Text helpers: Truncator and text.py django/utils/ |
Safe and format_html_join |
what changed told with the templates’ safe strings | release 5.2 | told in What changed in templates |
parse_duration |
what changed reads ISO 8601’s weeks, alone or before days and a time: in a run "P1W2DT3H" was nine days and three hours (iso8601_ django/ |
release 6.1 | told in Dates, durations and addresses: parse_datetime, parse_duration and clean_ |
import_string |
what changed imports the whole path as a module first, so it returns a top-level module, and for a longer path a submodule rather than its package’s attribute of the same name, unless the package itself rebinds the name after importing the submodule (import_string django/ |
release 6.2 | told in Importing by name: import_string and autodiscover_ |
django.VERSION |
what changed a VersionTuple: an index or slice past its second part, its length, unpacking, or a comparison with three parts or more warns with Removed, the class once named for 7.0 (VersionTuple django/ |
release 6.2 | told in Django’s version: VersionTuple and get_version |