diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py index 550683bdd9..b64445ebfd 100644 --- a/django/contrib/admin/filters.py +++ b/django/contrib/admin/filters.py @@ -11,6 +11,7 @@ from django.db import models from django.core.exceptions import ImproperlyConfigured from django.utils.encoding import smart_unicode from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone from django.contrib.admin.util import (get_model_from_relation, reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value) @@ -282,44 +283,49 @@ class DateFieldListFilter(FieldListFilter): self.field_generic = '%s__' % field_path self.date_params = dict([(k, v) for k, v in params.items() if k.startswith(self.field_generic)]) - today = datetime.date.today() - one_week_ago = today - datetime.timedelta(days=7) - today_str = str(today) + + now = timezone.now() + # When time zone support is enabled, convert "now" to the user's time + # zone so Django's definition of "Today" matches what the user expects. + if now.tzinfo is not None: + current_tz = timezone.get_current_timezone() + now = now.astimezone(current_tz) + if hasattr(current_tz, 'normalize'): + # available for pytz time zones + now = current_tz.normalize(now) + if isinstance(field, models.DateTimeField): - today_str += ' 23:59:59' - self.lookup_kwarg_year = '%s__year' % field_path - self.lookup_kwarg_month = '%s__month' % field_path - self.lookup_kwarg_day = '%s__day' % field_path - self.lookup_kwarg_past_7_days_gte = '%s__gte' % field_path - self.lookup_kwarg_past_7_days_lte = '%s__lte' % field_path + today = now.replace(hour=0, minute=0, second=0, microsecond=0) + else: # field is a models.DateField + today = now.date() + tomorrow = today + datetime.timedelta(days=1) + + self.lookup_kwarg_since = '%s__gte' % field_path + self.lookup_kwarg_until = '%s__lt' % field_path self.links = ( (_('Any date'), {}), (_('Today'), { - self.lookup_kwarg_year: str(today.year), - self.lookup_kwarg_month: str(today.month), - self.lookup_kwarg_day: str(today.day), + self.lookup_kwarg_since: str(today), + self.lookup_kwarg_until: str(tomorrow), }), (_('Past 7 days'), { - self.lookup_kwarg_past_7_days_gte: str(one_week_ago), - self.lookup_kwarg_past_7_days_lte: today_str, + self.lookup_kwarg_since: str(today - datetime.timedelta(days=7)), + self.lookup_kwarg_until: str(tomorrow), }), (_('This month'), { - self.lookup_kwarg_year: str(today.year), - self.lookup_kwarg_month: str(today.month), + self.lookup_kwarg_since: str(today.replace(day=1)), + self.lookup_kwarg_until: str(tomorrow), }), (_('This year'), { - self.lookup_kwarg_year: str(today.year), + self.lookup_kwarg_since: str(today.replace(month=1, day=1)), + self.lookup_kwarg_until: str(tomorrow), }), ) super(DateFieldListFilter, self).__init__( field, request, params, model, model_admin, field_path) def expected_parameters(self): - return [ - self.lookup_kwarg_year, self.lookup_kwarg_month, - self.lookup_kwarg_day, self.lookup_kwarg_past_7_days_gte, - self.lookup_kwarg_past_7_days_lte - ] + return [self.lookup_kwarg_since, self.lookup_kwarg_until] def choices(self, cl): for title, param_dict in self.links: diff --git a/tests/regressiontests/admin_filters/tests.py b/tests/regressiontests/admin_filters/tests.py index 4988e57e61..539bb0cc7a 100644 --- a/tests/regressiontests/admin_filters/tests.py +++ b/tests/regressiontests/admin_filters/tests.py @@ -10,6 +10,7 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.core.exceptions import ImproperlyConfigured from django.test import TestCase, RequestFactory +from django.test.utils import override_settings from django.utils.encoding import force_unicode from .models import Book @@ -115,6 +116,7 @@ class ListFiltersTests(TestCase): def setUp(self): self.today = datetime.date.today() + self.tomorrow = self.today + datetime.timedelta(days=1) self.one_week_ago = self.today - datetime.timedelta(days=7) self.request_factory = RequestFactory() @@ -143,9 +145,8 @@ class ListFiltersTests(TestCase): request = self.request_factory.get('/') changelist = self.get_changelist(request, Book, modeladmin) - request = self.request_factory.get('/', {'date_registered__year': self.today.year, - 'date_registered__month': self.today.month, - 'date_registered__day': self.today.day}) + request = self.request_factory.get('/', {'date_registered__gte': self.today, + 'date_registered__lt': self.tomorrow}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned @@ -157,13 +158,12 @@ class ListFiltersTests(TestCase): self.assertEqual(force_unicode(filterspec.title), u'date registered') choice = select_by(filterspec.choices(changelist), "display", "Today") self.assertEqual(choice['selected'], True) - self.assertEqual(choice['query_string'], '?date_registered__day=%s' - '&date_registered__month=%s' - '&date_registered__year=%s' - % (self.today.day, self.today.month, self.today.year)) + self.assertEqual(choice['query_string'], '?date_registered__gte=%s' + '&date_registered__lt=%s' + % (self.today, self.tomorrow)) - request = self.request_factory.get('/', {'date_registered__year': self.today.year, - 'date_registered__month': self.today.month}) + request = self.request_factory.get('/', {'date_registered__gte': self.today.replace(day=1), + 'date_registered__lt': self.tomorrow}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned @@ -179,11 +179,12 @@ class ListFiltersTests(TestCase): self.assertEqual(force_unicode(filterspec.title), u'date registered') choice = select_by(filterspec.choices(changelist), "display", "This month") self.assertEqual(choice['selected'], True) - self.assertEqual(choice['query_string'], '?date_registered__month=%s' - '&date_registered__year=%s' - % (self.today.month, self.today.year)) + self.assertEqual(choice['query_string'], '?date_registered__gte=%s' + '&date_registered__lt=%s' + % (self.today.replace(day=1), self.tomorrow)) - request = self.request_factory.get('/', {'date_registered__year': self.today.year}) + request = self.request_factory.get('/', {'date_registered__gte': self.today.replace(month=1, day=1), + 'date_registered__lt': self.tomorrow}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned @@ -199,11 +200,12 @@ class ListFiltersTests(TestCase): self.assertEqual(force_unicode(filterspec.title), u'date registered') choice = select_by(filterspec.choices(changelist), "display", "This year") self.assertEqual(choice['selected'], True) - self.assertEqual(choice['query_string'], '?date_registered__year=%s' - % (self.today.year)) + self.assertEqual(choice['query_string'], '?date_registered__gte=%s' + '&date_registered__lt=%s' + % (self.today.replace(month=1, day=1), self.tomorrow)) request = self.request_factory.get('/', {'date_registered__gte': str(self.one_week_ago), - 'date_registered__lte': str(self.today)}) + 'date_registered__lt': str(self.tomorrow)}) changelist = self.get_changelist(request, Book, modeladmin) # Make sure the correct queryset is returned @@ -216,8 +218,13 @@ class ListFiltersTests(TestCase): choice = select_by(filterspec.choices(changelist), "display", "Past 7 days") self.assertEqual(choice['selected'], True) self.assertEqual(choice['query_string'], '?date_registered__gte=%s' - '&date_registered__lte=%s' - % (str(self.one_week_ago), str(self.today))) + '&date_registered__lt=%s' + % (str(self.one_week_ago), str(self.tomorrow))) + + @override_settings(USE_TZ=True) + def test_datefieldlistfilter_with_time_zone_support(self): + # Regression for #17830 + self.test_datefieldlistfilter() def test_allvaluesfieldlistfilter(self): modeladmin = BookAdmin(Book, site)