Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #17992 -- Added a public API for localtime.

Thanks Bradley Ayers for the report.
  • Loading branch information...
commit 3e8b40f479a02e0f8c40ef3c7dae740082478b89 1 parent 5aa51fa
Aymeric Augustin aaugustin authored
10 django/contrib/admin/filters.py
View
@@ -290,19 +290,15 @@ def __init__(self, field, request, params, model, model_admin, field_path):
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 timezone.is_aware(now):
+ now = timezone.localtime(now)
if isinstance(field, models.DateTimeField):
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 = (
4 django/contrib/admin/util.py
View
@@ -325,7 +325,7 @@ def display_for_field(value, field):
elif value is None:
return EMPTY_CHANGELIST_VALUE
elif isinstance(field, models.DateTimeField):
- return formats.localize(timezone.localtime(value))
+ return formats.localize(timezone.template_localtime(value))
elif isinstance(field, (models.DateField, models.TimeField)):
return formats.localize(value)
elif isinstance(field, models.DecimalField):
@@ -345,7 +345,7 @@ def display_for_value(value, boolean=False):
elif value is None:
return EMPTY_CHANGELIST_VALUE
elif isinstance(value, datetime.datetime):
- return formats.localize(timezone.localtime(value))
+ return formats.localize(timezone.template_localtime(value))
elif isinstance(value, (datetime.date, datetime.time)):
return formats.localize(value)
elif isinstance(value, (decimal.Decimal, float, int, long)):
6 django/template/base.py
View
@@ -18,7 +18,7 @@
from django.utils.formats import localize
from django.utils.html import escape
from django.utils.module_loading import module_has_submodule
-from django.utils.timezone import localtime
+from django.utils.timezone import template_localtime
TOKEN_TEXT = 0
@@ -592,7 +592,7 @@ def resolve(self, context, ignore_failures=False):
else:
arg_vals.append(arg.resolve(context))
if getattr(func, 'expects_localtime', False):
- obj = localtime(obj, context.use_tz)
+ obj = template_localtime(obj, context.use_tz)
if getattr(func, 'needs_autoescape', False):
new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
else:
@@ -853,7 +853,7 @@ def _render_value_in_context(value, context):
means escaping, if required, and conversion to a unicode object. If value
is a string, it is expected to have already been translated.
"""
- value = localtime(value, use_tz=context.use_tz)
+ value = template_localtime(value, use_tz=context.use_tz)
value = localize(value, use_l10n=context.use_l10n)
value = force_unicode(value)
if ((context.autoescape and not isinstance(value, SafeData)) or
4 django/template/debug.py
View
@@ -3,7 +3,7 @@
from django.utils.html import escape
from django.utils.safestring import SafeData, EscapeData
from django.utils.formats import localize
-from django.utils.timezone import localtime
+from django.utils.timezone import template_localtime
class DebugLexer(Lexer):
@@ -82,7 +82,7 @@ class DebugVariableNode(VariableNode):
def render(self, context):
try:
output = self.filter_expression.resolve(context)
- output = localtime(output, use_tz=context.use_tz)
+ output = template_localtime(output, use_tz=context.use_tz)
output = localize(output, use_l10n=context.use_l10n)
output = force_unicode(output)
except UnicodeDecodeError:
6 django/templatetags/tz.py
View
@@ -72,11 +72,7 @@ def do_timezone(value, arg):
else:
return ''
- # Convert and prevent further conversion
- result = value.astimezone(tz)
- if hasattr(tz, 'normalize'):
- # available for pytz time zones
- result = tz.normalize(result)
+ result = timezone.localtime(value, tz)
# HACK: the convert_to_local_time flag will prevent
# automatic conversion of the value to local time.
28 django/utils/timezone.py
View
@@ -206,7 +206,7 @@ def __exit__(self, exc_type, exc_value, traceback):
# Templates
-def localtime(value, use_tz=None):
+def template_localtime(value, use_tz=None):
"""
Checks if value is a datetime and converts it to local time if necessary.
@@ -215,20 +215,30 @@ def localtime(value, use_tz=None):
This function is designed for use by the template engine.
"""
- if (isinstance(value, datetime)
+ should_convert = (isinstance(value, datetime)
and (settings.USE_TZ if use_tz is None else use_tz)
and not is_naive(value)
- and getattr(value, 'convert_to_local_time', True)):
- timezone = get_current_timezone()
- value = value.astimezone(timezone)
- if hasattr(timezone, 'normalize'):
- # available for pytz time zones
- value = timezone.normalize(value)
- return value
+ and getattr(value, 'convert_to_local_time', True))
+ return localtime(value) if should_convert else value
# Utilities
+def localtime(value, timezone=None):
+ """
+ Converts an aware datetime.datetime to local time.
+
+ Local time is defined by the current time zone, unless another time zone
+ is specified.
+ """
+ if timezone is None:
+ timezone = get_current_timezone()
+ value = value.astimezone(timezone)
+ if hasattr(timezone, 'normalize'):
+ # available for pytz time zones
+ value = timezone.normalize(value)
+ return value
+
def now():
"""
Returns an aware or naive datetime.datetime, depending on settings.USE_TZ.
10 docs/ref/utils.txt
View
@@ -666,6 +666,16 @@ For a complete discussion on the usage of the following see the
``None``, the :ref:`current time zone <default-current-time-zone>` is unset
on entry with :func:`deactivate()` instead.
+.. versionadded:: 1.5
+
+.. function:: localtime(value, timezone=None)
+
+ Converts an aware :class:`~datetime.datetime` to a different time zone,
+ by default the :ref:`current time zone <default-current-time-zone>`.
+
+ This function doesn't work on naive datetimes; use :func:`make_aware`
+ instead.
+
.. function:: now()
Returns an aware or naive :class:`~datetime.datetime` that represents the
3  docs/releases/1.5.txt
View
@@ -41,6 +41,9 @@ Django 1.5 also includes several smaller improvements worth noting:
* The template engine now interprets ``True``, ``False`` and ``None`` as the
corresponding Python objects.
+* :mod:`django.utils.timezone` provides a helper for converting aware
+ datetimes between time zones, see :func:`~django.utils.timezone.localtime`.
+
Backwards incompatible changes in 1.5
=====================================
29 tests/regressiontests/utils/timezone.py
View
@@ -1,18 +1,33 @@
import copy
+import datetime
import pickle
-from django.utils.timezone import UTC, LocalTimezone
+from django.test.utils import override_settings
+from django.utils import timezone
from django.utils import unittest
+
class TimezoneTests(unittest.TestCase):
+ def test_localtime(self):
+ now = datetime.datetime.utcnow().replace(tzinfo=timezone.utc)
+ local_tz = timezone.LocalTimezone()
+ local_now = timezone.localtime(now, local_tz)
+ self.assertEqual(local_now.tzinfo, local_tz)
+
+ def test_now(self):
+ with override_settings(USE_TZ=True):
+ self.assertTrue(timezone.is_aware(timezone.now()))
+ with override_settings(USE_TZ=False):
+ self.assertTrue(timezone.is_naive(timezone.now()))
+
def test_copy(self):
- self.assertIsInstance(copy.copy(UTC()), UTC)
- self.assertIsInstance(copy.copy(LocalTimezone()), LocalTimezone)
+ self.assertIsInstance(copy.copy(timezone.UTC()), timezone.UTC)
+ self.assertIsInstance(copy.copy(timezone.LocalTimezone()), timezone.LocalTimezone)
def test_deepcopy(self):
- self.assertIsInstance(copy.deepcopy(UTC()), UTC)
- self.assertIsInstance(copy.deepcopy(LocalTimezone()), LocalTimezone)
+ self.assertIsInstance(copy.deepcopy(timezone.UTC()), timezone.UTC)
+ self.assertIsInstance(copy.deepcopy(timezone.LocalTimezone()), timezone.LocalTimezone)
def test_pickling_unpickling(self):
- self.assertIsInstance(pickle.loads(pickle.dumps(UTC())), UTC)
- self.assertIsInstance(pickle.loads(pickle.dumps(LocalTimezone())), LocalTimezone)
+ self.assertIsInstance(pickle.loads(pickle.dumps(timezone.UTC())), timezone.UTC)
+ self.assertIsInstance(pickle.loads(pickle.dumps(timezone.LocalTimezone())), timezone.LocalTimezone)
Please sign in to comment.
Something went wrong with that request. Please try again.