When support for time zones is enabled, Django stores datetime 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 datetime information according to each user’s wall clock.

Even if your website is available in only one time zone, it’s still good
practice to store data in UTC in your database. The main reason is Daylight
Saving Time (DST). Many countries have a system of DST, where clocks are moved
forward 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.
(The pytz documentation discusses these issues in greater detail.) This
probably doesn’t matter for your blog, but it’s a problem 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 use local time only when
interacting with end users.

Time zone support is disabled by default. To enable it, set USE_TZ=True in your settings file. Installing pytz is highly recommended,
but may not be mandatory depending on your particular database backend,
operating system and time zone. If you encounter an exception querying dates
or times, please try installing it before filing a bug. It’s as simple as:

Python’s datetime.datetime objects have a tzinfo attribute that
can be used to store time zone information, represented as an instance of a
subclass of datetime.tzinfo. When this attribute is set and describes
an offset, a datetime object is aware. Otherwise, it’s 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:

importdatetimenow=datetime.datetime.now()

When time zone support is enabled (USE_TZ=True), 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:

fromdjango.utilsimporttimezonenow=timezone.now()

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 the pytz documentation
carefully.

Note

Python’s datetime.time objects also feature a tzinfo
attribute, and PostgreSQL has a matching timewithtimezone 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, as a timezone for a time with no
associated date does not make sense.

When USE_TZ is True, Django still accepts naive datetime
objects, in order to preserve backwards-compatibility. When the database layer
receives one, it attempts to make it aware by interpreting it in the
default time zone and raises a warning.

Unfortunately, during DST transitions, some datetimes don’t exist or are
ambiguous. In such situations, pytz raises an exception. Other
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 timedelta arithmetic. The only
datetime that’s often created in application code is the current time, and
timezone.now() automatically does the
right thing.

The default time zone is the time zone defined by the 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
activate(). Otherwise, the default time zone is
used.

Note

As explained in the documentation of TIME_ZONE, Django sets
environment variables so that its process runs in the default time zone.
This happens regardless of the value of USE_TZ and of the
current time zone.

When USE_TZ is True, this is useful to preserve
backwards-compatibility with applications that still rely on local time.
However, as explained above, this isn’t
entirely reliable, and you should always work with aware datetimes in UTC
in your own code. For instance, use
utcfromtimestamp() instead of
fromtimestamp() – and don’t forget to set
tzinfo to utc.

The current time zone is the equivalent of the current locale 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 time zone selection
functions. Use them to build the time zone
selection logic that makes sense for you.

Most websites that 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.)

When you enable time zone support, Django interprets datetimes entered in
forms in the 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.

When you enable time zone support, Django converts aware datetime objects to
the current time zone when they’re rendered
in templates. This behaves very much like format localization.

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 by the tz template tag library, allow you to control the time zone
conversions.

The PostgreSQL backend stores datetimes as timestampwithtimezone. 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 TIME_ZONE or UTC respectively, so that Django
obtains correct datetimes in all cases. You don’t need to perform any data
conversions.

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.

The first step is to add USE_TZ=True 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.

When serializing an aware datetime, the UTC offset is included, like this:

"2011-09-01T13:20:30+03:00"

For a naive datetime, it obviously isn’t:

"2011-09-01T13:20:30"

For models with DateTimeFields, this difference
makes it impossible to write a fixture that works both with and without time
zone 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 time
zone support, you’ll see RuntimeWarnings when you load them. To get
rid of the warnings, you must convert your fixtures to the “aware” format.

You can regenerate fixtures with loaddata then dumpdata.
Or, if they’re small enough, you can simply edit them to add the UTC offset
that matches your TIME_ZONE to each serialized datetime.

Yes. When time zone support is enabled, Django uses a more accurate model
of local time. This shields you from subtle and unreproducible bugs around
Daylight Saving Time (DST) transitions.

In this regard, time zones are comparable to unicode in Python. At first
it’s hard. You get encoding and decoding errors. Then you learn the rules.
And some problems disappear – you never get mangled output again when your
application receives non-ASCII input.

When you enable time zone support, you’ll encounter some errors because
you’re using naive datetimes where Django expects aware datetimes. Such
errors show up when running tests and they’re easy to fix. You’ll quickly
learn how to avoid invalid operations.

