summaryrefslogtreecommitdiff
path: root/django/db
diff options
context:
space:
mode:
authorSigurd Ljødal <sigurd@ljodal.no>2017-09-28 22:39:03 +0200
committerTim Graham <timograham@gmail.com>2018-01-27 09:59:13 -0500
commita455e732a0011dfbd15eddaa4fe60cb9439326e0 (patch)
tree1df72fb36c377ae630e1bbc8a588662f8f3cb46f /django/db
parentf229049d6cd186025f4033dc47608b0c9731e882 (diff)
Fixed #28650 -- Added TruncWeek database function.
Diffstat (limited to 'django/db')
-rw-r--r--django/db/backends/mysql/operations.py10
-rw-r--r--django/db/backends/oracle/operations.py4
-rw-r--r--django/db/backends/sqlite3/base.py6
-rw-r--r--django/db/models/functions/__init__.py4
-rw-r--r--django/db/models/functions/datetime.py8
-rw-r--r--django/db/models/query.py8
6 files changed, 33 insertions, 7 deletions
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py
index ad4c13652e..2961d6449b 100644
--- a/django/db/backends/mysql/operations.py
+++ b/django/db/backends/mysql/operations.py
@@ -53,6 +53,10 @@ class DatabaseOperations(BaseDatabaseOperations):
return "MAKEDATE(YEAR(%s), 1) + INTERVAL QUARTER(%s) QUARTER - INTERVAL 1 QUARTER" % (
field_name, field_name
)
+ elif lookup_type == 'week':
+ return "DATE_SUB(%s, INTERVAL WEEKDAY(%s) DAY)" % (
+ field_name, field_name
+ )
else:
return "DATE(%s)" % (field_name)
@@ -84,6 +88,12 @@ class DatabaseOperations(BaseDatabaseOperations):
"INTERVAL QUARTER({field_name}) QUARTER - " +
"INTERVAL 1 QUARTER, '%%Y-%%m-01 00:00:00') AS DATETIME)"
).format(field_name=field_name)
+ if lookup_type == 'week':
+ return (
+ "CAST(DATE_FORMAT(DATE_SUB({field_name}, "
+ "INTERVAL WEEKDAY({field_name}) DAY), "
+ "'%%Y-%%m-%%d 00:00:00') AS DATETIME)"
+ ).format(field_name=field_name)
try:
i = fields.index(lookup_type) + 1
except ValueError:
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index bea03e355e..748f606368 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -78,6 +78,8 @@ END;
return "TRUNC(%s, '%s')" % (field_name, lookup_type.upper())
elif lookup_type == 'quarter':
return "TRUNC(%s, 'Q')" % field_name
+ elif lookup_type == 'week':
+ return "TRUNC(%s, 'IW')" % field_name
else:
return "TRUNC(%s)" % field_name
@@ -116,6 +118,8 @@ END;
sql = "TRUNC(%s, '%s')" % (field_name, lookup_type.upper())
elif lookup_type == 'quarter':
sql = "TRUNC(%s, 'Q')" % field_name
+ elif lookup_type == 'week':
+ sql = "TRUNC(%s, 'IW')" % field_name
elif lookup_type == 'day':
sql = "TRUNC(%s)" % field_name
elif lookup_type == 'hour':
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 857b050361..0971c500dd 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -335,6 +335,9 @@ def _sqlite_date_trunc(lookup_type, dt):
return '%i-%02i-01' % (dt.year, month_in_quarter)
elif lookup_type == 'month':
return "%i-%02i-01" % (dt.year, dt.month)
+ elif lookup_type == 'week':
+ dt = dt - datetime.timedelta(days=dt.weekday())
+ return "%i-%02i-%02i" % (dt.year, dt.month, dt.day)
elif lookup_type == 'day':
return "%i-%02i-%02i" % (dt.year, dt.month, dt.day)
@@ -403,6 +406,9 @@ def _sqlite_datetime_trunc(lookup_type, dt, tzname):
return '%i-%02i-01 00:00:00' % (dt.year, month_in_quarter)
elif lookup_type == 'month':
return "%i-%02i-01 00:00:00" % (dt.year, dt.month)
+ elif lookup_type == 'week':
+ dt = dt - datetime.timedelta(days=dt.weekday())
+ return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day)
elif lookup_type == 'day':
return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day)
elif lookup_type == 'hour':
diff --git a/django/db/models/functions/__init__.py b/django/db/models/functions/__init__.py
index 0680200c7a..0815628dd0 100644
--- a/django/db/models/functions/__init__.py
+++ b/django/db/models/functions/__init__.py
@@ -3,7 +3,7 @@ from .datetime import (
Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,
ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay, ExtractYear,
Now, Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth,
- TruncQuarter, TruncSecond, TruncTime, TruncYear,
+ TruncQuarter, TruncSecond, TruncTime, TruncWeek, TruncYear,
)
from .text import (
Concat, ConcatPair, Length, Lower, Replace, StrIndex, Substr, Upper,
@@ -21,7 +21,7 @@ __all__ = [
'ExtractQuarter', 'ExtractSecond', 'ExtractWeek', 'ExtractWeekDay',
'ExtractYear', 'Now', 'Trunc', 'TruncDate', 'TruncDay', 'TruncHour',
'TruncMinute', 'TruncMonth', 'TruncQuarter', 'TruncSecond', 'TruncTime',
- 'TruncYear',
+ 'TruncWeek', 'TruncYear',
# text
'Concat', 'ConcatPair', 'Length', 'Lower', 'Replace', 'StrIndex', 'Substr',
'Upper',
diff --git a/django/db/models/functions/datetime.py b/django/db/models/functions/datetime.py
index 44bcb731e6..74b88b77ca 100644
--- a/django/db/models/functions/datetime.py
+++ b/django/db/models/functions/datetime.py
@@ -199,7 +199,8 @@ class TruncBase(TimezoneMixin, Transform):
field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField'
))
elif isinstance(field, TimeField) and (
- isinstance(output_field, DateTimeField) or copy.kind in ('year', 'quarter', 'month', 'day', 'date')):
+ isinstance(output_field, DateTimeField) or
+ copy.kind in ('year', 'quarter', 'month', 'week', 'day', 'date')):
raise ValueError("Cannot truncate TimeField '%s' to %s. " % (
field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField'
))
@@ -242,6 +243,11 @@ class TruncMonth(TruncBase):
kind = 'month'
+class TruncWeek(TruncBase):
+ """Truncate to midnight on the Monday of the week."""
+ kind = 'week'
+
+
class TruncDay(TruncBase):
kind = 'day'
diff --git a/django/db/models/query.py b/django/db/models/query.py
index e17f8da1a8..44bc51d0e0 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -777,8 +777,8 @@ class QuerySet:
Return a list of date objects representing all available dates for
the given field_name, scoped to 'kind'.
"""
- assert kind in ("year", "month", "day"), \
- "'kind' must be one of 'year', 'month' or 'day'."
+ assert kind in ('year', 'month', 'week', 'day'), \
+ "'kind' must be one of 'year', 'month', 'week', or 'day'."
assert order in ('ASC', 'DESC'), \
"'order' must be either 'ASC' or 'DESC'."
return self.annotate(
@@ -793,8 +793,8 @@ class QuerySet:
Return a list of datetime objects representing all available
datetimes for the given field_name, scoped to 'kind'.
"""
- assert kind in ("year", "month", "day", "hour", "minute", "second"), \
- "'kind' must be one of 'year', 'month', 'day', 'hour', 'minute' or 'second'."
+ assert kind in ('year', 'month', 'week', 'day', 'hour', 'minute', 'second'), \
+ "'kind' must be one of 'year', 'month', 'week', 'day', 'hour', 'minute', or 'second'."
assert order in ('ASC', 'DESC'), \
"'order' must be either 'ASC' or 'DESC'."
if settings.USE_TZ: