Kaynağa Gözat

Ensure Oscar always displays timezone-aware datetimes

In a few cases, django.templatefilters.date was used to format a
datetime as string. This filters expects localtime, but was always
passed time in UTC.
Introduced a helper function that should be used instead of the template
filter.
master
Maik Hoepfel 12 yıl önce
ebeveyn
işleme
325eee6830

+ 7
- 7
oscar/apps/dashboard/orders/views.py Dosyayı Görüntüle

@@ -7,14 +7,14 @@ from django.core.urlresolvers import reverse
7 7
 from django.core.exceptions import ObjectDoesNotExist
8 8
 from django.db.models.loading import get_model
9 9
 from django.db.models import fields, Q, Sum, Count
10
-from django.http import HttpResponse, HttpResponseRedirect, Http404
10
+from django.http import HttpResponse, HttpResponseRedirect
11 11
 from django.shortcuts import get_object_or_404
12
-from django.template.defaultfilters import date as format_date
13 12
 from django.utils.datastructures import SortedDict
14 13
 from django.views.generic import ListView, DetailView, UpdateView, FormView
15 14
 from django.conf import settings
16 15
 
17 16
 from oscar.core.loading import get_class
17
+from oscar.core.utils import format_datetime
18 18
 from oscar.apps.dashboard.orders import forms
19 19
 from oscar.views.generic import BulkEditMixin
20 20
 from oscar.apps.dashboard.reports.csv_utils import CsvUnicodeWriter
@@ -131,13 +131,13 @@ class OrderListView(ListView, BulkEditMixin):
131 131
 
132 132
         if data['date_from'] and data['date_to']:
133 133
             desc_ctx['date_filter'] = _(" placed between %(start_date)s and %(end_date)s") % {
134
-                'start_date': format_date(data['date_from']),
135
-                'end_date': format_date(data['date_to'])}
134
+                'start_date': format_datetime(data['date_from']),
135
+                'end_date': format_datetime(data['date_to'])}
136 136
         elif data['date_from']:
137
-            desc_ctx['date_filter'] = _(" placed since %s") % format_date(data['date_from'])
137
+            desc_ctx['date_filter'] = _(" placed since %s") % format_datetime(data['date_from'])
138 138
         elif data['date_to']:
139 139
             date_to = data['date_to'] + datetime.timedelta(days=1)
140
-            desc_ctx['date_filter'] = _(" placed before %s") % format_date(data['date_to'])
140
+            desc_ctx['date_filter'] = _(" placed before %s") % format_datetime(data['date_to'])
141 141
 
142 142
         if data['voucher']:
143 143
             desc_ctx['voucher_filter'] = _(" using voucher '%s'") % data['voucher']
@@ -286,7 +286,7 @@ class OrderListView(ListView, BulkEditMixin):
286 286
             row = columns.copy()
287 287
             row['number'] = order.number
288 288
             row['value'] = order.total_incl_tax
289
-            row['date'] = format_date(order.date_placed, 'DATETIME_FORMAT')
289
+            row['date'] = format_datetime(order.date_placed, 'DATETIME_FORMAT')
290 290
             row['num_items'] = order.num_items
291 291
             row['status'] = order.status
292 292
             row['customer'] = order.email

+ 4
- 3
oscar/apps/dashboard/reports/reports.py Dosyayı Görüntüle

@@ -1,7 +1,8 @@
1
-from django.template.defaultfilters import date
2 1
 from django.http import HttpResponse
3 2
 from django.utils.translation import ugettext_lazy as _
3
+
4 4
 from oscar.apps.dashboard.reports.csv_utils import CsvUnicodeWriter
5
+from oscar.core import utils
5 6
 
6 7
 
7 8
 class ReportGenerator(object):
@@ -46,10 +47,10 @@ class ReportGenerator(object):
46 47
 
47 48
 class ReportFormatter(object):
48 49
     def format_datetime(self, dt):
49
-        return date(dt, 'DATETIME_FORMAT')
50
+        return utils.format_datetime(dt, 'DATETIME_FORMAT')
50 51
 
51 52
     def format_date(self, d):
52
-        return date(d, 'DATE_FORMAT')
53
+        return utils.format_datetime(d, 'DATE_FORMAT')
53 54
 
54 55
     def filename(self):
55 56
         return self.filename_template

+ 5
- 5
oscar/apps/dashboard/reviews/views.py Dosyayı Görüntüle

@@ -5,10 +5,10 @@ from django.db.models import get_model, Q
5 5
 from django.utils.translation import ugettext_lazy as _
6 6
 from django.core.urlresolvers import reverse
7 7
 from django.http import HttpResponseRedirect
8
-from django.template.defaultfilters import date as format_date
9 8
 
10 9
 from oscar.views.generic import BulkEditMixin
11 10
 from oscar.apps.dashboard.reviews import forms
11
+from oscar.core.utils import format_datetime
12 12
 
13 13
 ProductReview = get_model('reviews', 'productreview')
14 14
 
@@ -52,17 +52,17 @@ class ReviewListView(generic.ListView, BulkEditMixin):
52 52
                 date_created__lt=date_to
53 53
             )
