summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorCan Sarigol <ertugrulsarigol@gmail.com>2021-11-11 09:57:50 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-11-12 11:14:08 +0100
commitd54aa49a7d15e708b7da1620310167f39e859cba (patch)
treecf38196b421ddcdddd067b9b7f036dfd682a3e99 /django
parent45de30dc693fcd63d33816a85fb02abd5f7a10b4 (diff)
[4.0.x] Fixed #33279 -- Fixed handling time zones with "-" sign in names.
Thanks yakimka for the report. Regression in fde9b7d35e4e185903cc14aa587ca870037941b1. Backport of 661316b066923493ff91d6d2aa92e463f595a6b1 from main.
Diffstat (limited to 'django')
-rw-r--r--django/db/backends/mysql/operations.py8
-rw-r--r--django/db/backends/oracle/operations.py11
-rw-r--r--django/db/backends/postgresql/operations.py9
-rw-r--r--django/db/backends/sqlite3/base.py13
-rw-r--r--django/db/backends/utils.py13
5 files changed, 31 insertions, 23 deletions
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py
index 89730cee29..3e1faad41b 100644
--- a/django/db/backends/mysql/operations.py
+++ b/django/db/backends/mysql/operations.py
@@ -2,6 +2,7 @@ import uuid
from django.conf import settings
from django.db.backends.base.operations import BaseDatabaseOperations
+from django.db.backends.utils import split_tzname_delta
from django.utils import timezone
from django.utils.encoding import force_str
@@ -76,11 +77,8 @@ class DatabaseOperations(BaseDatabaseOperations):
return "DATE(%s)" % (field_name)
def _prepare_tzname_delta(self, tzname):
- if '+' in tzname:
- return tzname[tzname.find('+'):]
- elif '-' in tzname:
- return tzname[tzname.find('-'):]
- return tzname
+ tzname, sign, offset = split_tzname_delta(tzname)
+ return f'{sign}{offset}' if offset else tzname
def _convert_field_to_tz(self, field_name, tzname):
if tzname and settings.USE_TZ and self.connection.timezone_name != tzname:
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index 4cfc7da070..f497390bea 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -5,7 +5,9 @@ from functools import lru_cache
from django.conf import settings
from django.db import DatabaseError, NotSupportedError
from django.db.backends.base.operations import BaseDatabaseOperations
-from django.db.backends.utils import strip_quotes, truncate_name
+from django.db.backends.utils import (
+ split_tzname_delta, strip_quotes, truncate_name,
+)
from django.db.models import AutoField, Exists, ExpressionWrapper, Lookup
from django.db.models.expressions import RawSQL
from django.db.models.sql.where import WhereNode
@@ -108,11 +110,8 @@ END;
_tzname_re = _lazy_re_compile(r'^[\w/:+-]+$')
def _prepare_tzname_delta(self, tzname):
- if '+' in tzname:
- return tzname[tzname.find('+'):]
- elif '-' in tzname:
- return tzname[tzname.find('-'):]
- return tzname
+ tzname, sign, offset = split_tzname_delta(tzname)
+ return f'{sign}{offset}' if offset else tzname
def _convert_field_to_tz(self, field_name, tzname):
if not (settings.USE_TZ and tzname):
diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py
index 8d19872bea..399c1b24e7 100644
--- a/django/db/backends/postgresql/operations.py
+++ b/django/db/backends/postgresql/operations.py
@@ -2,6 +2,7 @@ from psycopg2.extras import Inet
from django.conf import settings
from django.db.backends.base.operations import BaseDatabaseOperations
+from django.db.backends.utils import split_tzname_delta
class DatabaseOperations(BaseDatabaseOperations):
@@ -44,10 +45,10 @@ class DatabaseOperations(BaseDatabaseOperations):
return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
def _prepare_tzname_delta(self, tzname):
- if '+' in tzname:
- return tzname.replace('+', '-')
- elif '-' in tzname:
- return tzname.replace('-', '+')
+ tzname, sign, offset = split_tzname_delta(tzname)
+ if offset:
+ sign = '-' if sign == '+' else '+'
+ return f'{tzname}{sign}{offset}'
return tzname
def _convert_field_to_tz(self, field_name, tzname):
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index ddf5f40c8e..76ce12ac6d 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -433,14 +433,11 @@ def _sqlite_datetime_parse(dt, tzname=None, conn_tzname=None):
if conn_tzname:
dt = dt.replace(tzinfo=timezone_constructor(conn_tzname))
if tzname is not None and tzname != conn_tzname:
- sign_index = tzname.find('+') + tzname.find('-') + 1
- if sign_index > -1:
- sign = tzname[sign_index]
- tzname, offset = tzname.split(sign)
- if offset:
- hours, minutes = offset.split(':')
- offset_delta = datetime.timedelta(hours=int(hours), minutes=int(minutes))
- dt += offset_delta if sign == '+' else -offset_delta
+ tzname, sign, offset = backend_utils.split_tzname_delta(tzname)
+ if offset:
+ hours, minutes = offset.split(':')
+ offset_delta = datetime.timedelta(hours=int(hours), minutes=int(minutes))
+ dt += offset_delta if sign == '+' else -offset_delta
dt = timezone.localtime(dt, timezone_constructor(tzname))
return dt
diff --git a/django/db/backends/utils.py b/django/db/backends/utils.py
index c342cf79b5..ff7523742f 100644
--- a/django/db/backends/utils.py
+++ b/django/db/backends/utils.py
@@ -7,6 +7,7 @@ import time
from contextlib import contextmanager
from django.db import NotSupportedError
+from django.utils.dateparse import parse_time
logger = logging.getLogger('django.db.backends')
@@ -130,6 +131,18 @@ class CursorDebugWrapper(CursorWrapper):
)
+def split_tzname_delta(tzname):
+ """
+ Split a time zone name into a 3-tuple of (name, sign, offset).
+ """
+ for sign in ['+', '-']:
+ if sign in tzname:
+ name, offset = tzname.rsplit(sign, 1)
+ if offset and parse_time(offset):
+ return name, sign, offset
+ return tzname, None, None
+
+
###############################################
# Converters from database (string) to Python #
###############################################