A Django app providing DB, form, and REST framework fields for
zoneinfo and pytz timezone
objects.
Like Django, this app supports both pytz and zoneinfo objects while the community transitions away from pytz to
zoneinfo. All exposed fields and functions that return a timezone object accept an optional boolean kwarg use_pytz.
If not explicitly specified, the default value used for use_pytz matches Django's behavior:
- Django <= 3.X:
use_pytzdefaults toTrue - Django == 4.X:
use_pytzdefaults to the value ofdjango.conf.settings.USE_DEPRECATED_PYTZ, which itself defaults toFalse - Django >= 5.X:
drops support for
pytzaltogether, and this app has done the same.
Note that this app does not declare pytz to be a dependency, so if you're using this app with use_pytz=True, you'll need
to ensure pytz is included in the environment yourself.
pytz and zoneinfo search for timezone data differently.
pytzbundles and searches within its own copy of the IANA timezone DBzoneinfofirst searches the local system's timezone DB for a match. If no match is found, it then searches within thetzdatapackage if it is installed. Thetzdatapackage contains a copy of the IANA timezone DB.
If the local system's timezone DB doesn't cover the entire IANA timezone DB and the tzdata package is not installed,
you may run across errors like ZoneInfoNotFoundError: 'No time zone found with key Pacific/Kanton' for seemingly valid
timezones when transitioning from pytz to zoneinfo. The easy fix is to add tzdata to your project with
poetry add tzdata or pip install tzdata.
Assuming you have the tzdata package installed if needed, no
data migration should be necessary when
switching from pytz to zoneinfo.
import zoneinfo
import pytz
from django.db import models
from timezone_field import TimeZoneField
class MyModel(models.Model):
tz1 = TimeZoneField(default="Asia/Dubai") # defaults supported, in ModelForm renders like "Asia/Dubai"
tz2 = TimeZoneField(choices_display="WITH_GMT_OFFSET") # in ModelForm renders like "GMT+04:00 Asia/Dubai"
tz3 = TimeZoneField(use_pytz=True) # returns pytz timezone objects
tz4 = TimeZoneField(use_pytz=False) # returns zoneinfo objects
my_model = MyModel(
tz2="America/Vancouver", # assignment of a string
tz3=pytz.timezone("America/Vancouver"), # assignment of a pytz timezone
tz4=zoneinfo.ZoneInfo("America/Vancouver"), # assignment of a zoneinfo
)
my_model.full_clean() # validates against pytz.common_timezones by default
my_model.save() # values stored in DB as strings
my_model.tz3 # value returned as pytz timezone: <DstTzInfo 'America/Vancouver' LMT-1 day, 15:48:00 STD>
my_model.tz4 # value returned as zoneinfo: zoneinfo.ZoneInfo(key='America/Vancouver')
my_model.tz1 = "UTC" # assignment of a string, immediately converted to timezone object
my_model.tz1 # zoneinfo.ZoneInfo(key='UTC') or pytz.utc, depending on use_pytz default
my_model.tz2 = "Invalid/Not_A_Zone" # immediately raises ValidationErrorfrom django import forms
from timezone_field import TimeZoneFormField
class MyForm(forms.Form):
tz1 = TimeZoneFormField() # renders like "Asia/Dubai"
tz2 = TimeZoneFormField(choices_display="WITH_GMT_OFFSET") # renders like "GMT+04:00 Asia/Dubai"
tz3 = TimeZoneFormField(use_pytz=True) # returns pytz timezone objects
tz4 = TimeZoneFormField(use_pytz=False) # returns zoneinfo objects
my_form = MyForm({"tz3": "Europe/Berlin", "tz4": "Europe/Berlin"})
my_form.full_clean() # validates against pytz.common_timezones by default
my_form.cleaned_data["tz3"] # value returned as pytz timezone: <DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD>
my_form.cleaned_data["tz4"] # value returned as zoneinfo: zoneinfo.ZoneInfo(key='Europe/Berlin')from rest_framework import serializers
from timezone_field.rest_framework import TimeZoneSerializerField
class MySerializer(serializers.Serializer):
tz1 = TimeZoneSerializerField(use_pytz=True)
tz2 = TimeZoneSerializerField(use_pytz=False)
my_serializer = MySerializer(data={
"tz1": "America/Argentina/Buenos_Aires",
"tz2": "America/Argentina/Buenos_Aires",
})
my_serializer.is_valid()
my_serializer.validated_data["tz1"] # <DstTzInfo 'America/Argentina/Buenos_Aires' LMT-1 day, 20:06:00 STD>
my_serializer.validated_data["tz2"] # zoneinfo.ZoneInfo(key='America/Argentina/Buenos_Aires')Releases are hosted on pypi and can be installed using various
python packaging tools.
# with poetry
poetry add django-timezone-field
# with pip
pip install django-timezone-fieldFrom the repository root, with poetry:
poetry install
poetry run pytest- Add support for django 5.2
- Add support for python 3.13
- Better default sorting of
choices(#116), (#123) - Convert string value to timezone object immediately on creation/assignment.
Accessing a TimeZoneField will always return a timezone or None (never a string).
(Potentially BREAKING: Unknown timezone names now raise
ValidationErrorat time of assignment. Previously, conversion was delayed until modelfull_cleanorsave.) (#57) - Add support for django 5.1
- Drop support for django 3.2, 4.0, 4.1
- Change base class of
TimeZoneSerializerFieldfrom DJRF'sFieldtoCharField(#137)
- Add support for django 5.0
- Add support for python 3.12
- Fix issue with
Factorytimezone on some BSD systems (#114)
- Use correct default backend when running with django 3.X (#109)
- BREAKING:
pytzremoved from dependencies. If you use this package withuse_pytz=True, you'll need to installpytzyourself. - Drop support for django 2.2
- Drop support for python 3.7
- Add django as a dependency of this package, with correct version constraints (#90)
- Add support for django 4.1, 4.2
- Add support for python 3.11
- Add support for
zoneinfoobjects (#79) - Add support for django 4.0
- Remove
timezone_field.utils.add_gmt_offset_to_choices,display_GMT_offsetkwarg (usechoices_displayinstead) - Drop support for django 3.0, 3.1
- Drop support for python 3.5, 3.6
- Fix sdist installs (#78)
- Officially support python 3.10
- Reinstate
TimeZoneField.default_choices(#76)
- Officially support django 3.2, python 3.9
- Fix bug with field deconstruction (#74)
- Housekeeping: use poetry, github actions, pytest
- Avoid
NonExistentTimeErrorduring DST transition (#70)
- Don't import
rest_frameworkfrom package root (#67)
- Add Django REST Framework serializer field
- Add new
choices_displaykwarg with supported valuesWITH_GMT_OFFSETandSTANDARD - Deprecate
display_GMT_offsetkwarg
- Add support for django 3.0, python 3.8
- Drop support for django 1.11, 2.0, 2.1, python 2.7, 3.4
- Officially support django 2.2 (already worked)
- Add option to display TZ offsets in form field (#46)
- Support django 1.11, 2.0, 2.1
- Add support for python 3.7
- Change default human-readable timezone names to exclude underscores (#32 & #37)
- Add support for django 1.10, 1.11
- Add support for python 3.6
- Add wheel support
- Support bytes in DB fields (#38 & #39)
- Drop support for django 1.7, add support for django 1.9
- Drop support for python 3.2, 3.3, add support for python 3.5
- Remove tests from source distribution
- Drop support for django 1.6, add support for django 1.8
- Various bug fixes
- For form field, changed default list of accepted timezones from
pytz.all_timezonestopytz.common_timezones, to match DB field behavior.
- Django 1.7 compatibility
- Added support for formatting
choiceskwarg as[[<str>, <str>], ...], in addition to previous format of[[<pytz.timezone>, <str>], ...]. - Changed default list of accepted timezones from
pytz.all_timezonestopytz.common_timezones. If you have timezones in your DB that are inpytz.all_timezonesbut not inpytz.common_timezones, this is a backward-incompatible change. Old behavior can be restored by specifyingchoices=[(tz, tz) for tz in pytz.all_timezones]in your model definition.
- Initial release as
timezone_field.
Originally adapted from Brian Rosner's django-timezones.
Made possible thanks to the work of the contributors.