Timezone aware field rendering

Alarm clock picture

Date handling in Python seems to be a very polarizing topic, you either love or hate it. With the addition of localization support for dates and their explicit support in Django a few versions ago the headaches seems to be more frequent. One part where date handling operates in a seemingly random manner is template date rendering. Sure we have the date template filter, but what about those cases where our project extracts the date from a model in a dynamic way? Or when our site support different locales for each user?

Instead of scratching your head trying to detect date values and creating custom date filters here is a quick "cheat sheet" of how timezone aware date types behave when rendered in a template (plus a bonus hack to insert custom template tags and filters at runtime).

#!/usr/bin/env python

from datetime import datetime

import os

import pytz

from django import setup
from django.conf import settings
from django.template import Context, Library, Template
from django.template.base import builtins
from django.utils import formats
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _

now = datetime.now(pytz.utc)       os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mayan.settings')
register = Library()

def test_filter(value):
    return now

def test_tag(context):
    context['test_tag_result'] = now

    return now



context = Context(
        'now': now, 'translated_raw': _('%s') % now,
        'translated_rendered': _('%s') % force_text(formats.localize(now, use_l10n=True))

print Template(
    '\n\nReturn value: {% test_tag %}\n'
    'Inserted context variable: {{ test_tag_result }}\n'
    'Filter result: {{ 1|test_filter }}\n'
    'Context variable: {{ now }}\n'
    'Translated raw: {{ translated_raw }}\n'
    'Translated rendered: {{ translated_rendered }}\n\n'

The expected output from this snippet is:

Return value: 2015-08-20 23:16:27.913086+00:00
Inserted context variable: Aug. 20, 2015, 11:16 p.m.
Filter result: Aug. 20, 2015, 11:16 p.m.
Context variable: Aug. 20, 2015, 11:16 p.m.
Translated raw: 2015-08-20 23:16:27.913086+00:00
Translated rendered: Aug. 20, 2015, 11:16 p.m.

As you can see passing the date value from the view will not adjust it to local time, neither will passing it a the result of a custom template tag. To ensure local time conversion is performed during template rendering, pass the date object as the result of a filter or inject it as a context variable. If you can't manipulate the template context or use a custom filter, convert the date to local time and renderer it in the view or model using:

from django.utils import formats
from django.utils.encoding import force_text

force_text(formats.localize(<date object>, use_l10n=True))

Hope this helps keeping you from pulling too many hairs the next time your timezone aware datetime fields aren't rendering the way you expect them to.

Alarm clock picture