==========Time zones==========.. _time-zones-overview:Overview========When support for time zones is enabled, Django stores datetime information inUTC in the database, uses time-zone-aware datetime objects internally, andtranslates them to the end user's time zone in templates and forms.This is handy if your users live in more than one time zone and you want todisplay datetime information according to each user's wall clock.Even if your website is available in only one time zone, it's still goodpractice to store data in UTC in your database. The main reason is daylightsaving time (DST). Many countries have a system of DST, where clocks are movedforward in spring and backward in autumn. If you're working in local time,you're likely to encounter errors twice a year, when the transitions happen.This probably doesn't matter for your blog, but it's a problem if you over billor under bill your customers by one hour, twice a year, every year. Thesolution to this problem is to use UTC in the code and use local time only wheninteracting with end users.Time zone support is disabled by default. To enable it, set :setting:`USE_TZ =True <USE_TZ>` in your settings file... note::In Django 5.0, time zone support will be enabled by default.Time zone support uses :mod:`zoneinfo`, which is part of the Python standardlibrary from Python 3.9. The ``backports.zoneinfo`` package is automaticallyinstalled alongside Django if you are using Python 3.8... versionchanged:: 4.0:mod:`zoneinfo` was made the default timezone implementation. You maycontinue to use `pytz`_ during the 4.x release cycle via the:setting:`USE_DEPRECATED_PYTZ` setting... note::The default :file:`settings.py` file created by :djadmin:`django-adminstartproject <startproject>` includes :setting:`USE_TZ = True <USE_TZ>`for convenience.If you're wrestling with a particular problem, start with the :ref:`time zoneFAQ <time-zones-faq>`.Concepts========.. _naive_vs_aware_datetimes:Naive and aware datetime objects--------------------------------Python's :class:`datetime.datetime` objects have a ``tzinfo`` attribute thatcan be used to store time zone information, represented as an instance of asubclass of :class:`datetime.tzinfo`. When this attribute is set and describesan offset, a datetime object is **aware**. Otherwise, it's **naive**.You can use :func:`~django.utils.timezone.is_aware` and:func:`~django.utils.timezone.is_naive` to determine whether datetimes areaware or naive.When time zone support is disabled, Django uses naive datetime objects in localtime. This is sufficient for many use cases. In this mode, to obtain thecurrent time, you would write::import datetimenow = datetime.datetime.now()When time zone support is enabled (:setting:`USE_TZ=True <USE_TZ>`), Django usestime-zone-aware datetime objects. If your code creates datetime objects, theyshould be aware too. In this mode, the example above becomes::from django.utils import timezonenow = timezone.now().. warning::Dealing with aware datetime objects isn't always intuitive. For instance,the ``tzinfo`` argument of the standard datetime constructor doesn't workreliably for time zones with DST. Using UTC is generally safe; if you'reusing other time zones, you should review the :mod:`zoneinfo`documentation carefully... note::Python's :class:`datetime.time` objects also feature a ``tzinfo``attribute, and PostgreSQL has a matching ``time with time zone`` type.However, as PostgreSQL's docs put it, this type "exhibits properties whichlead to questionable usefulness".Django only supports naive time objects and will raise an exception if youattempt to save an aware time object, as a timezone for a time with noassociated date does not make sense... _naive-datetime-objects:Interpretation of naive datetime objects----------------------------------------When :setting:`USE_TZ` is ``True``, Django still accepts naive datetimeobjects, in order to preserve backwards-compatibility. When the database layerreceives one, it attempts to make it aware by interpreting it in the:ref:`default time zone <default-current-time-zone>` and raises a warning.Unfortunately, during DST transitions, some datetimes don't exist or areambiguous. That's why you should always create aware datetime objects when timezone support is enabled. (See the :mod:`Using ZoneInfo section of the zoneinfodocs <zoneinfo>` for examples using the ``fold`` attribute to specify theoffset that should apply to a datetime during a DST transition.)In practice, this is rarely an issue. Django gives you aware datetime objectsin the models and forms, and most often, new datetime objects are created fromexisting ones through :class:`~datetime.timedelta` arithmetic. The onlydatetime that's often created in application code is the current time, and:func:`timezone.now() <django.utils.timezone.now>` automatically does theright thing... _default-current-time-zone:Default time zone and current time zone---------------------------------------The **default time zone** is the time zone defined by the :setting:`TIME_ZONE`setting.The **current time zone** is the time zone that's used for rendering.You should set the current time zone to the end user's actual time zone with:func:`~django.utils.timezone.activate`. Otherwise, the default time zone isused... note::As explained in the documentation of :setting:`TIME_ZONE`, Django setsenvironment variables so that its process runs in the default time zone.This happens regardless of the value of :setting:`USE_TZ` and of thecurrent time zone.When :setting:`USE_TZ` is ``True``, this is useful to preservebackwards-compatibility with applications that still rely on local time.However, :ref:`as explained above <naive-datetime-objects>`, this isn'tentirely reliable, and you should always work with aware datetimes in UTCin your own code. For instance, use :meth:`~datetime.datetime.fromtimestamp`and set the ``tz`` parameter to :attr:`~datetime.timezone.utc`.Selecting the current time zone-------------------------------The current time zone is the equivalent of the current :term:`locale <localename>` for translations. However, there's no equivalent of the``Accept-Language`` HTTP header that Django could use to determine the user'stime zone automatically. Instead, Django provides :ref:`time zone selectionfunctions <time-zone-selection-functions>`. Use them to build the time zoneselection logic that makes sense for you.Most websites that care about time zones ask users in which time zone they liveand store this information in the user's profile. For anonymous users, they usethe time zone of their primary audience or UTC.:func:`zoneinfo.available_timezones` provides a set of available timezones thatyou can use to build a map from likely locations to time zones.Here's an example that stores the current timezone in the session. (It skipserror handling entirely for the sake of simplicity.)Add the following middleware to :setting:`MIDDLEWARE`::import zoneinfofrom django.utils import timezoneclass TimezoneMiddleware:def __init__(self, get_response):self.get_response = get_responsedef __call__(self, request):tzname = request.session.get('django_timezone')if tzname:timezone.activate(zoneinfo.ZoneInfo(tzname))else:timezone.deactivate()return self.get_response(request)Create a view that can set the current timezone::from django.shortcuts import redirect, render# Prepare a map of common locations to timezone choices you wish to offer.common_timezones = {'London': 'Europe/London','Paris': 'Europe/Paris','New York': 'America/New_York',}def set_timezone(request):if request.method == 'POST':request.session['django_timezone'] = request.POST['timezone']return redirect('/')else:return render(request, 'template.html', {'timezones': common_timezones})Include a form in ``template.html`` that will ``POST`` to this view:.. code-block:: html+django{% load tz %}{% get_current_timezone as TIME_ZONE %}<form action="{% url 'set_timezone' %}" method="POST">{% csrf_token %}<label for="timezone">Time zone:</label><select name="timezone">{% for city, tz in timezones %}<option value="{{ tz }}"{% if tz == TIME_ZONE %} selected{% endif %}>{{ city }}</option>{% endfor %}</select><input type="submit" value="Set"></form>.. _time-zones-in-forms:Time zone aware input in forms==============================When you enable time zone support, Django interprets datetimes entered informs in the :ref:`current time zone <default-current-time-zone>` and returnsaware datetime objects in ``cleaned_data``.Converted datetimes that don't exist or are ambiguous because they fall in aDST transition will be reported as invalid values... _time-zones-in-templates:Time zone aware output in templates===================================When you enable time zone support, Django converts aware datetime objects tothe :ref:`current time zone <default-current-time-zone>` when they're renderedin templates. This behaves very much like :doc:`format localization</topics/i18n/formatting>`... warning::Django doesn't convert naive datetime objects, because they could beambiguous, and because your code should never produce naive datetimes whentime zone support is enabled. However, you can force conversion with thetemplate filters described below.Conversion to local time isn't always appropriate -- you may be generatingoutput for computers rather than for humans. The following filters and tags,provided by the ``tz`` template tag library, allow you to control the time zoneconversions... highlight:: html+djangoTemplate tags-------------.. templatetag:: localtime``localtime``~~~~~~~~~~~~~Enables or disables conversion of aware datetime objects to the current timezone in the contained block.This tag has exactly the same effects as the :setting:`USE_TZ` setting as faras the template engine is concerned. It allows a more fine grained control ofconversion.To activate or deactivate conversion for a template block, use::{% load tz %}{% localtime on %}{{ value }}{% endlocaltime %}{% localtime off %}{{ value }}{% endlocaltime %}.. note::The value of :setting:`USE_TZ` isn't respected inside of a``{% localtime %}`` block... templatetag:: timezone``timezone``~~~~~~~~~~~~Sets or unsets the current time zone in the contained block. When the currenttime zone is unset, the default time zone applies.::{% load tz %}{% timezone "Europe/Paris" %}Paris time: {{ value }}{% endtimezone %}{% timezone None %}Server time: {{ value }}{% endtimezone %}.. templatetag:: get_current_timezone``get_current_timezone``~~~~~~~~~~~~~~~~~~~~~~~~You can get the name of the current time zone using the``get_current_timezone`` tag::{% get_current_timezone as TIME_ZONE %}Alternatively, you can activate the:func:`~django.template.context_processors.tz` context processor anduse the ``TIME_ZONE`` context variable.Template filters----------------These filters accept both aware and naive datetimes. For conversion purposes,they assume that naive datetimes are in the default time zone. They alwaysreturn aware datetimes... templatefilter:: localtime``localtime``~~~~~~~~~~~~~Forces conversion of a single value to the current time zone.For example::{% load tz %}{{ value|localtime }}.. templatefilter:: utc``utc``~~~~~~~Forces conversion of a single value to UTC.For example::{% load tz %}{{ value|utc }}.. templatefilter:: timezone``timezone``~~~~~~~~~~~~Forces conversion of a single value to an arbitrary timezone.The argument must be an instance of a :class:`~datetime.tzinfo` subclass or atime zone name.For example::{% load tz %}{{ value|timezone:"Europe/Paris" }}.. highlight:: python.. _time-zones-migration-guide:Migration guide===============Here's how to migrate a project that was started before Django supported timezones.Database--------PostgreSQL~~~~~~~~~~The PostgreSQL backend stores datetimes as ``timestamp with time zone``. Inpractice, this means it converts datetimes from the connection's time zone toUTC on storage, and from UTC to the connection's time zone on retrieval.As a consequence, if you're using PostgreSQL, you can switch between ``USE_TZ= False`` and ``USE_TZ = True`` freely. The database connection's time zonewill be set to :setting:`TIME_ZONE` or ``UTC`` respectively, so that Djangoobtains correct datetimes in all cases. You don't need to perform any dataconversions.Other databases~~~~~~~~~~~~~~~Other backends store datetimes without time zone information. If you switchfrom ``USE_TZ = False`` to ``USE_TZ = True``, you must convert your data fromlocal time to UTC -- which isn't deterministic if your local time has DST.Code----The first step is to add :setting:`USE_TZ = True <USE_TZ>` to your settingsfile. At this point, things should mostly work. If you create naive datetimeobjects in your code, Django makes them aware when necessary.However, these conversions may fail around DST transitions, which means youaren't getting the full benefits of time zone support yet. Also, you're likelyto run into a few problems because it's impossible to compare a naive datetimewith an aware datetime. Since Django now gives you aware datetimes, you'll getexceptions wherever you compare a datetime that comes from a model or a formwith a naive datetime that you've created in your code.So the second step is to refactor your code wherever you instantiate datetimeobjects to make them aware. This can be done incrementally.:mod:`django.utils.timezone` defines some handy helpers for compatibilitycode: :func:`~django.utils.timezone.now`,:func:`~django.utils.timezone.is_aware`,:func:`~django.utils.timezone.is_naive`,:func:`~django.utils.timezone.make_aware`, and:func:`~django.utils.timezone.make_naive`.Finally, in order to help you locate code that needs upgrading, Django raisesa warning when you attempt to save a naive datetime to the database::RuntimeWarning: DateTimeField ModelName.field_name received a naivedatetime (2012-01-01 00:00:00) while time zone support is active.During development, you can turn such warnings into exceptions and get atraceback by adding the following to your settings file::import warningswarnings.filterwarnings('error', r"DateTimeField .* received a naive datetime",RuntimeWarning, r'django\.db\.models\.fields',)Fixtures--------When serializing an aware datetime, the UTC offset is included, like this::"2011-09-01T13:20:30+03:00"While for a naive datetime, it isn't::"2011-09-01T13:20:30"For models with :class:`~django.db.models.DateTimeField`\ s, this differencemakes it impossible to write a fixture that works both with and without timezone support.Fixtures generated with ``USE_TZ = False``, or before Django 1.4, use the"naive" format. If your project contains such fixtures, after you enable timezone support, you'll see :exc:`RuntimeWarning`\ s when you load them. To getrid of the warnings, you must convert your fixtures to the "aware" format.You can regenerate fixtures with :djadmin:`loaddata` then :djadmin:`dumpdata`.Or, if they're small enough, you can edit them to add the UTC offset thatmatches your :setting:`TIME_ZONE` to each serialized datetime... _time-zones-faq:FAQ===Setup-----#. **I don't need multiple time zones. Should I enable time zone support?**Yes. When time zone support is enabled, Django uses a more accurate modelof local time. This shields you from subtle and unreproducible bugs arounddaylight saving time (DST) transitions.When you enable time zone support, you'll encounter some errors becauseyou're using naive datetimes where Django expects aware datetimes. Sucherrors show up when running tests. You'll quickly learn how to avoid invalidoperations.On the other hand, bugs caused by the lack of time zone support are muchharder to prevent, diagnose and fix. Anything that involves scheduled tasksor datetime arithmetic is a candidate for subtle bugs that will bite youonly once or twice a year.For these reasons, time zone support is enabled by default in new projects,and you should keep it unless you have a very good reason not to.#. **I've enabled time zone support. Am I safe?**Maybe. You're better protected from DST-related bugs, but you can stillshoot yourself in the foot by carelessly turning naive datetimes into awaredatetimes, and vice-versa.If your application connects to other systems -- for instance, if it queriesa web service -- make sure datetimes are properly specified. To transmitdatetimes safely, their representation should include the UTC offset, ortheir values should be in UTC (or both!).Finally, our calendar system contains interesting edge cases. For example,you can't always subtract one year directly from a given date::>>> import datetime>>> def one_year_before(value): # Wrong example.... return value.replace(year=value.year - 1)>>> one_year_before(datetime.datetime(2012, 3, 1, 10, 0))datetime.datetime(2011, 3, 1, 10, 0)>>> one_year_before(datetime.datetime(2012, 2, 29, 10, 0))Traceback (most recent call last):...ValueError: day is out of range for monthTo implement such a function correctly, you must decide whether 2012-02-29minus one year is 2011-02-28 or 2011-03-01, which depends on your businessrequirements.#. **How do I interact with a database that stores datetimes in local time?**Set the :setting:`TIME_ZONE <DATABASE-TIME_ZONE>` option to the appropriatetime zone for this database in the :setting:`DATABASES` setting.This is useful for connecting to a database that doesn't support time zonesand that isn't managed by Django when :setting:`USE_TZ` is ``True``.Troubleshooting---------------#. **My application crashes with** ``TypeError: can't compare offset-naive````and offset-aware datetimes`` **-- what's wrong?**Let's reproduce this error by comparing a naive and an aware datetime::>>> from django.utils import timezone>>> aware = timezone.now()>>> naive = timezone.make_naive(aware)>>> naive == awareTraceback (most recent call last):...TypeError: can't compare offset-naive and offset-aware datetimesIf you encounter this error, most likely your code is comparing these twothings:- a datetime provided by Django -- for instance, a value read from a form ora model field. Since you enabled time zone support, it's aware.- a datetime generated by your code, which is naive (or you wouldn't bereading this).Generally, the correct solution is to change your code to use an awaredatetime instead.If you're writing a pluggable application that's expected to workindependently of the value of :setting:`USE_TZ`, you may find:func:`django.utils.timezone.now` useful. This function returns the currentdate and time as a naive datetime when ``USE_TZ = False`` and as an awaredatetime when ``USE_TZ = True``. You can add or subtract:class:`datetime.timedelta` as needed.#. **I see lots of** ``RuntimeWarning: DateTimeField received a naivedatetime`` ``(YYYY-MM-DD HH:MM:SS)`` ``while time zone support is active``**-- is that bad?**When time zone support is enabled, the database layer expects to receiveonly aware datetimes from your code. This warning occurs when it receives anaive datetime. This indicates that you haven't finished porting your codefor time zone support. Please refer to the :ref:`migration guide<time-zones-migration-guide>` for tips on this process.In the meantime, for backwards compatibility, the datetime is considered tobe in the default time zone, which is generally what you expect.#. ``now.date()`` **is yesterday! (or tomorrow)**If you've always used naive datetimes, you probably believe that you canconvert a datetime to a date by calling its :meth:`~datetime.datetime.date`method. You also consider that a :class:`~datetime.date` is a lot like a:class:`~datetime.datetime`, except that it's less accurate.None of this is true in a time zone aware environment::>>> import datetime>>> import zoneinfo>>> paris_tz = zoneinfo.ZoneInfo("Europe/Paris")>>> new_york_tz = zoneinfo.ZoneInfo("America/New_York")>>> paris = datetime.datetime(2012, 3, 3, 1, 30, tzinfo=paris_tz)# This is the correct way to convert between time zones.>>> new_york = paris.astimezone(new_york_tz)>>> paris == new_york, paris.date() == new_york.date()(True, False)>>> paris - new_york, paris.date() - new_york.date()(datetime.timedelta(0), datetime.timedelta(1))>>> parisdatetime.datetime(2012, 3, 3, 1, 30, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))>>> new_yorkdatetime.datetime(2012, 3, 2, 19, 30, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))As this example shows, the same datetime has a different date, depending onthe time zone in which it is represented. But the real problem is morefundamental.A datetime represents a **point in time**. It's absolute: it doesn't dependon anything. On the contrary, a date is a **calendaring concept**. It's aperiod of time whose bounds depend on the time zone in which the date isconsidered. As you can see, these two concepts are fundamentally different,and converting a datetime to a date isn't a deterministic operation.What does this mean in practice?Generally, you should avoid converting a :class:`~datetime.datetime` to:class:`~datetime.date`. For instance, you can use the :tfilter:`date`template filter to only show the date part of a datetime. This filter willconvert the datetime into the current time zone before formatting it,ensuring the results appear correctly.If you really need to do the conversion yourself, you must ensure thedatetime is converted to the appropriate time zone first. Usually, thiswill be the current timezone::>>> from django.utils import timezone>>> timezone.activate(zoneinfo.ZoneInfo("Asia/Singapore"))# For this example, we set the time zone to Singapore, but here's how# you would obtain the current time zone in the general case.>>> current_tz = timezone.get_current_timezone()>>> local = paris.astimezone(current_tz)>>> localdatetime.datetime(2012, 3, 3, 8, 30, tzinfo=zoneinfo.ZoneInfo(key='Asia/Singapore'))>>> local.date()datetime.date(2012, 3, 3)#. **I get an error** "``Are time zone definitions for your databaseinstalled?``"If you are using MySQL, see the :ref:`mysql-time-zone-definitions` sectionof the MySQL notes for instructions on loading time zone definitions.Usage-----#. **I have a string** ``"2012-02-21 10:28:45"`` **and I know it's in the**``"Europe/Helsinki"`` **time zone. How do I turn that into an awaredatetime?**Here you need to create the required ``ZoneInfo`` instance and attach it tothe naïve datetime::>>> import zoneinfo>>> from django.utils.dateparse import parse_datetime>>> naive = parse_datetime("2012-02-21 10:28:45")>>> naive.replace(tzinfo=zoneinfo.ZoneInfo("Europe/Helsinki"))datetime.datetime(2012, 2, 21, 10, 28, 45, tzinfo=zoneinfo.ZoneInfo(key='Europe/Helsinki'))#. **How can I obtain the local time in the current time zone?**Well, the first question is, do you really need to?You should only use local time when you're interacting with humans, and thetemplate layer provides :ref:`filters and tags <time-zones-in-templates>`to convert datetimes to the time zone of your choice.Furthermore, Python knows how to compare aware datetimes, taking intoaccount UTC offsets when necessary. It's much easier (and possibly faster)to write all your model and view code in UTC. So, in most circumstances,the datetime in UTC returned by :func:`django.utils.timezone.now` will besufficient.For the sake of completeness, though, if you really want the local timein the current time zone, here's how you can obtain it::>>> from django.utils import timezone>>> timezone.localtime(timezone.now())datetime.datetime(2012, 3, 3, 20, 10, 53, 873365, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))In this example, the current time zone is ``"Europe/Paris"``.#. **How can I see all available time zones?**:func:`zoneinfo.available_timezones` provides the set of all valid keys forIANA time zones available to your system. See the docs for usageconsiderations... _pytz: http://pytz.sourceforge.net/