mirror of
https://github.com/django/django.git
synced 2025-08-03 18:38:50 +00:00
Added support for time zones. Thanks Luke Plant for the review. Fixed #2626.
For more information on this project, see this thread:
cf0423bbb8
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17106 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
01f70349c9
commit
9b1cb755a2
58 changed files with 2720 additions and 284 deletions
|
@ -502,7 +502,9 @@ cache multilingual sites without having to create the cache key yourself.
|
|||
|
||||
.. versionchanged:: 1.4
|
||||
|
||||
This also happens when :setting:`USE_L10N` is set to ``True``.
|
||||
Cache keys also include the active :term:`language <language code>` when
|
||||
:setting:`USE_L10N` is set to ``True`` and the :ref:`current time zone
|
||||
<default-current-time-zone>` when :setting:`USE_TZ` is set to ``True``.
|
||||
|
||||
__ `Controlling cache: Using other headers`_
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ Internationalization and localization
|
|||
|
||||
translation
|
||||
formatting
|
||||
timezones
|
||||
|
||||
Overview
|
||||
========
|
||||
|
@ -17,8 +18,8 @@ application to offer its content in languages and formats tailored to the
|
|||
audience.
|
||||
|
||||
Django has full support for :doc:`translation of text
|
||||
</topics/i18n/translation>` and :doc:`formatting of dates, times and numbers
|
||||
</topics/i18n/formatting>`.
|
||||
</topics/i18n/translation>`, :doc:`formatting of dates, times and numbers
|
||||
</topics/i18n/formatting>`, and :doc:`time zones </topics/i18n/timezones>`.
|
||||
|
||||
Essentially, Django does two things:
|
||||
|
||||
|
@ -27,8 +28,9 @@ Essentially, Django does two things:
|
|||
* It uses these hooks to localize Web apps for particular users according to
|
||||
their preferences.
|
||||
|
||||
Obviously, translation depends on the target language. Formatting usually
|
||||
depends on the target country.
|
||||
Obviously, translation depends on the target language, and formatting usually
|
||||
depends on the target country. These informations are provided by browsers in
|
||||
the ``Accept-Language`` header. However, the time zone isn't readily available.
|
||||
|
||||
Definitions
|
||||
===========
|
||||
|
|
429
docs/topics/i18n/timezones.txt
Normal file
429
docs/topics/i18n/timezones.txt
Normal file
|
@ -0,0 +1,429 @@
|
|||
.. _time-zones:
|
||||
|
||||
==========
|
||||
Time zones
|
||||
==========
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
When support for time zones is enabled, Django stores date and time
|
||||
information in UTC in the database, uses time zone-aware datetime objects
|
||||
internally, and translates 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 to
|
||||
display date and time information according to each user's wall clock. Even if
|
||||
your website is available in only one time zone, it's still a good practice to
|
||||
store data in UTC in your database. Here is why.
|
||||
|
||||
Many countries have a system of daylight saving time (DST), where clocks are
|
||||
moved forwards in spring and backwards in autumn. If you're working in local
|
||||
time, you're likely to encounter errors twice a year, when the transitions
|
||||
happen. pytz' docs discuss `these issues`_ in greater detail. It probably
|
||||
doesn't matter for your blog, but it's more annoying if you over-bill or
|
||||
under-bill your customers by one hour, twice a year, every year. The solution
|
||||
to this problem is to use UTC in the code and local time only when
|
||||
interacting with end users.
|
||||
|
||||
Time zone support is disabled by default. To enable it, set :setting:`USE_TZ =
|
||||
True <USE_TZ>` in your settings file. Installing pytz_ is highly recommended,
|
||||
but not mandatory.
|
||||
|
||||
.. note::
|
||||
|
||||
The default :file:`settings.py` file created by :djadmin:`django-admin.py
|
||||
startproject <startproject>` includes :setting:`USE_TZ = True <USE_TZ>`
|
||||
for convenience.
|
||||
|
||||
.. note::
|
||||
|
||||
There is also an independent but related :setting:`USE_L10N` setting that
|
||||
controls if Django should activate format localization. See
|
||||
:doc:`/topics/i18n/formatting` for more details.
|
||||
|
||||
Concepts
|
||||
========
|
||||
|
||||
Naive and aware datetime objects
|
||||
--------------------------------
|
||||
|
||||
Python's :class:`datetime.datetime` objects have a ``tzinfo`` attribute that
|
||||
can be used to store time zone information, represented as an instance of a
|
||||
subclass of :class:`datetime.tzinfo`. When this attribute is set and describes
|
||||
an 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 if datetimes are aware or
|
||||
naive.
|
||||
|
||||
When time zone support is disabled, Django uses naive datetime objects in local
|
||||
time. This is simple and sufficient for many use cases. In this mode, to obtain
|
||||
the current time, you would write::
|
||||
|
||||
import datetime
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
When time zone support is enabled, Django uses time zone aware datetime
|
||||
objects. If your code creates datetime objects, they should be aware too. In
|
||||
this mode, the example above becomes::
|
||||
|
||||
import datetime
|
||||
from django.utils.timezone import utc
|
||||
|
||||
now = datetime.datetime.utcnow().replace(tzinfo=utc)
|
||||
|
||||
.. note::
|
||||
|
||||
:mod:`django.utils.timezone` provides a
|
||||
:func:`~django.utils.timezone.now()` function that returns a naive or
|
||||
aware datetime object according to the value of :setting:`USE_TZ`.
|
||||
|
||||
.. warning::
|
||||
|
||||
Dealing with aware datetime objects isn't always intuitive. For instance,
|
||||
the ``tzinfo`` argument of the standard datetime constructor doesn't work
|
||||
reliably for time zones with DST. Using UTC is generally safe; if you're
|
||||
using other time zones, you should review `pytz' documentation <pytz>`_
|
||||
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 which
|
||||
lead to questionable usefulness".
|
||||
|
||||
Django only supports naive time objects and will raise an exception if you
|
||||
attempt to save an aware time object.
|
||||
|
||||
.. _naive-datetime-objects:
|
||||
|
||||
Interpretation of naive datetime objects
|
||||
----------------------------------------
|
||||
|
||||
When :setting:`USE_TZ` is ``True``, Django still accepts naive datetime
|
||||
objects, in order to preserve backwards-compatibility. It attempts to make them
|
||||
aware by interpreting them in the :ref:`default time zone
|
||||
<default-current-time-zone>`.
|
||||
|
||||
Unfortunately, during DST transitions, some datetimes don't exist or are
|
||||
ambiguous. In such situations, pytz_ raises an exception. Other
|
||||
:class:`~datetime.tzinfo` implementations, such as the local time zone used as
|
||||
a fallback when pytz_ isn't installed, may raise an exception or return
|
||||
inaccurate results. That's why you should always create aware datetime objects
|
||||
when time zone support is enabled.
|
||||
|
||||
In practice, this is rarely an issue. Django gives you aware datetime objects
|
||||
in the models and forms, and most often, new datetime objects are created from
|
||||
existing ones through :class:`~datetime.timedelta` arithmetic. The only
|
||||
datetime that's often created in application code is the current time, and
|
||||
:func:`timezone.now() <django.utils.timezone.now>` automatically does the
|
||||
right 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.
|
||||
|
||||
When pytz_ is available, Django loads the definition of the default time zone
|
||||
from the `tz database`_. This is the most accurate solution. Otherwise, it
|
||||
relies on the difference between local time and UTC, as reported by the
|
||||
operating system, to compute conversions. This is less reliable, especially
|
||||
around DST transitions.
|
||||
|
||||
The **current time zone** is the time zone that's used for rendering.
|
||||
|
||||
You should set it to the end user's actual time zone with
|
||||
:func:`~django.utils.timezone.activate`. Otherwise, the default time zone is
|
||||
used.
|
||||
|
||||
.. note::
|
||||
|
||||
As explained in the documentation of :setting:`TIME_ZONE`, Django sets
|
||||
environment variables so that its process runs in the default time zone.
|
||||
This happens regardless of the value of :setting:`USE_TZ` and of the
|
||||
current time zone.
|
||||
|
||||
When :setting:`USE_TZ` is ``True``, this is useful to preserve
|
||||
backwards-compatibility with applications that still rely on local time.
|
||||
However, :ref:`as explained above <naive-datetime-objects>`, this isn't
|
||||
entirely reliable, and you should always work with aware datetimes in UTC
|
||||
in your own code. For instance, use
|
||||
:meth:`~datetime.datetime.utcfromtimestamp` instead of
|
||||
:meth:`~datetime.datetime.fromtimestamp` -- and don't forget to set
|
||||
``tzinfo`` to :data:`~django.utils.timezone.utc`.
|
||||
|
||||
Selecting the current time zone
|
||||
-------------------------------
|
||||
|
||||
The current time zone is the equivalent of the current :term:`locale <locale
|
||||
name>` for translations. However, there's no equivalent of the
|
||||
``Accept-Language`` HTTP header that Django could use to determine the user's
|
||||
time zone automatically. Instead, Django provides :ref:`time zone selection
|
||||
functions <time-zone-selection-functions>`. Use them to build the time zone
|
||||
selection logic that makes sense for you.
|
||||
|
||||
Most websites who care about time zones just ask users in which time zone they
|
||||
live and store this information in the user's profile. For anonymous users,
|
||||
they use the time zone of their primary audience or UTC. pytz_ provides
|
||||
helpers, like a list of time zones per country, that you can use to pre-select
|
||||
the most likely choices.
|
||||
|
||||
Here's an example that stores the current timezone in the session. (It skips
|
||||
error handling entirely for the sake of simplicity.)
|
||||
|
||||
Add the following middleware to :setting:`MIDDLEWARE_CLASSES`::
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
class TimezoneMiddleware(object):
|
||||
def process_request(self, request):
|
||||
tz = request.session.get('django_timezone')
|
||||
if tz:
|
||||
timezone.activate(tz)
|
||||
|
||||
Create a view that can set the current timezone::
|
||||
|
||||
import pytz
|
||||
from django.shortcuts import redirect, render
|
||||
|
||||
def set_timezone(request):
|
||||
if request.method == 'POST':
|
||||
request.session[session_key] = pytz.timezone(request.POST['timezone'])
|
||||
return redirect('/')
|
||||
else:
|
||||
return render(request, 'template.html', {'timezones': pytz.common_timezones})
|
||||
|
||||
Include in :file:`template.html` a form that will ``POST`` to this view:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{% load tz %}{% load url from future %}
|
||||
<form action="{% url 'set_timezone' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<label for="timezone">Time zone:</label>
|
||||
<select name="timezone">
|
||||
{% for tz in timezones %}
|
||||
<option value="{{ tz }}"{% if tz == TIME_ZONE %} selected="selected"{% endif %}>{{ tz }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Set" />
|
||||
</form>
|
||||
|
||||
Time zone aware input in forms
|
||||
==============================
|
||||
|
||||
When you enable time zone support, Django interprets datetimes entered in
|
||||
forms in the :ref:`current time zone <default-current-time-zone>` and returns
|
||||
aware datetime objects in ``cleaned_data``.
|
||||
|
||||
If the current time zone raises an exception for datetimes that don't exist or
|
||||
are ambiguous because they fall in a DST transition (the timezones provided by
|
||||
pytz_ do this), such datetimes 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 to
|
||||
the :ref:`current time zone <default-current-time-zone>` when they're rendered
|
||||
in templates. This behaves very much like :doc:`format localization
|
||||
</topics/i18n/formatting>`.
|
||||
|
||||
.. warning::
|
||||
|
||||
Django doesn't convert naive datetime objects, because they could be
|
||||
ambiguous, and because your code should never produce naive datetimes when
|
||||
time zone support is enabled. However, you can force conversion with the
|
||||
template filters described below.
|
||||
|
||||
Conversion to local time isn't always appropriate -- you may be generating
|
||||
output for computers rather than for humans. The following filters and tags,
|
||||
provided the ``tz`` template library, allow you to control the time zone
|
||||
conversions.
|
||||
|
||||
Template tags
|
||||
-------------
|
||||
|
||||
.. templatetag:: localtime
|
||||
|
||||
localtime
|
||||
~~~~~~~~~
|
||||
|
||||
Enables or disables conversion of aware datetime objects to the current time
|
||||
zone in the contained block.
|
||||
|
||||
This tag has exactly the same effects as the :setting:`USE_TZ` setting as far
|
||||
as the template engine is concerned. It allows a more fine grained control of
|
||||
conversion.
|
||||
|
||||
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 current
|
||||
time zone is unset, the default time zone applies.
|
||||
|
||||
::
|
||||
|
||||
{% load tz %}
|
||||
|
||||
{% timezone "Europe/Paris" %}
|
||||
Paris time: {{ value }}
|
||||
{% endtimezone %}
|
||||
|
||||
{% timezone None %}
|
||||
Server time: {{ value }}
|
||||
{% endtimezone %}
|
||||
|
||||
.. note::
|
||||
|
||||
In the second block, ``None`` resolves to the Python object ``None``
|
||||
because isn't defined in the template context, not because it's the string
|
||||
``None``.
|
||||
|
||||
.. templatetag:: get_current_timezone
|
||||
|
||||
get_current_timezone
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When the :func:`django.core.context_processors.tz` context processor is
|
||||
enabled -- by default, it is -- each :class:`~django.template.RequestContext`
|
||||
contains a ``TIME_ZONE`` variable that provides the name of the current time
|
||||
zone.
|
||||
|
||||
If you don't use a :class:`~django.template.RequestContext`, you can obtain
|
||||
this value with the ``get_current_timezone`` tag::
|
||||
|
||||
{% get_current_timezone as TIME_ZONE %}
|
||||
|
||||
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 always
|
||||
return aware datetimes.
|
||||
|
||||
.. templatefilter:: aslocaltime
|
||||
|
||||
aslocaltime
|
||||
~~~~~~~~~~~
|
||||
|
||||
Forces conversion of a single value to the current time zone.
|
||||
|
||||
For example::
|
||||
|
||||
{% load tz %}
|
||||
|
||||
{{ value|aslocaltime }}
|
||||
|
||||
.. templatefilter:: asutc
|
||||
|
||||
asutc
|
||||
~~~~~
|
||||
|
||||
Forces conversion of a single value to UTC.
|
||||
|
||||
For example::
|
||||
|
||||
{% load tz %}
|
||||
|
||||
{{ value|asutc }}
|
||||
|
||||
astimezone
|
||||
~~~~~~~~~~
|
||||
|
||||
Forces conversion of a single value to an arbitrary timezone.
|
||||
|
||||
The argument must be an instance of a :class:`~datetime.tzinfo` subclass or a
|
||||
time zone name. If it is a time zone name, pytz_ is required.
|
||||
|
||||
For example::
|
||||
|
||||
{% load tz %}
|
||||
|
||||
{{ value|astimezone:"Europe/Paris" }}
|
||||
|
||||
.. _time-zones-migration-guide:
|
||||
|
||||
Migration guide
|
||||
===============
|
||||
|
||||
Here's how to migrate a project that was started before Django supported time
|
||||
zones.
|
||||
|
||||
Data
|
||||
----
|
||||
|
||||
PostgreSQL
|
||||
~~~~~~~~~~
|
||||
|
||||
The PostgreSQL backend stores datetimes as ``timestamp with time zone``. In
|
||||
practice, this means it converts datetimes from the connection's time zone to
|
||||
UTC 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 zone
|
||||
will be set to :setting:`TIME_ZONE` or ``UTC`` respectively, so that Django
|
||||
obtains correct datetimes in all cases. You don't need to perform any data
|
||||
conversions.
|
||||
|
||||
Other databases
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Other backends store datetimes without time zone information. If you switch
|
||||
from ``USE_TZ = False`` to ``USE_TZ = True``, you must convert your data from
|
||||
local 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 settings
|
||||
file and install pytz_ (if possible). At this point, things should mostly
|
||||
work. If you create naive datetime objects in your code, Django makes them
|
||||
aware when necessary.
|
||||
|
||||
However, these conversions may fail around DST transitions, which means you
|
||||
aren't getting the full benefits of time zone support yet. Also, you're likely
|
||||
to run into a few problems because it's impossible to compare a naive datetime
|
||||
with an aware datetime. Since Django now gives you aware datetimes, you'll get
|
||||
exceptions wherever you compare a datetime that comes from a model or a form
|
||||
with a naive datetime that you've created in your code.
|
||||
|
||||
So the second step is to refactor your code wherever you instanciate datetime
|
||||
objects to make them aware. This can be done incrementally.
|
||||
:mod:`django.utils.timezone` defines some handy helpers for compatibility
|
||||
code: :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`.
|
||||
|
||||
.. _pytz: http://pytz.sourceforge.net/
|
||||
.. _these issues: http://pytz.sourceforge.net/#problems-with-localtime
|
||||
.. _tz database: http://en.wikipedia.org/wiki/Tz_database
|
Loading…
Add table
Add a link
Reference in a new issue