Fixed #23524 -- Allowed DATABASES['TIME_ZONE'] option on PostgreSQL.
This commit is contained in:
parent
ad88524e4d
commit
c06492dd87
|
@ -124,11 +124,11 @@ class BaseDatabaseWrapper:
|
|||
|
||||
When the database backend supports time zones, it doesn't matter which
|
||||
time zone Django uses, as long as aware datetimes are used everywhere.
|
||||
For simplicity, Django selects UTC.
|
||||
Other users connecting to the database can choose their own time zone.
|
||||
|
||||
When the database backend doesn't support time zones, the time zone
|
||||
Django uses can be selected with the TIME_ZONE configuration option, so
|
||||
it can match what other users of the database expect.
|
||||
Django uses may be constrained by the requirements of other users of
|
||||
the database.
|
||||
"""
|
||||
if not settings.USE_TZ:
|
||||
return None
|
||||
|
@ -205,15 +205,11 @@ class BaseDatabaseWrapper:
|
|||
self.run_on_commit = []
|
||||
|
||||
def check_settings(self):
|
||||
if self.settings_dict['TIME_ZONE'] is not None:
|
||||
if not settings.USE_TZ:
|
||||
raise ImproperlyConfigured(
|
||||
"Connection '%s' cannot set TIME_ZONE because USE_TZ is "
|
||||
"False." % self.alias)
|
||||
elif self.features.supports_timezones:
|
||||
raise ImproperlyConfigured(
|
||||
"Connection '%s' cannot set TIME_ZONE because its engine "
|
||||
"handles time zones conversions natively." % self.alias)
|
||||
if self.settings_dict['TIME_ZONE'] is not None and not settings.USE_TZ:
|
||||
raise ImproperlyConfigured(
|
||||
"Connection '%s' cannot set TIME_ZONE because USE_TZ is False."
|
||||
% self.alias
|
||||
)
|
||||
|
||||
@async_unsafe
|
||||
def ensure_connection(self):
|
||||
|
|
|
@ -47,7 +47,6 @@ from .features import DatabaseFeatures # NOQA isort:skip
|
|||
from .introspection import DatabaseIntrospection # NOQA isort:skip
|
||||
from .operations import DatabaseOperations # NOQA isort:skip
|
||||
from .schema import DatabaseSchemaEditor # NOQA isort:skip
|
||||
from .utils import utc_tzinfo_factory # NOQA isort:skip
|
||||
|
||||
psycopg2.extensions.register_adapter(SafeString, psycopg2.extensions.QuotedString)
|
||||
psycopg2.extras.register_uuid()
|
||||
|
@ -231,9 +230,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
cursor = self.connection.cursor(name, scrollable=False, withhold=self.connection.autocommit)
|
||||
else:
|
||||
cursor = self.connection.cursor()
|
||||
cursor.tzinfo_factory = utc_tzinfo_factory if settings.USE_TZ else None
|
||||
cursor.tzinfo_factory = self.tzinfo_factory if settings.USE_TZ else None
|
||||
return cursor
|
||||
|
||||
def tzinfo_factory(self, offset):
|
||||
return self.timezone
|
||||
|
||||
@async_unsafe
|
||||
def chunked_cursor(self):
|
||||
self._named_cursor_idx += 1
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
from django.utils.timezone import utc
|
||||
|
||||
|
||||
def utc_tzinfo_factory(offset):
|
||||
if offset != 0:
|
||||
raise AssertionError("database connection isn't set to UTC")
|
||||
return utc
|
|
@ -121,8 +121,11 @@ Django needs the following parameters for its database connections:
|
|||
- ``client_encoding``: ``'UTF8'``,
|
||||
- ``default_transaction_isolation``: ``'read committed'`` by default,
|
||||
or the value set in the connection options (see below),
|
||||
- ``timezone``: ``'UTC'`` when :setting:`USE_TZ` is ``True``, value of
|
||||
:setting:`TIME_ZONE` otherwise.
|
||||
- ``timezone``:
|
||||
- when :setting:`USE_TZ` is ``True``, ``'UTC'`` by default, or the
|
||||
:setting:`TIME_ZONE <DATABASE-TIME_ZONE>` value set for the connection,
|
||||
- when :setting:`USE_TZ` is ``False``, the value of the global
|
||||
:setting:`TIME_ZONE` setting.
|
||||
|
||||
If these parameters already have the correct values, Django won't set them for
|
||||
every new connection, which improves performance slightly. You can configure
|
||||
|
|
|
@ -632,24 +632,53 @@ default port. Not used with SQLite.
|
|||
|
||||
Default: ``None``
|
||||
|
||||
A string representing the time zone for datetimes stored in this database
|
||||
(assuming that it doesn't support time zones) or ``None``. This inner option of
|
||||
the :setting:`DATABASES` setting accepts the same values as the general
|
||||
:setting:`TIME_ZONE` setting.
|
||||
|
||||
This allows interacting with third-party databases that store datetimes in
|
||||
local time rather than UTC. To avoid issues around DST changes, you shouldn't
|
||||
set this option for databases managed by Django.
|
||||
|
||||
When :setting:`USE_TZ` is ``True`` and the database doesn't support time zones
|
||||
(e.g. SQLite, MySQL, Oracle), Django reads and writes datetimes in local time
|
||||
according to this option if it is set and in UTC if it isn't.
|
||||
|
||||
When :setting:`USE_TZ` is ``True`` and the database supports time zones (e.g.
|
||||
PostgreSQL), it is an error to set this option.
|
||||
A string representing the time zone for this database connection or ``None``.
|
||||
This inner option of the :setting:`DATABASES` setting accepts the same values
|
||||
as the general :setting:`TIME_ZONE` setting.
|
||||
|
||||
When :setting:`USE_TZ` is ``True`` and this option is set, reading datetimes
|
||||
from the database returns aware datetimes in this time zone instead of UTC.
|
||||
When :setting:`USE_TZ` is ``False``, it is an error to set this option.
|
||||
|
||||
* If the database backend doesn't support time zones (e.g. SQLite, MySQL,
|
||||
Oracle), Django reads and writes datetimes in local time according to this
|
||||
option if it is set and in UTC if it isn't.
|
||||
|
||||
Changing the connection time zone changes how datetimes are read from and
|
||||
written to the database.
|
||||
|
||||
* If Django manages the database and you don't have a strong reason to do
|
||||
otherwise, you should leave this option unset. It's best to store datetimes
|
||||
in UTC because it avoids ambiguous or nonexistent datetimes during daylight
|
||||
saving time changes. Also, receiving datetimes in UTC keeps datetime
|
||||
arithmetic simple — there's no need for the ``normalize()`` method provided
|
||||
by pytz.
|
||||
|
||||
* If you're connecting to a third-party database that stores datetimes in a
|
||||
local time rather than UTC, then you must set this option to the
|
||||
appropriate time zone. Likewise, if Django manages the database but
|
||||
third-party systems connect to the same database and expect to find
|
||||
datetimes in local time, then you must set this option.
|
||||
|
||||
* If the database backend supports time zones (e.g. PostgreSQL), the
|
||||
``TIME_ZONE`` option is very rarely needed. It can be changed at any time;
|
||||
the database takes care of converting datetimes to the desired time zone.
|
||||
|
||||
Setting the time zone of the database connection may be useful for running
|
||||
raw SQL queries involving date/time functions provided by the database, such
|
||||
as ``date_trunc``, because their results depend on the time zone.
|
||||
|
||||
However, this has a downside: receiving all datetimes in local time makes
|
||||
datetime arithmetic more tricky — you must call the ``normalize()`` method
|
||||
provided by pytz after each operation.
|
||||
|
||||
Consider converting to local time explicitly with ``AT TIME ZONE`` in raw SQL
|
||||
queries instead of setting the ``TIME_ZONE`` option.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
Using this option when the database backend supports time zones was allowed.
|
||||
|
||||
.. setting:: DATABASE-DISABLE_SERVER_SIDE_CURSORS
|
||||
|
||||
``DISABLE_SERVER_SIDE_CURSORS``
|
||||
|
|
|
@ -283,6 +283,9 @@ Miscellaneous
|
|||
:class:`pathlib.Path` instead of :mod:`os.path` for building filesystem
|
||||
paths.
|
||||
|
||||
* The :setting:`TIME_ZONE <DATABASE-TIME_ZONE>` setting is now allowed on
|
||||
databases that support time zones.
|
||||
|
||||
.. _backwards-incompatible-3.1:
|
||||
|
||||
Backwards incompatible changes in 3.1
|
||||
|
|
|
@ -9,8 +9,7 @@ import pytz
|
|||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import serializers
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import connection, connections
|
||||
from django.db import connection
|
||||
from django.db.models import F, Max, Min
|
||||
from django.http import HttpRequest
|
||||
from django.template import (
|
||||
|
@ -532,6 +531,14 @@ class NewDatabaseTests(TestCase):
|
|||
cursor.execute('SELECT dt FROM timezones_event WHERE dt = %s', [utc_naive_dt])
|
||||
self.assertEqual(cursor.fetchall()[0][0], utc_naive_dt)
|
||||
|
||||
@skipUnlessDBFeature('supports_timezones')
|
||||
def test_cursor_explicit_time_zone(self):
|
||||
with override_database_connection_timezone('Europe/Paris'):
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute('SELECT CURRENT_TIMESTAMP')
|
||||
now = cursor.fetchone()[0]
|
||||
self.assertEqual(now.tzinfo.zone, 'Europe/Paris')
|
||||
|
||||
@requires_tz_support
|
||||
def test_filter_date_field_with_aware_datetime(self):
|
||||
# Regression test for #17742
|
||||
|
@ -595,27 +602,6 @@ class ForcedTimeZoneDatabaseTests(TransactionTestCase):
|
|||
self.assertEqual(event.dt, fake_dt)
|
||||
|
||||
|
||||
@skipUnlessDBFeature('supports_timezones')
|
||||
@override_settings(TIME_ZONE='Africa/Nairobi', USE_TZ=True)
|
||||
class UnsupportedTimeZoneDatabaseTests(TestCase):
|
||||
|
||||
def test_time_zone_parameter_not_supported_if_database_supports_timezone(self):
|
||||
connections.databases['tz'] = connections.databases['default'].copy()
|
||||
connections.databases['tz']['TIME_ZONE'] = 'Asia/Bangkok'
|
||||
tz_conn = connections['tz']
|
||||
try:
|
||||
msg = (
|
||||
"Connection 'tz' cannot set TIME_ZONE because its engine "
|
||||
"handles time zones conversions natively."
|
||||
)
|
||||
with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
||||
tz_conn.cursor()
|
||||
finally:
|
||||
connections['tz'].close() # in case the test fails
|
||||
del connections['tz']
|
||||
del connections.databases['tz']
|
||||
|
||||
|
||||
@override_settings(TIME_ZONE='Africa/Nairobi')
|
||||
class SerializationTests(SimpleTestCase):
|
||||
|
||||
|
|
Loading…
Reference in New Issue