Kaydet (Commit) a455e732 authored tarafından Sigurd Ljødal's avatar Sigurd Ljødal Kaydeden (comit) Tim Graham

Fixed #28650 -- Added TruncWeek database function.

üst f229049d
...@@ -53,6 +53,10 @@ class DatabaseOperations(BaseDatabaseOperations): ...@@ -53,6 +53,10 @@ class DatabaseOperations(BaseDatabaseOperations):
return "MAKEDATE(YEAR(%s), 1) + INTERVAL QUARTER(%s) QUARTER - INTERVAL 1 QUARTER" % ( return "MAKEDATE(YEAR(%s), 1) + INTERVAL QUARTER(%s) QUARTER - INTERVAL 1 QUARTER" % (
field_name, field_name field_name, field_name
) )
elif lookup_type == 'week':
return "DATE_SUB(%s, INTERVAL WEEKDAY(%s) DAY)" % (
field_name, field_name
)
else: else:
return "DATE(%s)" % (field_name) return "DATE(%s)" % (field_name)
...@@ -84,6 +88,12 @@ class DatabaseOperations(BaseDatabaseOperations): ...@@ -84,6 +88,12 @@ class DatabaseOperations(BaseDatabaseOperations):
"INTERVAL QUARTER({field_name}) QUARTER - " + "INTERVAL QUARTER({field_name}) QUARTER - " +
"INTERVAL 1 QUARTER, '%%Y-%%m-01 00:00:00') AS DATETIME)" "INTERVAL 1 QUARTER, '%%Y-%%m-01 00:00:00') AS DATETIME)"
).format(field_name=field_name) ).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: try:
i = fields.index(lookup_type) + 1 i = fields.index(lookup_type) + 1
except ValueError: except ValueError:
......
...@@ -78,6 +78,8 @@ END; ...@@ -78,6 +78,8 @@ END;
return "TRUNC(%s, '%s')" % (field_name, lookup_type.upper()) return "TRUNC(%s, '%s')" % (field_name, lookup_type.upper())
elif lookup_type == 'quarter': elif lookup_type == 'quarter':
return "TRUNC(%s, 'Q')" % field_name return "TRUNC(%s, 'Q')" % field_name
elif lookup_type == 'week':
return "TRUNC(%s, 'IW')" % field_name
else: else:
return "TRUNC(%s)" % field_name return "TRUNC(%s)" % field_name
...@@ -116,6 +118,8 @@ END; ...@@ -116,6 +118,8 @@ END;
sql = "TRUNC(%s, '%s')" % (field_name, lookup_type.upper()) sql = "TRUNC(%s, '%s')" % (field_name, lookup_type.upper())
elif lookup_type == 'quarter': elif lookup_type == 'quarter':
sql = "TRUNC(%s, 'Q')" % field_name sql = "TRUNC(%s, 'Q')" % field_name
elif lookup_type == 'week':
sql = "TRUNC(%s, 'IW')" % field_name
elif lookup_type == 'day': elif lookup_type == 'day':
sql = "TRUNC(%s)" % field_name sql = "TRUNC(%s)" % field_name
elif lookup_type == 'hour': elif lookup_type == 'hour':
......
...@@ -335,6 +335,9 @@ def _sqlite_date_trunc(lookup_type, dt): ...@@ -335,6 +335,9 @@ def _sqlite_date_trunc(lookup_type, dt):
return '%i-%02i-01' % (dt.year, month_in_quarter) return '%i-%02i-01' % (dt.year, month_in_quarter)
elif lookup_type == 'month': elif lookup_type == 'month':
return "%i-%02i-01" % (dt.year, dt.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': elif lookup_type == 'day':
return "%i-%02i-%02i" % (dt.year, dt.month, dt.day) return "%i-%02i-%02i" % (dt.year, dt.month, dt.day)
...@@ -403,6 +406,9 @@ def _sqlite_datetime_trunc(lookup_type, dt, tzname): ...@@ -403,6 +406,9 @@ def _sqlite_datetime_trunc(lookup_type, dt, tzname):
return '%i-%02i-01 00:00:00' % (dt.year, month_in_quarter) return '%i-%02i-01 00:00:00' % (dt.year, month_in_quarter)
elif lookup_type == 'month': elif lookup_type == 'month':
return "%i-%02i-01 00:00:00" % (dt.year, dt.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': elif lookup_type == 'day':
return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day) return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day)
elif lookup_type == 'hour': elif lookup_type == 'hour':
......
...@@ -3,7 +3,7 @@ from .datetime import ( ...@@ -3,7 +3,7 @@ from .datetime import (
Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth, Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,
ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay, ExtractYear, ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay, ExtractYear,
Now, Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth, Now, Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth,
TruncQuarter, TruncSecond, TruncTime, TruncYear, TruncQuarter, TruncSecond, TruncTime, TruncWeek, TruncYear,
) )
from .text import ( from .text import (
Concat, ConcatPair, Length, Lower, Replace, StrIndex, Substr, Upper, Concat, ConcatPair, Length, Lower, Replace, StrIndex, Substr, Upper,
...@@ -21,7 +21,7 @@ __all__ = [ ...@@ -21,7 +21,7 @@ __all__ = [
'ExtractQuarter', 'ExtractSecond', 'ExtractWeek', 'ExtractWeekDay', 'ExtractQuarter', 'ExtractSecond', 'ExtractWeek', 'ExtractWeekDay',
'ExtractYear', 'Now', 'Trunc', 'TruncDate', 'TruncDay', 'TruncHour', 'ExtractYear', 'Now', 'Trunc', 'TruncDate', 'TruncDay', 'TruncHour',
'TruncMinute', 'TruncMonth', 'TruncQuarter', 'TruncSecond', 'TruncTime', 'TruncMinute', 'TruncMonth', 'TruncQuarter', 'TruncSecond', 'TruncTime',
'TruncYear', 'TruncWeek', 'TruncYear',
# text # text
'Concat', 'ConcatPair', 'Length', 'Lower', 'Replace', 'StrIndex', 'Substr', 'Concat', 'ConcatPair', 'Length', 'Lower', 'Replace', 'StrIndex', 'Substr',
'Upper', 'Upper',
......
...@@ -199,7 +199,8 @@ class TruncBase(TimezoneMixin, Transform): ...@@ -199,7 +199,8 @@ class TruncBase(TimezoneMixin, Transform):
field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField' field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField'
)) ))
elif isinstance(field, TimeField) and ( 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. " % ( raise ValueError("Cannot truncate TimeField '%s' to %s. " % (
field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField' field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField'
)) ))
...@@ -242,6 +243,11 @@ class TruncMonth(TruncBase): ...@@ -242,6 +243,11 @@ class TruncMonth(TruncBase):
kind = 'month' kind = 'month'
class TruncWeek(TruncBase):
"""Truncate to midnight on the Monday of the week."""
kind = 'week'
class TruncDay(TruncBase): class TruncDay(TruncBase):
kind = 'day' kind = 'day'
......
...@@ -777,8 +777,8 @@ class QuerySet: ...@@ -777,8 +777,8 @@ class QuerySet:
Return a list of date objects representing all available dates for Return a list of date objects representing all available dates for
the given field_name, scoped to 'kind'. the given field_name, scoped to 'kind'.
""" """
assert kind in ("year", "month", "day"), \ assert kind in ('year', 'month', 'week', 'day'), \
"'kind' must be one of 'year', 'month' or 'day'." "'kind' must be one of 'year', 'month', 'week', or 'day'."
assert order in ('ASC', 'DESC'), \ assert order in ('ASC', 'DESC'), \
"'order' must be either 'ASC' or 'DESC'." "'order' must be either 'ASC' or 'DESC'."
return self.annotate( return self.annotate(
...@@ -793,8 +793,8 @@ class QuerySet: ...@@ -793,8 +793,8 @@ class QuerySet:
Return a list of datetime objects representing all available Return a list of datetime objects representing all available
datetimes for the given field_name, scoped to 'kind'. datetimes for the given field_name, scoped to 'kind'.
""" """
assert kind in ("year", "month", "day", "hour", "minute", "second"), \ assert kind in ('year', 'month', 'week', 'day', 'hour', 'minute', 'second'), \
"'kind' must be one of 'year', 'month', 'day', 'hour', 'minute' or 'second'." "'kind' must be one of 'year', 'month', 'week', 'day', 'hour', 'minute', or 'second'."
assert order in ('ASC', 'DESC'), \ assert order in ('ASC', 'DESC'), \
"'order' must be either 'ASC' or 'DESC'." "'order' must be either 'ASC' or 'DESC'."
if settings.USE_TZ: if settings.USE_TZ:
......
...@@ -439,6 +439,7 @@ return: ...@@ -439,6 +439,7 @@ return:
* "year": 2015-01-01 00:00:00+00:00 * "year": 2015-01-01 00:00:00+00:00
* "quarter": 2015-04-01 00:00:00+00:00 * "quarter": 2015-04-01 00:00:00+00:00
* "month": 2015-06-01 00:00:00+00:00 * "month": 2015-06-01 00:00:00+00:00
* "week": 2015-06-15 00:00:00+00:00
* "day": 2015-06-15 00:00:00+00:00 * "day": 2015-06-15 00:00:00+00:00
* "hour": 2015-06-15 14:00:00+00:00 * "hour": 2015-06-15 14:00:00+00:00
* "minute": 2015-06-15 14:30:00+00:00 * "minute": 2015-06-15 14:30:00+00:00
...@@ -452,6 +453,7 @@ values returned when this timezone is active will be: ...@@ -452,6 +453,7 @@ values returned when this timezone is active will be:
* "year": 2015-01-01 00:00:00+11:00 * "year": 2015-01-01 00:00:00+11:00
* "quarter": 2015-04-01 00:00:00+10:00 * "quarter": 2015-04-01 00:00:00+10:00
* "month": 2015-06-01 00:00:00+10:00 * "month": 2015-06-01 00:00:00+10:00
* "week": 2015-06-16 00:00:00+10:00
* "day": 2015-06-16 00:00:00+10:00 * "day": 2015-06-16 00:00:00+10:00
* "hour": 2015-06-16 00:00:00+10:00 * "hour": 2015-06-16 00:00:00+10:00
* "minute": 2015-06-16 00:30:00+10:00 * "minute": 2015-06-16 00:30:00+10:00
...@@ -504,6 +506,14 @@ Usage example:: ...@@ -504,6 +506,14 @@ Usage example::
.. attribute:: kind = 'month' .. attribute:: kind = 'month'
.. class:: TruncWeek(expression, output_field=None, tzinfo=None, **extra)
.. versionadded:: 2.1
Truncates to midnight on the Monday of the week.
.. attribute:: kind = 'week'
.. class:: TruncQuarter(expression, output_field=None, tzinfo=None, **extra) .. class:: TruncQuarter(expression, output_field=None, tzinfo=None, **extra)
.. versionadded:: 2.0 .. versionadded:: 2.0
......
...@@ -694,13 +694,15 @@ objects representing all available dates of a particular kind within the ...@@ -694,13 +694,15 @@ objects representing all available dates of a particular kind within the
contents of the ``QuerySet``. contents of the ``QuerySet``.
``field`` should be the name of a ``DateField`` of your model. ``field`` should be the name of a ``DateField`` of your model.
``kind`` should be either ``"year"``, ``"month"`` or ``"day"``. Each ``kind`` should be either ``"year"``, ``"month"``, ``"week"``, or ``"day"``.
``datetime.date`` object in the result list is "truncated" to the given Each :class:`datetime.date` object in the result list is "truncated" to the
``type``. given ``type``.
* ``"year"`` returns a list of all distinct year values for the field. * ``"year"`` returns a list of all distinct year values for the field.
* ``"month"`` returns a list of all distinct year/month values for the * ``"month"`` returns a list of all distinct year/month values for the
field. field.
* ``"week"`` returns a list of all distinct year/week values for the field. All
dates will be a Monday.
* ``"day"`` returns a list of all distinct year/month/day values for the * ``"day"`` returns a list of all distinct year/month/day values for the
field. field.
...@@ -713,6 +715,8 @@ Examples:: ...@@ -713,6 +715,8 @@ Examples::
[datetime.date(2005, 1, 1)] [datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month') >>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)] [datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'week')
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates('pub_date', 'day') >>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)] [datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC') >>> Entry.objects.dates('pub_date', 'day', order='DESC')
...@@ -720,6 +724,10 @@ Examples:: ...@@ -720,6 +724,10 @@ Examples::
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)] [datetime.date(2005, 3, 20)]
.. versionchanged:: 2.1
"week" support was added.
``datetimes()`` ``datetimes()``
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
...@@ -731,9 +739,9 @@ contents of the ``QuerySet``. ...@@ -731,9 +739,9 @@ contents of the ``QuerySet``.
``field_name`` should be the name of a ``DateTimeField`` of your model. ``field_name`` should be the name of a ``DateTimeField`` of your model.
``kind`` should be either ``"year"``, ``"month"``, ``"day"``, ``"hour"``, ``kind`` should be either ``"year"``, ``"month"``, ``"week"``, ``"day"``,
``"minute"`` or ``"second"``. Each ``datetime.datetime`` object in the result ``"hour"``, ``"minute"``, or ``"second"``. Each :class:`datetime.datetime`
list is "truncated" to the given ``type``. object in the result list is "truncated" to the given ``type``.
``order``, which defaults to ``'ASC'``, should be either ``'ASC'`` or ``order``, which defaults to ``'ASC'``, should be either ``'ASC'`` or
``'DESC'``. This specifies how to order the results. ``'DESC'``. This specifies how to order the results.
...@@ -745,6 +753,10 @@ object. If it's ``None``, Django uses the :ref:`current time zone ...@@ -745,6 +753,10 @@ object. If it's ``None``, Django uses the :ref:`current time zone
<default-current-time-zone>`. It has no effect when :setting:`USE_TZ` is <default-current-time-zone>`. It has no effect when :setting:`USE_TZ` is
``False``. ``False``.
.. versionchanged:: 2.1
"week" support was added.
.. _database-time-zone-definitions: .. _database-time-zone-definitions:
.. note:: .. note::
......
...@@ -173,6 +173,10 @@ Models ...@@ -173,6 +173,10 @@ Models
* The new :class:`~django.db.models.functions.Replace` database function * The new :class:`~django.db.models.functions.Replace` database function
replaces strings in an expression. replaces strings in an expression.
* The new :class:`~django.db.models.functions.TruncWeek` function truncates
:class:`~django.db.models.DateField` and
:class:`~django.db.models.DateTimeField` to the Monday of a week.
Requests and Responses Requests and Responses
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -55,6 +55,12 @@ class DatesTests(TestCase): ...@@ -55,6 +55,12 @@ class DatesTests(TestCase):
datetime.date(2010, 7, 1), datetime.date(2010, 7, 1),
], ],
) )
self.assertSequenceEqual(
Comment.objects.dates("article__pub_date", "week"), [
datetime.date(2005, 7, 25),
datetime.date(2010, 7, 26),
],
)
self.assertSequenceEqual( self.assertSequenceEqual(
Comment.objects.dates("article__pub_date", "day"), [ Comment.objects.dates("article__pub_date", "day"), [
datetime.date(2005, 7, 28), datetime.date(2005, 7, 28),
...@@ -93,7 +99,8 @@ class DatesTests(TestCase): ...@@ -93,7 +99,8 @@ class DatesTests(TestCase):
) )
def test_dates_fails_when_given_invalid_kind_argument(self): def test_dates_fails_when_given_invalid_kind_argument(self):
with self.assertRaisesMessage(AssertionError, "'kind' must be one of 'year', 'month' or 'day'."): msg = "'kind' must be one of 'year', 'month', 'week', or 'day'."
with self.assertRaisesMessage(AssertionError, msg):
Article.objects.dates("pub_date", "bad_kind") Article.objects.dates("pub_date", "bad_kind")
def test_dates_fails_when_given_invalid_order_argument(self): def test_dates_fails_when_given_invalid_order_argument(self):
......
...@@ -53,6 +53,12 @@ class DateTimesTests(TestCase): ...@@ -53,6 +53,12 @@ class DateTimesTests(TestCase):
datetime.datetime(2010, 7, 1), datetime.datetime(2010, 7, 1),
], ],
) )
self.assertSequenceEqual(
Comment.objects.datetimes("article__pub_date", "week"), [
datetime.datetime(2005, 7, 25),
datetime.datetime(2010, 7, 26),
],
)
self.assertSequenceEqual( self.assertSequenceEqual(
Comment.objects.datetimes("article__pub_date", "day"), [ Comment.objects.datetimes("article__pub_date", "day"), [
datetime.datetime(2005, 7, 28), datetime.datetime(2005, 7, 28),
...@@ -98,6 +104,9 @@ class DateTimesTests(TestCase): ...@@ -98,6 +104,9 @@ class DateTimesTests(TestCase):
self.assertQuerysetEqual( self.assertQuerysetEqual(
Article.objects.datetimes('pub_date', 'month'), Article.objects.datetimes('pub_date', 'month'),
["datetime.datetime(2005, 7, 1, 0, 0)"]) ["datetime.datetime(2005, 7, 1, 0, 0)"])
self.assertQuerysetEqual(
Article.objects.datetimes('pub_date', 'week'),
["datetime.datetime(2005, 7, 25, 0, 0)"])
self.assertQuerysetEqual( self.assertQuerysetEqual(
Article.objects.datetimes('pub_date', 'day'), Article.objects.datetimes('pub_date', 'day'),
["datetime.datetime(2005, 7, 28, 0, 0)", ["datetime.datetime(2005, 7, 28, 0, 0)",
......
from datetime import datetime from datetime import datetime, timedelta
import pytz import pytz
...@@ -8,7 +8,7 @@ from django.db.models.functions import ( ...@@ -8,7 +8,7 @@ from django.db.models.functions import (
Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth, Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,
ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay, ExtractYear, ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay, ExtractYear,
Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth, Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth,
TruncQuarter, TruncSecond, TruncTime, TruncYear, TruncQuarter, TruncSecond, TruncTime, TruncWeek, TruncYear,
) )
from django.test import ( from django.test import (
TestCase, override_settings, skipIfDBFeature, skipUnlessDBFeature, TestCase, override_settings, skipIfDBFeature, skipUnlessDBFeature,
...@@ -34,6 +34,10 @@ def truncate_to(value, kind, tzinfo=None): ...@@ -34,6 +34,10 @@ def truncate_to(value, kind, tzinfo=None):
if isinstance(value, datetime): if isinstance(value, datetime):
return value.replace(hour=0, minute=0, second=0, microsecond=0) return value.replace(hour=0, minute=0, second=0, microsecond=0)
return value return value
if kind == 'week':
if isinstance(value, datetime):
return (value - timedelta(days=value.weekday())).replace(hour=0, minute=0, second=0, microsecond=0)
return value - timedelta(days=value.weekday())
if kind == 'month': if kind == 'month':
if isinstance(value, datetime): if isinstance(value, datetime):
return value.replace(day=1, hour=0, minute=0, second=0, microsecond=0) return value.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
...@@ -536,6 +540,7 @@ class DateFunctionTests(TestCase): ...@@ -536,6 +540,7 @@ class DateFunctionTests(TestCase):
test_date_kind('year') test_date_kind('year')
test_date_kind('quarter') test_date_kind('quarter')
test_date_kind('month') test_date_kind('month')
test_date_kind('week')
test_date_kind('day') test_date_kind('day')
test_time_kind('hour') test_time_kind('hour')
test_time_kind('minute') test_time_kind('minute')
...@@ -543,6 +548,7 @@ class DateFunctionTests(TestCase): ...@@ -543,6 +548,7 @@ class DateFunctionTests(TestCase):
test_datetime_kind('year') test_datetime_kind('year')
test_datetime_kind('quarter') test_datetime_kind('quarter')
test_datetime_kind('month') test_datetime_kind('month')
test_datetime_kind('week')
test_datetime_kind('day') test_datetime_kind('day')
test_datetime_kind('hour') test_datetime_kind('hour')
test_datetime_kind('minute') test_datetime_kind('minute')
...@@ -656,6 +662,30 @@ class DateFunctionTests(TestCase): ...@@ -656,6 +662,30 @@ class DateFunctionTests(TestCase):
with self.assertRaisesMessage(ValueError, "Cannot truncate TimeField 'start_time' to DateTimeField"): with self.assertRaisesMessage(ValueError, "Cannot truncate TimeField 'start_time' to DateTimeField"):
list(DTModel.objects.annotate(truncated=TruncMonth('start_time', output_field=TimeField()))) list(DTModel.objects.annotate(truncated=TruncMonth('start_time', output_field=TimeField())))
def test_trunc_week_func(self):
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
end_datetime = truncate_to(datetime(2016, 6, 15, 14, 10, 50, 123), 'week')
if settings.USE_TZ:
start_datetime = timezone.make_aware(start_datetime, is_dst=False)
end_datetime = timezone.make_aware(end_datetime, is_dst=False)
self.create_model(start_datetime, end_datetime)
self.create_model(end_datetime, start_datetime)
self.assertQuerysetEqual(
DTModel.objects.annotate(extracted=TruncWeek('start_datetime')).order_by('start_datetime'),
[
(start_datetime, truncate_to(start_datetime, 'week')),
(end_datetime, truncate_to(end_datetime, 'week')),
],
lambda m: (m.start_datetime, m.extracted)
)
self.assertEqual(DTModel.objects.filter(start_datetime=TruncWeek('start_datetime')).count(), 1)
with self.assertRaisesMessage(ValueError, "Cannot truncate TimeField 'start_time' to DateTimeField"):
list(DTModel.objects.annotate(truncated=TruncWeek('start_time')))
with self.assertRaisesMessage(ValueError, "Cannot truncate TimeField 'start_time' to DateTimeField"):
list(DTModel.objects.annotate(truncated=TruncWeek('start_time', output_field=TimeField())))
def test_trunc_date_func(self): def test_trunc_date_func(self):
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321) start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123) end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
...@@ -960,6 +990,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests): ...@@ -960,6 +990,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
test_date_kind('year') test_date_kind('year')
test_date_kind('quarter') test_date_kind('quarter')
test_date_kind('month') test_date_kind('month')
test_date_kind('week')
test_date_kind('day') test_date_kind('day')
test_time_kind('hour') test_time_kind('hour')
test_time_kind('minute') test_time_kind('minute')
...@@ -967,6 +998,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests): ...@@ -967,6 +998,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
test_datetime_kind('year') test_datetime_kind('year')
test_datetime_kind('quarter') test_datetime_kind('quarter')
test_datetime_kind('month') test_datetime_kind('month')
test_datetime_kind('week')
test_datetime_kind('day') test_datetime_kind('day')
test_datetime_kind('hour') test_datetime_kind('hour')
test_datetime_kind('minute') test_datetime_kind('minute')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment