summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/admin/templatetags/admin_list.py23
-rw-r--r--tests/admin_views/tests.py12
2 files changed, 30 insertions, 5 deletions
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 4fd9c45a57..d686764ace 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -2,7 +2,8 @@ import datetime
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.admin.utils import (
- display_for_field, display_for_value, label_for_field, lookup_field,
+ display_for_field, display_for_value, get_fields_from_path,
+ label_for_field, lookup_field,
)
from django.contrib.admin.views.main import (
ALL_VAR, ORDER_VAR, PAGE_VAR, SEARCH_VAR,
@@ -13,7 +14,7 @@ from django.template import Library
from django.template.loader import get_template
from django.templatetags.static import static
from django.urls import NoReverseMatch
-from django.utils import formats
+from django.utils import formats, timezone
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
@@ -359,6 +360,13 @@ def date_hierarchy(cl):
"""
if cl.date_hierarchy:
field_name = cl.date_hierarchy
+ field = get_fields_from_path(cl.model, field_name)[-1]
+ if isinstance(field, models.DateTimeField):
+ dates_or_datetimes = 'datetimes'
+ qs_kwargs = {'is_dst': True}
+ else:
+ dates_or_datetimes = 'dates'
+ qs_kwargs = {}
year_field = '%s__year' % field_name
month_field = '%s__month' % field_name
day_field = '%s__day' % field_name
@@ -374,6 +382,11 @@ def date_hierarchy(cl):
# select appropriate start level
date_range = cl.queryset.aggregate(first=models.Min(field_name),
last=models.Max(field_name))
+ if dates_or_datetimes == 'datetimes':
+ date_range = {
+ k: timezone.localtime(v) if timezone.is_aware(v) else v
+ for k, v in date_range.items()
+ }
if date_range['first'] and date_range['last']:
if date_range['first'].year == date_range['last'].year:
year_lookup = date_range['first'].year
@@ -391,7 +404,7 @@ def date_hierarchy(cl):
'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}]
}
elif year_lookup and month_lookup:
- days = getattr(cl.queryset, 'dates')(field_name, 'day')
+ days = getattr(cl.queryset, dates_or_datetimes)(field_name, 'day', **qs_kwargs)
return {
'show': True,
'back': {
@@ -404,7 +417,7 @@ def date_hierarchy(cl):
} for day in days]
}
elif year_lookup:
- months = getattr(cl.queryset, 'dates')(field_name, 'month')
+ months = getattr(cl.queryset, dates_or_datetimes)(field_name, 'month', **qs_kwargs)
return {
'show': True,
'back': {
@@ -417,7 +430,7 @@ def date_hierarchy(cl):
} for month in months]
}
else:
- years = getattr(cl.queryset, 'dates')(field_name, 'year')
+ years = getattr(cl.queryset, dates_or_datetimes)(field_name, 'year', **qs_kwargs)
return {
'show': True,
'back': None,
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index c2b3ff0ff1..8fde8ed45c 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -981,6 +981,18 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
self.assertContains(response, 'question__expires__month=10')
self.assertContains(response, 'question__expires__year=2016')
+ @override_settings(TIME_ZONE='America/Los_Angeles', USE_TZ=True)
+ def test_date_hierarchy_local_date_differ_from_utc(self):
+ # This datetime is 2017-01-01 in UTC.
+ date = pytz.timezone('America/Los_Angeles').localize(datetime.datetime(2016, 12, 31, 16))
+ q = Question.objects.create(question='Why?', expires=date)
+ Answer2.objects.create(question=q, answer='Because.')
+ response = self.client.get(reverse('admin:admin_views_answer2_changelist'))
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'question__expires__day=31')
+ self.assertContains(response, 'question__expires__month=12')
+ self.assertContains(response, 'question__expires__year=2016')
+
def test_sortable_by_columns_subset(self):
expected_sortable_fields = ('date', 'callable_year')
expected_not_sortable_fields = (