54 54
             self.desc_ctx['date_filter'] = _(" created between %(start_date)s and %(end_date)s") % {
55
-                'start_date': format_date(date_from),
56
-                'end_date': format_date(date_to)
55
+                'start_date': format_datetime(date_from),
56
+                'end_date': format_datetime(date_to)
57 57
             }
58 58
         elif date_from:
59 59
             queryset = queryset.filter(date_created__gte=date_from)
60
-            self.desc_ctx['date_filter'] =  _(" created after %s") % format_date(date_from)
60
+            self.desc_ctx['date_filter'] =  _(" created after %s") % format_datetime(date_from)
61 61
         elif date_to:
62 62
             # Add 24 hours to make search inclusive
63 63
             date_to = date_to + datetime.timedelta(days=1)
64 64
             queryset = queryset.filter(date_created__lt=date_to)
65
-            self.desc_ctx['date_filter'] = _(" created before %s") % format_date(date_to)
65
+            self.desc_ctx['date_filter'] = _(" created before %s") % format_datetime(date_to)
66 66
 
67 67
         return queryset
68 68
 

+ 11
- 10
oscar/apps/offer/models.py Dosyayı Görüntüle

@@ -2,9 +2,9 @@ from decimal import Decimal as D, ROUND_DOWN, ROUND_UP
2 2
 
3 3
 from django.core import exceptions
4 4
 from django.db.models import get_model
5
-from django.template.defaultfilters import date
5
+from django.template.defaultfilters import date as date_filter
6 6
 from django.db import models
7
-from django.utils.timezone import now
7
+from django.utils.timezone import now, get_current_timezone
8 8
 from django.utils.translation import ungettext, ugettext as _
9 9
 from django.utils.importlib import import_module
10 10
 from django.core.exceptions import ValidationError
@@ -332,26 +332,27 @@ class ConditionalOffer(models.Model):
332 332
                 'description': desc,
333 333
                 'is_satisfied': True})
334 334
 
335
-        def format_datetime(dt):
335
+        def hide_time_if_zero(dt):
336 336
             # Only show hours/minutes if they have been specified
337
-            if dt.hour == 0 and dt.minute == 0:
338
-                return date(dt, settings.DATE_FORMAT)
339
-            return date(dt, settings.DATETIME_FORMAT)
337
+            localtime = dt.astimezone(get_current_timezone())
338
+            if localtime.hour == 0 and localtime.minute == 0:
339
+                return date_filter(localtime, settings.DATE_FORMAT)
340
+            return date_filter(localtime, settings.DATETIME_FORMAT)
340 341
 
341 342
         if self.start_datetime or self.end_datetime:
342 343
             today = now()
343 344
             if self.start_datetime and self.end_datetime:
344 345
                 desc = _("Available between %(start)s and %(end)s") % {
345
-                        'start': format_datetime(self.start_datetime),
346
-                        'end': format_datetime(self.end_datetime)}
346
+                        'start': hide_time_if_zero(self.start_datetime),
347
+                        'end': hide_time_if_zero(self.end_datetime)}
347 348
                 is_satisfied = self.start_datetime <= today <= self.end_datetime
348 349
             elif self.start_datetime:
349 350
                 desc = _("Available from %(start)s") % {
350
-                    'start': format_datetime(self.start_datetime)}
351
+                    'start': hide_time_if_zero(self.start_datetime)}
351 352
                 is_satisfied = today >= self.start_datetime
352 353
             elif self.end_datetime:
353 354
                 desc = _("Available until %(end)s") % {
354
-                    'end': format_datetime(self.end_datetime)}
355
+                    'end': hide_time_if_zero(self.end_datetime)}
355 356
                 is_satisfied = today <= self.end_datetime
356 357
             restrictions.append({
357 358
                 'description': desc,

+ 26
- 0
oscar/core/utils.py Dosyayı Görüntüle

@@ -1,5 +1,10 @@
1
+from __future__ import absolute_import  # for import below
2
+import logging
3
+
4
+from django.utils.timezone import get_current_timezone, is_naive, make_aware
1 5
 from unidecode import unidecode
2 6
 from django.conf import settings
7
+from django.template.defaultfilters import date as date_filter
3 8
 
4 9
 
5 10
 def slugify(value):
@@ -48,3 +53,24 @@ def compose(*functions):
48 53
                 args = fn(args)
49 54
         return args
50 55
     return _composed
56
+
57
+
58
+
59
+def format_datetime(dt, format=None):
60
+    """
61
+    Takes an instance of datetime, converts it to the current timezone and
62
+    formats it as a string. Use this instead of
63
+    django.core.templatefilters.date, which expects localtime.
64
+
65
+    :param format: Common will be settings.DATETIME_FORMAT or
66
+                   settings.DATE_FORMAT, or the resp. shorthands
67
+                   ('DATETIME_FORMAT', 'DATE_FORMAT')
68
+    """
69
+    if is_naive(dt):
70
+        localtime = make_aware(dt, get_current_timezone())
71
+        logging.warning(
72
+            "oscar.core.utils.format_datetime received native datetime")
73
+    else:
74
+        localtime = dt.astimezone(get_current_timezone())
75
+    return date_filter(localtime, format)
76
+

Loading…
İptal
Kaydet