mirror of https://github.com/django/django.git
Fixed #17992 -- Added a public API for localtime.
Thanks Bradley Ayers for the report.
This commit is contained in:
parent
5aa51fa999
commit
3e8b40f479
|
@ -290,19 +290,15 @@ class DateFieldListFilter(FieldListFilter):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
# When time zone support is enabled, convert "now" to the user's time
|
# 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.
|
# zone so Django's definition of "Today" matches what the user expects.
|
||||||
if now.tzinfo is not None:
|
if timezone.is_aware(now):
|
||||||
current_tz = timezone.get_current_timezone()
|
now = timezone.localtime(now)
|
||||||
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):
|
if isinstance(field, models.DateTimeField):
|
||||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
else: # field is a models.DateField
|
else: # field is a models.DateField
|
||||||
today = now.date()
|
today = now.date()
|
||||||
tomorrow = today + datetime.timedelta(days=1)
|
tomorrow = today + datetime.timedelta(days=1)
|
||||||
|
|
||||||
self.lookup_kwarg_since = '%s__gte' % field_path
|
self.lookup_kwarg_since = '%s__gte' % field_path
|
||||||
self.lookup_kwarg_until = '%s__lt' % field_path
|
self.lookup_kwarg_until = '%s__lt' % field_path
|
||||||
self.links = (
|
self.links = (
|
||||||
|
|
|
@ -325,7 +325,7 @@ def display_for_field(value, field):
|
||||||
elif value is None:
|
elif value is None:
|
||||||
return EMPTY_CHANGELIST_VALUE
|
return EMPTY_CHANGELIST_VALUE
|
||||||
elif isinstance(field, models.DateTimeField):
|
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)):
|
elif isinstance(field, (models.DateField, models.TimeField)):
|
||||||
return formats.localize(value)
|
return formats.localize(value)
|
||||||
elif isinstance(field, models.DecimalField):
|
elif isinstance(field, models.DecimalField):
|
||||||
|
@ -345,7 +345,7 @@ def display_for_value(value, boolean=False):
|
||||||
elif value is None:
|
elif value is None:
|
||||||
return EMPTY_CHANGELIST_VALUE
|
return EMPTY_CHANGELIST_VALUE
|
||||||
elif isinstance(value, datetime.datetime):
|
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)):
|
elif isinstance(value, (datetime.date, datetime.time)):
|
||||||
return formats.localize(value)
|
return formats.localize(value)
|
||||||
elif isinstance(value, (decimal.Decimal, float, int, long)):
|
elif isinstance(value, (decimal.Decimal, float, int, long)):
|
||||||
|
|
|
@ -18,7 +18,7 @@ from django.utils.safestring import (SafeData, EscapeData, mark_safe,
|
||||||
from django.utils.formats import localize
|
from django.utils.formats import localize
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.module_loading import module_has_submodule
|
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
|
TOKEN_TEXT = 0
|
||||||
|
@ -592,7 +592,7 @@ class FilterExpression(object):
|
||||||
else:
|
else:
|
||||||
arg_vals.append(arg.resolve(context))
|
arg_vals.append(arg.resolve(context))
|
||||||
if getattr(func, 'expects_localtime', False):
|
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):
|
if getattr(func, 'needs_autoescape', False):
|
||||||
new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
|
new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
|
||||||
else:
|
else:
|
||||||
|
@ -853,7 +853,7 @@ def _render_value_in_context(value, context):
|
||||||
means escaping, if required, and conversion to a unicode object. If value
|
means escaping, if required, and conversion to a unicode object. If value
|
||||||
is a string, it is expected to have already been translated.
|
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 = localize(value, use_l10n=context.use_l10n)
|
||||||
value = force_unicode(value)
|
value = force_unicode(value)
|
||||||
if ((context.autoescape and not isinstance(value, SafeData)) or
|
if ((context.autoescape and not isinstance(value, SafeData)) or
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.utils.encoding import force_unicode
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import SafeData, EscapeData
|
from django.utils.safestring import SafeData, EscapeData
|
||||||
from django.utils.formats import localize
|
from django.utils.formats import localize
|
||||||
from django.utils.timezone import localtime
|
from django.utils.timezone import template_localtime
|
||||||
|
|
||||||
|
|
||||||
class DebugLexer(Lexer):
|
class DebugLexer(Lexer):
|
||||||
|
@ -82,7 +82,7 @@ class DebugVariableNode(VariableNode):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
output = self.filter_expression.resolve(context)
|
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 = localize(output, use_l10n=context.use_l10n)
|
||||||
output = force_unicode(output)
|
output = force_unicode(output)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
|
|
|
@ -72,11 +72,7 @@ def do_timezone(value, arg):
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
# Convert and prevent further conversion
|
result = timezone.localtime(value, tz)
|
||||||
result = value.astimezone(tz)
|
|
||||||
if hasattr(tz, 'normalize'):
|
|
||||||
# available for pytz time zones
|
|
||||||
result = tz.normalize(result)
|
|
||||||
|
|
||||||
# HACK: the convert_to_local_time flag will prevent
|
# HACK: the convert_to_local_time flag will prevent
|
||||||
# automatic conversion of the value to local time.
|
# automatic conversion of the value to local time.
|
||||||
|
|
|
@ -206,7 +206,7 @@ class override(object):
|
||||||
|
|
||||||
# Templates
|
# 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.
|
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.
|
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 (settings.USE_TZ if use_tz is None else use_tz)
|
||||||
and not is_naive(value)
|
and not is_naive(value)
|
||||||
and getattr(value, 'convert_to_local_time', True)):
|
and getattr(value, 'convert_to_local_time', True))
|
||||||
timezone = get_current_timezone()
|
return localtime(value) if should_convert else value
|
||||||
value = value.astimezone(timezone)
|
|
||||||
if hasattr(timezone, 'normalize'):
|
|
||||||
# available for pytz time zones
|
|
||||||
value = timezone.normalize(value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
# Utilities
|
# 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():
|
def now():
|
||||||
"""
|
"""
|
||||||
Returns an aware or naive datetime.datetime, depending on settings.USE_TZ.
|
Returns an aware or naive datetime.datetime, depending on settings.USE_TZ.
|
||||||
|
|
|
@ -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
|
``None``, the :ref:`current time zone <default-current-time-zone>` is unset
|
||||||
on entry with :func:`deactivate()` instead.
|
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()
|
.. function:: now()
|
||||||
|
|
||||||
Returns an aware or naive :class:`~datetime.datetime` that represents the
|
Returns an aware or naive :class:`~datetime.datetime` that represents the
|
||||||
|
|
|
@ -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
|
* The template engine now interprets ``True``, ``False`` and ``None`` as the
|
||||||
corresponding Python objects.
|
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
|
Backwards incompatible changes in 1.5
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,33 @@
|
||||||
import copy
|
import copy
|
||||||
|
import datetime
|
||||||
import pickle
|
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
|
from django.utils import unittest
|
||||||
|
|
||||||
|
|
||||||
class TimezoneTests(unittest.TestCase):
|
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):
|
def test_copy(self):
|
||||||
self.assertIsInstance(copy.copy(UTC()), UTC)
|
self.assertIsInstance(copy.copy(timezone.UTC()), timezone.UTC)
|
||||||
self.assertIsInstance(copy.copy(LocalTimezone()), LocalTimezone)
|
self.assertIsInstance(copy.copy(timezone.LocalTimezone()), timezone.LocalTimezone)
|
||||||
|
|
||||||
def test_deepcopy(self):
|
def test_deepcopy(self):
|
||||||
self.assertIsInstance(copy.deepcopy(UTC()), UTC)
|
self.assertIsInstance(copy.deepcopy(timezone.UTC()), timezone.UTC)
|
||||||
self.assertIsInstance(copy.deepcopy(LocalTimezone()), LocalTimezone)
|
self.assertIsInstance(copy.deepcopy(timezone.LocalTimezone()), timezone.LocalTimezone)
|
||||||
|
|
||||||
def test_pickling_unpickling(self):
|
def test_pickling_unpickling(self):
|
||||||
self.assertIsInstance(pickle.loads(pickle.dumps(UTC())), UTC)
|
self.assertIsInstance(pickle.loads(pickle.dumps(timezone.UTC())), timezone.UTC)
|
||||||
self.assertIsInstance(pickle.loads(pickle.dumps(LocalTimezone())), LocalTimezone)
|
self.assertIsInstance(pickle.loads(pickle.dumps(timezone.LocalTimezone())), timezone.LocalTimezone)
|
||||||
|
|
Loading…
Reference in New Issue