On the other hand, bugs caused by the lack of time zone support are much
harder to prevent, diagnose and fix. Anything that involves scheduled tasks
or datetime arithmetic is a candidate for subtle bugs that will bite you
only 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 still
shoot yourself in the foot by carelessly turning naive datetimes into aware
datetimes, and vice-versa.

If your application connects to other systems – for instance, if it queries
a Web service – make sure datetimes are properly specified. To transmit
datetimes safely, their representation should include the UTC offset, or
their values should be in UTC (or both!).

(To implement this function, you must decide whether 2012-02-29 minus
one year is 2011-02-28 or 2011-03-01, which depends on your business
requirements.)

Should I install pytz?

Yes. Django has a policy of not requiring external dependencies, and for
this reason pytz is optional. However, it’s much safer to install it.

As soon as you activate time zone support, Django needs a definition of the
default time zone. When pytz is available, Django loads this definition
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.

Furthermore, if you want to support users in more than one time zone, pytz
is the reference for time zone definitions.

How do I interact with a database that stores datetimes in local time?

Set the TIME_ZONE option to the appropriate
time zone for this database in the DATABASES setting.

This is useful for connecting to a database that doesn’t support time zones
and that isn’t managed by Django when USE_TZ is True.

If you encounter this error, most likely your code is comparing these two
things:

a datetime provided by Django – for instance, a value read from a form or
a model field. Since you enabled time zone support, it’s aware.

a datetime generated by your code, which is naive (or you wouldn’t be
reading this).

Generally, the correct solution is to change your code to use an aware
datetime instead.

If you’re writing a pluggable application that’s expected to work
independently of the value of USE_TZ, you may find
django.utils.timezone.now() useful. This function returns the current
date and time as a naive datetime when USE_TZ=False and as an aware
datetime when USE_TZ=True. You can add or subtract
datetime.timedelta as needed.

I see lots ofRuntimeWarning:DateTimeFieldreceivedanaivedatetime(YYYY-MM-DDHH:MM:SS)whiletimezonesupportisactive– is that bad?

When time zone support is enabled, the database layer expects to receive
only aware datetimes from your code. This warning occurs when it receives a
naive datetime. This indicates that you haven’t finished porting your code
for time zone support. Please refer to the migration guide for tips on this process.

In the meantime, for backwards compatibility, the datetime is considered to
be 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 can
convert a datetime to a date by calling its date()
method. You also consider that a date is a lot like a
datetime, except that it’s less accurate.

As this example shows, the same datetime has a different date, depending on
the time zone in which it is represented. But the real problem is more
fundamental.

A datetime represents a point in time. It’s absolute: it doesn’t depend
on anything. On the contrary, a date is a calendaring concept. It’s a
period of time whose bounds depend on the time zone in which the date is
considered. 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 datetime to
date. For instance, you can use the date
template filter to only show the date part of a datetime. This filter will
convert 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 the
datetime is converted to the appropriate time zone first. Usually, this
will be the current timezone:

>>> fromdjango.utilsimporttimezone>>> timezone.activate(pytz.timezone("Asia/Singapore"))# For this example, we just 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()# Again, this is the correct way to convert between time zones with pytz.>>> local=current_tz.normalize(paris.astimezone(current_tz))>>> localdatetime.datetime(2012, 3, 3, 8, 30, tzinfo=<DstTzInfo 'Asia/Singapore' SGT+8:00:00 STD>)>>> local.date()datetime.date(2012, 3, 3)

I get an error “Aretimezonedefinitionsforyourdatabaseandpytzinstalled?” pytz is installed, so I guess the problem is my database?

If you are using MySQL, see the Time zone definitions section
of the MySQL notes for instructions on loading time zone definitions.

Note that localize is a pytz extension to the tzinfo
API. Also, you may want to catch pytz.InvalidTimeError. The
documentation of pytz contains more examples. You should review it
before attempting to manipulate aware datetimes.

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 the
template layer provides filters and tags
to convert datetimes to the time zone of your choice.

Furthermore, Python knows how to compare aware datetimes, taking into
account 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 django.utils.timezone.now() will be
sufficient.

For the sake of completeness, though, if you really want the local time
in the current time zone, here’s how you can obtain it: