Explorar el Código

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 hace 12 años
padre
commit
325eee6830

+ 7
- 7
oscar/apps/dashboard/orders/views.py Ver fichero

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

+ 4
- 3
oscar/apps/dashboard/reports/reports.py Ver fichero

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

+ 5
- 5
oscar/apps/dashboard/reviews/views.py Ver fichero

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

+ 11
- 10
oscar/apps/offer/models.py Ver fichero

2
 
2
 
3
 from django.core import exceptions
3
 from django.core import exceptions
4
 from django.db.models import get_model
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
 from django.db import models
6
 from django.db import models
7
-from django.utils.timezone import now
7
+from django.utils.timezone import now, get_current_timezone
8
 from django.utils.translation import ungettext, ugettext as _
8
 from django.utils.translation import ungettext, ugettext as _
9
 from django.utils.importlib import import_module
9
 from django.utils.importlib import import_module
10
 from django.core.exceptions import ValidationError
10
 from django.core.exceptions import ValidationError
332
                 'description': desc,
332
                 'description': desc,
333
                 'is_satisfied': True})
333
                 'is_satisfied': True})
334
 
334
 
335
-        def format_datetime(dt):
335
+        def hide_time_if_zero(dt):
336
             # Only show hours/minutes if they have been specified
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
         if self.start_datetime or self.end_datetime:
342
         if self.start_datetime or self.end_datetime:
342
             today = now()
343
             today = now()
343
             if self.start_datetime and self.end_datetime:
344
             if self.start_datetime and self.end_datetime:
344
                 desc = _("Available between %(start)s and %(end)s") % {
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
                 is_satisfied = self.start_datetime <= today <= self.end_datetime
348
                 is_satisfied = self.start_datetime <= today <= self.end_datetime
348
             elif self.start_datetime:
349
             elif self.start_datetime:
349
                 desc = _("Available from %(start)s") % {
350
                 desc = _("Available from %(start)s") % {
350
-                    'start': format_datetime(self.start_datetime)}
351
+                    'start': hide_time_if_zero(self.start_datetime)}
351
                 is_satisfied = today >= self.start_datetime
352
                 is_satisfied = today >= self.start_datetime
352
             elif self.end_datetime:
353
             elif self.end_datetime:
353
                 desc = _("Available until %(end)s") % {
354
                 desc = _("Available until %(end)s") % {
354
-                    'end': format_datetime(self.end_datetime)}
355
+                    'end': hide_time_if_zero(self.end_datetime)}
355
                 is_satisfied = today <= self.end_datetime
356
                 is_satisfied = today <= self.end_datetime
356
             restrictions.append({
357
             restrictions.append({
357
                 'description': desc,
358
                 'description': desc,

+ 26
- 0
oscar/core/utils.py Ver fichero

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
 from unidecode import unidecode
5
 from unidecode import unidecode
2
 from django.conf import settings
6
 from django.conf import settings
7
+from django.template.defaultfilters import date as date_filter
3
 
8
 
4
 
9
 
5
 def slugify(value):
10
 def slugify(value):
48
                 args = fn(args)
53
                 args = fn(args)
49
         return args
54
         return args
50
     return _composed
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…
Cancelar
Guardar