You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tz.py 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. from datetime import datetime, tzinfo
  2. import pytz
  3. from django.template import Library, Node, TemplateSyntaxError
  4. from django.utils import timezone
  5. register = Library()
  6. # HACK: datetime instances cannot be assigned new attributes. Define a subclass
  7. # in order to define new attributes in do_timezone().
  8. class datetimeobject(datetime):
  9. pass
  10. # Template filters
  11. @register.filter
  12. def localtime(value):
  13. """
  14. Convert a datetime to local time in the active time zone.
  15. This only makes sense within a {% localtime off %} block.
  16. """
  17. return do_timezone(value, timezone.get_current_timezone())
  18. @register.filter
  19. def utc(value):
  20. """
  21. Convert a datetime to UTC.
  22. """
  23. return do_timezone(value, timezone.utc)
  24. @register.filter('timezone')
  25. def do_timezone(value, arg):
  26. """
  27. Convert a datetime to local time in a given time zone.
  28. The argument must be an instance of a tzinfo subclass or a time zone name.
  29. Naive datetimes are assumed to be in local time in the default time zone.
  30. """
  31. if not isinstance(value, datetime):
  32. return ''
  33. # Obtain a timezone-aware datetime
  34. try:
  35. if timezone.is_naive(value):
  36. default_timezone = timezone.get_default_timezone()
  37. value = timezone.make_aware(value, default_timezone)
  38. # Filters must never raise exceptions, and pytz' exceptions inherit
  39. # Exception directly, not a specific subclass. So catch everything.
  40. except Exception:
  41. return ''
  42. # Obtain a tzinfo instance
  43. if isinstance(arg, tzinfo):
  44. tz = arg
  45. elif isinstance(arg, str):
  46. try:
  47. tz = pytz.timezone(arg)
  48. except pytz.UnknownTimeZoneError:
  49. return ''
  50. else:
  51. return ''
  52. result = timezone.localtime(value, tz)
  53. # HACK: the convert_to_local_time flag will prevent
  54. # automatic conversion of the value to local time.
  55. result = datetimeobject(result.year, result.month, result.day,
  56. result.hour, result.minute, result.second,
  57. result.microsecond, result.tzinfo)
  58. result.convert_to_local_time = False
  59. return result
  60. # Template tags
  61. class LocalTimeNode(Node):
  62. """
  63. Template node class used by ``localtime_tag``.
  64. """
  65. def __init__(self, nodelist, use_tz):
  66. self.nodelist = nodelist
  67. self.use_tz = use_tz
  68. def render(self, context):
  69. old_setting = context.use_tz
  70. context.use_tz = self.use_tz
  71. output = self.nodelist.render(context)
  72. context.use_tz = old_setting
  73. return output
  74. class TimezoneNode(Node):
  75. """
  76. Template node class used by ``timezone_tag``.
  77. """
  78. def __init__(self, nodelist, tz):
  79. self.nodelist = nodelist
  80. self.tz = tz
  81. def render(self, context):
  82. with timezone.override(self.tz.resolve(context)):
  83. output = self.nodelist.render(context)
  84. return output
  85. class GetCurrentTimezoneNode(Node):
  86. """
  87. Template node class used by ``get_current_timezone_tag``.
  88. """
  89. def __init__(self, variable):
  90. self.variable = variable
  91. def render(self, context):
  92. context[self.variable] = timezone.get_current_timezone_name()
  93. return ''
  94. @register.tag('localtime')
  95. def localtime_tag(parser, token):
  96. """
  97. Force or prevent conversion of datetime objects to local time,
  98. regardless of the value of ``settings.USE_TZ``.
  99. Sample usage::
  100. {% localtime off %}{{ value_in_utc }}{% endlocaltime %}
  101. """
  102. bits = token.split_contents()
  103. if len(bits) == 1:
  104. use_tz = True
  105. elif len(bits) > 2 or bits[1] not in ('on', 'off'):
  106. raise TemplateSyntaxError("%r argument should be 'on' or 'off'" %
  107. bits[0])
  108. else:
  109. use_tz = bits[1] == 'on'
  110. nodelist = parser.parse(('endlocaltime',))
  111. parser.delete_first_token()
  112. return LocalTimeNode(nodelist, use_tz)
  113. @register.tag('timezone')
  114. def timezone_tag(parser, token):
  115. """
  116. Enable a given time zone just for this block.
  117. The ``timezone`` argument must be an instance of a ``tzinfo`` subclass, a
  118. time zone name, or ``None``. If it is ``None``, the default time zone is
  119. used within the block.
  120. Sample usage::
  121. {% timezone "Europe/Paris" %}
  122. It is {{ now }} in Paris.
  123. {% endtimezone %}
  124. """
  125. bits = token.split_contents()
  126. if len(bits) != 2:
  127. raise TemplateSyntaxError("'%s' takes one argument (timezone)" %
  128. bits[0])
  129. tz = parser.compile_filter(bits[1])
  130. nodelist = parser.parse(('endtimezone',))
  131. parser.delete_first_token()
  132. return TimezoneNode(nodelist, tz)
  133. @register.tag("get_current_timezone")
  134. def get_current_timezone_tag(parser, token):
  135. """
  136. Store the name of the current time zone in the context.
  137. Usage::
  138. {% get_current_timezone as TIME_ZONE %}
  139. This will fetch the currently active time zone and put its name
  140. into the ``TIME_ZONE`` context variable.
  141. """
  142. # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
  143. args = token.contents.split()
  144. if len(args) != 3 or args[1] != 'as':
  145. raise TemplateSyntaxError("'get_current_timezone' requires "
  146. "'as variable' (got %r)" % args)
  147. return GetCurrentTimezoneNode(args[2])